2

I want to know exactly why this is not working:

[HttpPost]
public IHttpActionResult Post(Slack_Webhook json)
{
    return Ok(json.challenge);
}

public class Slack_Webhook
{
    public string type { get; set; }
    public string token { get; set; }
    public string challenge { get; set; }
}

The Official Documentation says:

We’ll send HTTP POST requests to this URL when events occur. As soon as you enter a URL, we’ll send a request with a challenge parameter, and your endpoint must respond with the challenge value.

This is an example object (JSON) sent by Slack:

{
    "token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
    "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
    "type": "url_verification"
}

EDIT: I could write a book on code that does not work in this issue... here's another example that did not work - still no idea what is wrong:

[HttpPost]
public IHttpActionResult Post()
{
    var pairs = Request.GetQueryNameValuePairs();
    bool isValidToken = false;
    string c = "This does not work.";
    foreach(var pair in pairs)
    {
        if (pair.Key == "token")
        {
            if (pair.Value == "<UNIQUETOKEN>")
            {
                isValidToken = true;
            }
        }
        if (pair.Key == "challenge")
        {
            c = pair.Value;
        }
    }
    if (isValidToken == true)
    {
        return Json(new {challenge = c });
    }
    else
    {
        return BadRequest();
    }
}

EDIT2: Very interesting that I get NULL as a response from below code - that means the body of the received POST is empty.. Could anyone with a working Slack-Integration try that out? So their site is wrong, stating the challenge is sent in the body - where else could it be?

// POST: api/Slack
[HttpPost]
public IHttpActionResult Post([FromBody]string json)
{
    return Json(json);
}

EDIT3: This function is used to get the raw request, but there is nothing inside the body - I am out of solutions.. the support of Slack said, they have no idea about ASP.NET and I should ask here on SO for a solution. Here we are again! ;-)

[HttpPost]
public async Task<IHttpActionResult> ReceivePostAsync()
{
    string rawpostdata = await RawContentReader.Read(this.Request);
    return Json(new StringContent( rawpostdata));
}
public class RawContentReader
{
    public static async Task<string> Read(HttpRequestMessage req)
    {
        using (var contentStream = await req.Content.ReadAsStreamAsync())
        {
            contentStream.Seek(0, SeekOrigin.Begin);
            using (var sr = new StreamReader(contentStream))
            {
                return sr.ReadToEnd();
            }
        }
    }
}

The result ( as expected ) looks like this:

Our Request:
POST
"body": { 
     "type": "url_verification",
     "token": "<token>",
     "challenge": "<challenge>"
}
Your Response:
"code": 200
"error": "challenge_failed"
"body": {
 {"Headers":[{"Key":"Content-Type","Value":["text/plain; charset=utf-8"]}]} 
}

I think I'm missing something - is there another way to get the body of the POST-Request? I mean, I can get everything else - except the body ( or it says it is empty).

EDIT4: I tried to read the body with another function I found - without success, returns empty string - but to let you know what I already tried, here it is:

[HttpPost]
public IHttpActionResult ReceivePost()
{
    var bodyStream = new 
    StreamReader(HttpContext.Current.Request.InputStream);
    bodyStream.BaseStream.Seek(0, SeekOrigin.Begin);
    var bodyText = bodyStream.ReadToEnd();
    return Json(bodyText);
}

While trying to solve this I learnt a lot - but this one seems to be so impossible, that I think I will never solve it alone. Thousands of tries with thousands of different functions - I have tried hundreds of parameters and functions in all of WebApi / ASP.NET / MVC / whatever - why is there no BODY? Does it exist? What's his/her name? Where does it live? I really wanna hang out with that parameter if I ever find it, must be hidden at the end of the rainbow under a pot of gold.

ekad
  • 14,436
  • 26
  • 44
  • 46
S Belz
  • 83
  • 10
  • Well according to the documentation it is possible you are not returning the value (challenge) in one of the expected formats. Also You have not stated what is not working. – Nkosi Sep 09 '17 at 14:11
  • Are you able to inspect the raw request? – Nkosi Sep 09 '17 at 16:58
  • Not as a whole - I checked the headers, content-type is json, everything else seems ok. How do I inspect the raw request? I don't know how I would do this. – S Belz Sep 09 '17 at 17:54

3 Answers3

1

If you can use ASP.NET Core 2, this will do the trick:

public async Task<ActionResult> HandleEvent([FromBody] dynamic data)
    => new ContentResult {Content = data.challenge};
Leon V
  • 541
  • 5
  • 12
0

According to the official documentation linked to in the OP you have to format your response depending on the content type you return.

It is possible you are not returning the value (challenge) in one of the expected formats.

Once you receive the event, respond in plaintext with the challenge attribute value. In this example, that might be:

HTTP 200 OK
Content-type: text/plain
3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P

To do the above you would have needed to return your request differently

[HttpPost]
public IHttpActionResult Post([FromBody]Slack_Webhook json) {
    //Please verify that the token value found in the payload 
    //matches your application's configured Slack token.
    if (ModelState.IsValid && json != null && ValidToken(json.token)) {
        var response = Request.CreateResponse(HttpStatusCode.OK, json.challenge, "text/plain");
        return ResponseMessage(response);
    }
    return BadRequest();
}

Documentation also shows

Or even JSON:

HTTP 200 OK
Content-type: application/json
{"challenge":"3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P"}

Which again would have to be formatted a little differently

[HttpPost]
public IHttpActionResult Post([FromBody]Slack_Webhook json) {
    //Please verify that the token value found in the payload 
    //matches your application's configured Slack token.
    if (ModelState.IsValid && json != null && ValidToken(json.token)) {
        var model = new { challenge = json.challenge };
        return Ok(model);
    }
    return BadRequest();
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Thank you for your answer! Unfortunately, the problem seems to be different. The proposed solutions do not work ( returns Error 500). If I use Post() without parameter, it returns Ok, but how do I access the body of the request with the challenge inside? It has to be something with the Json-Object received that does not fit Slack_Webhook ( which seems to be very weird, as it should be automatically serialized to fit the object).. – S Belz Sep 09 '17 at 15:47
  • @SBelz Check update. You have to tell the framework to look in the body of the request. – Nkosi Sep 09 '17 at 15:51
  • Mhh. I get Code 500 with this one, too. Postman gets 400, with an empty body. – S Belz Sep 09 '17 at 16:05
0

Here's how you can access the data:

[HttpPost]  
[Route("something")]
public JsonResult DoSomething()
{
    var token = HttpContext.Request.Form["token"];

    // Is the same as:
    // var token = Request.Form["token"];

    return new JsonResult(token);
}

I suggest using a Request Bin for further debugging.

not-matthias
  • 3
  • 1
  • 2
  • 6