How to make a controller accept post data from the body as well as URL in MVC 6 ASP.NET 5

4k Views Asked by At

Currently I am trying to write a controller in MVC 6 that is capable of accepting data from a post request. The issue is that depending on the client (which is not always a web browser), the data can come in as either key value pairs in the request URL, or as JSON in the request body.

Currently this method works for accepting the data from the URL:

[HttpPost]
public async Task<CaptureResponse> CaptureData(CaptureInfo capture) {
    ...
}

After a lot of trial and error (and stack overflow answers), I figured out that the [FromBody] attribute tells the model binder to look in the request body, which is required now because MVC 6 combines WebApi and standard MVC together. The following code parses data from JSON in the form body:

[HttpPost]
public async Task<CaptureResponse> CaptureData([FromBody] CaptureInfo capture) {
   ...
}

For simplicity, I would like to combine the two together somehow, so the model binder gives me the data in the same parameter variable. So far, the only way I can get the data into the same Action is to specify two parameters, one for the URL and one for the body, and do some null checking on each like so:

[HttpPost]
public async Task<CaptureResponse> CaptureData(CaptureInfo capture, [FromBody] CaptureInfo bodyCapture) {
    if (bodyCapture != null) {
        if (bodyCapture.RequiredProperty1!= null
        && bodyCapture.RequiredProperty2!= null) {
            capture = bodyCapture;
        }
    }
    ...
}

I have tried specifying multiple properties before the input attribute like this:

[HttpPost]
public async Task<CaptureResponse> CaptureData(CaptureInfo [FromQuery][FromRoute][FromForm][FromBody] capture) {
    ...
}

But it does not work. Any ideas if something like this is possible?

2

There are 2 best solutions below

1
On

You need use Request.Form

like:

   string username = HttpContext.Current.Request.Form.GetValues("key1")[0].ToString();
    string password = HttpContext.Current.Request.Form.GetValues("key2")[0].ToString();
    string something = HttpContext.Current.Request.Form.GetValues("key3")[0].ToString();
0
On

As far as I know, it is just not possible. Of course you can try using workarounds, basically doing all request parsing yourself. It doesn't sound good, does it?

If you really want the things your way, I believe the best approach is to have two distinct endpoints in the controller and the private method for actual processing. Or, perhaps, even extract that method into an additional abstraction layer with BlaBlaService (CaptureService in your case, probably) class(es) responsible for all the dirty work. Sometimes it makes sense to separate your controllers layer from business logic - for example, for testing purposes.

PS: Your idea is quite similar to what was the approach in good old .NET & PHP times, and believe me, this particular idea is not the one that made those times good. You know, the MVC is much about the REST-like approach, so each endpoint of your controller is supposed to be dedicated to its own single function and obey to a single and uniform "intuitive" protocol.

The "intuitive" way to submit data to POST request for developers acquainted with REST is through the request body. I suggest you to consider going with this approach as the only one.