asp.net-web-apihttp-postasp.net-web-api-routinghttp-post-varsfrombodyattribute

Should I use "[FromBody]" values or custom params when responding to an HTTP Post call in a Web API app?


Is POST the right HTTP method/verb to use for telling the server which criteria to use to retrieve data and then save it locally?

I want to send an URL from a client (Windows Forms) app/util to my Web API app to tell it to retrieve data via a Stored Proc, then store the results in a local table. No data is returned to the caller, it's just a notification to do some work.

To prepare for this, I added a new route to WebApiConfig:

// Some reports (monthly) only need a begindate, such as "201509"; others need a range 
//  of 2..13 months, such as "2001502" and "201602")
config.Routes.MapHttpRoute(
    name: "ReportsApi",
    routeTemplate: "api/{controller}/{unit}/{begindate}/{enddate}",
    defaults: new { enddate = RouteParameter.Optional }
);

In the Controller, this method already existed (was automatically added):

// POST: api/PriceCompliance
public void Post([FromBody]string value)
{
}

...but I don't know if I want the "[FromBody]" jazz, so I added this:

public void Post(String unit, String beginDate, String endDate)
{
    // TODO: Call the corresponding SP (via a Model or directly here?) and store the results in a table.
}

Is this the right/better way to do it, or is it preferable to pull the URL args out of "[FromBody]"? In fact, is POST even the right HTTP verb to use for this sort of thing?


Solution

  • The matter of choosing the right verb for your action is always something debatable. If you look at the item 4.3.3 of RFC 7231:

    The POST method requests that the target resource process the
    representation enclosed in the request according to the resource's
    own specific semantics. For example, POST is used for the following
    functions (among others):

    • Providing a block of data, such as the fields entered into an HTML form, to a data-handling process;

    The keywords in that are highlight in bold. As you can see it fits perfectly your case, where you send a POST request with some block of data and some process is made by your API.

    As matter of how to handle the parameters in your POST. You could create a DTO to map your fields: Unit, BeginDate, EndDate. Since you are sending your parameters from body, WebAPI will bind the parameters using a Formatter. The WebAPI will choose the better formatter to use according to the content-type header. In the example I provided, the formatter used will be the JSON.Net (which is the default for JSON)

    PriceComplianceDTO:

    public class PriceComplianceDTO
    {
        public string Unit { get; set; }
    
        public DateTime BeginDate { get; set; }
    
        public DateTime EndDate { get; set; }
    }
    

    Controller:

    [RoutePrefix("api/v1")]
    public class PriceComplianceController : ApiController
    {
        [HttpPost]
        [Route("price")]
        public void Post(PriceComplianceDTO data)
        {
            //Call procedure and process data
        }
    }
    

    You can also remove the custom route you posted from your WebApiConfig if you decide using the approach above. I just created a new Web API and this is my default route, which is working with the POST:

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );