Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Supporting GET *and* POST in WebApi

Let's have a test model.

public class TestRequestModel
{
    public string Text { get; set; }
    public int Number { get; set; }
}

I would like this service to be able to accept the following requests:

  • GET /test?Number=1234&Text=MyText
  • POST /test with header: Content-Type: application/x-www-form-urlencoded and body: Number=1234&Text=MyText
  • POST /test with header: Content-Type: application/json and body: {"Text":"Provided!","Number":9876}

The routes are configured the following way:

_config.Routes.MapHttpRoute(
   "DefaultPost", "/{controller}/{action}",
   new { action = "Post" }, 
   new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) });

_config.Routes.MapHttpRoute(
   "The rest", "/{controller}/{action}",
   defaults: new { action = "Get" });

My controller looks like this:

public class TestController : ApiController
{
    [HttpGet]
    public TestResponseModel Get([FromUri] TestRequestModel model)
    {
       return Do(model);
    }

    [HttpPost]
    public TestResponseModel Post([FromBody] TestRequestModel model)
    {
       return Do(model);
    }
    (...)

This seems like an acceptable amount of boiler plate code, but I still would like to avoid it if possible.

Having the extra route is not ideal too. I have a fear of MVC/WebAPi routes and I believe they are evil.

Is there a way to avoid having two methods and/or the DefaultPost route?

like image 934
tymtam Avatar asked Aug 05 '13 04:08

tymtam


1 Answers

What you are asking for is not typical with ASP.NET Web API. In ASP.NET MVC, it is common to have the same action method handling the initial GET and the subsequent post back (POST). ASP.NET Web API is intended for building HTTP services and GET is used for retrieving a resource without changing anything in the system, while POST is for creating a new resource, as pointed by Matthew.

Anyway, it is not impossible to have one action method in Web API to accomplish this. But you want the same action method to not only handle GET and POST and also do the model binding and formatter binding. Model binding (similar to MVC) binds request URI, query string, etc to parameters while formatter binding (unique to web API) binds the body content to parameter. By default, simple types are bound from URI, query string and complex types from body. So, if you have an action method with parameters of string text, int number, TestRequestModel model, you can have web API bind from URI or body and in this case, you will need to check what is not empty and use that. But, such a solution will look more like a hack, unfortunately. Or if you want the same complex type to be populated from both URI/query string and body, you will need to write your own parameter binder that checks for request parts and populate the parameter accordingly.

Also, you don't need to have two route mappings. The default one like this will do.

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);
like image 146
Badri Avatar answered Sep 22 '22 09:09

Badri