Igor Konopko
Igor Konopko

Reputation: 764

Asp Web Api async action - 404 error

I've got some api controller with this action:

public class ProxyController : ApiController {
    public async Task<HttpResponseMessage> PostActionAsync(string confirmKey)
    {
         return await Task<HttpResponseMessage>.Factory.StartNew( () =>
               {
                  var result = GetSomeResult(confirmKey);
                  return Request.CreateResponse(HttpStatusCode.Created, result);
               });
    }
}

And here is my api routing confuguration:

routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });

When I try to make any Post/Get Requests to this action, it's returns '404' error. How can I fix it? All other not-async actions in this controller works fine.

UPD. JS query:

$.ajax({
        url: Url + '/api/Proxy/PostActionAsync',
        type: 'POST',
        data: { confirmKey: that.confirmKey },                  
        dataType: 'json',                   
        xhrFields: {  withCredentials: true  },
        success: function (data) {
            ............
        },
        error: function (jqXHR, textStatus, errorThrown) {
             ............
        }                        
});

UPD. Resolved by adding [FromBody] to my parameters in action method just like in J. Steen's answer, now it's look's like

public class ProxyController : ApiController {
       public async Task<HttpResponseMessage> PostActionAsync([FromBody]string confirmKey)
        {
            var someModel = new SomeResultModel(User.Identity.Name);
            await Task.Factory.StartNew(() => someModel.PrepareModel(confirmKey));

            return Request.CreateResponse(HttpStatusCode.OK, someModel);
        }
    }

And it works!

Upvotes: 2

Views: 4281

Answers (2)

J. Steen
J. Steen

Reputation: 15578

The routing configuration for Web API works a little differently than MVC.

Try

routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional });

Note the missing {action} as that is resolved by Web API at call-time, automagically, depending on the HTTP verb you use for your request.

Consider this article on Web API routing which lists (as an example):

HTTP Method  URI Path            Action           Parameter
GET          api/products        GetAllProducts   (none)
GET          api/products/4      GetProductById   4
DELETE       api/products/4      DeleteProduct    4

In your case, the async version of an action is also resolved automatically.

POST         api/products        PostActionAsync  (Post data)

Since we now know the controller name, requests would be like:

GET api/proxy
GET api/proxy/4
POST api/proxy (with post data)

Edit:

After additional research (brief, I admit) I have found the issue.

You need to add [FromBody] infront of your in-parameter.

public async Task<HttpResponseMessage> PostActionAsync([FromBody] string confirmKey)

This, combined with sending just the value (no wrapping json) works wonders. Set content type to "application/x-www-form-urlencoded" instead of json, and send your parameter as "=" + that.confirmKey.

Alternative:

If you don't want to fiddle around with content-types and prefixing =-signs, just send it as part of the querystring. Forget the [FromBody] and the rest. Call

/api/Proxy/PostActionAsync?confirmKey=' + that.confirmKey

Additional, exhaustive information in this blog.

Upvotes: 3

Vladimir
Vladimir

Reputation: 7475

Is that change possible?

public async Task<HttpResponseMessage> PostActionAsync()
{
    var result = await GetSomeResult();
    return Request.CreateResponse(HttpStatusCode.Created, result);
}

Upvotes: 1

Related Questions