Model binding JSON POSTs in ASP.NET Core

[Author]: https://andrewlock.net/model-binding-json-posts-in-asp-net-core/

I was catching up on the latest ASP.NET Community Standup the other day when a question popped up about Model Binding that I hadn’t previously picked up on (you can see the question around 46:30). It pointed out that in ASP.NET Core (the new name for ASP.NET 5), you can no longer simply post JSON data to an MVC controller and have it bound automatically, which you could previously do in ASP.NET 4/MVC 5.

In this post, I am going to show what to do if you are converting a project to ASP.NET Core and you discover your JSON POSTs aren’t working. I’ll demonstrate the differences between MVC 5 model binding and MVC Core model binding, highlighting the differences between the two, and how to setup your controllers for your project, depending on the data you expect.

TL;DR: Add the [FromBody] attribute to the parameter in your ASP.NET Core controller action

Where did my data go?

Imagine you have created a shiny new ASP.NET core project which you are using to rewrite an existing ASP.NET 4 app (only for sensible reasons of course!) You copy and paste your old WebApi controller in to your .NET Core Controller, clean up the namespaces, test out the GET action and all seems to be working well.

Note: In ASP.NET 4, although the MVC and WebApi pipelines behave very similarly, they are completely separate. Therefore you have separate ApiController and Controller classes for WebApi and Mvc respectively (and all the associated namespace confusion). In ASP.NET Core, the pipelines have all been merged and there is only the single Controller class.

As your GET request is working, you know the majority of your pipeline, for example routing, is probably configured correctly. You even submit a test form, which sends a POST to the controller and receives the JSON values it sent back. All looking good.

Posting x-www-url-formencoded content to ASP.NET Core controller

As the final piece of the puzzle, you test sending an AJAX POST with the data as JSON, and it all falls apart – you receive a 200 OK, but all the properties on your object are empty. But why?

Posting JSON content to ASP.NET Core controller

What is Model Binding?

Before we can go into details of what is happening here, we need to have a basic understanding of model binding. Model binding is the process whereby the MVC or WebApi pipeline takes the raw HTTP request and converts that into the arguments for an action method invocation on a controller.

So for example, consider the following WebApi controller and Person class:

public class PersonController : ApiController  
{
    [HttpPost]
    public Person Index(Person person)
    {
        return person;
    }
}

public class Person  
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

We can see that there is a single action method on the controller, a POST action, which takes a single parameter – an instance of the Person class. The controller then just echoes that object out, back to the response.

So where does the Person parameter come from? Model binding to the rescue! There are a number of different places the model binders can look for data in order to hydrate the person object. The model binders are highly extensible, and allow custom implementations, but common bindings include:

  • Route values – navigating to a route such as {controller}/{action}/{id} will allow binding to an idparameter
  • Querystrings – If you have passed variables as querystring parameters such as ?FirstName=Andrew, then the FirstName parameter can be bound.
  • Body – If you send data in the body of the post, this can be bound to the Person object
  • Header – You can also bind to HTTP header values, though this is less common.

So you can see there are a number of ways to send data to the server and have the model binder automatically create the correct method parameter for you. Some require explcit configuration, while others you get for free. For example Route values and querystring parameters are always bound, and for complex types (i.e. not primitives like string or int) the body is also bound.

It is important to note that if the model binders fail to bind the parameters for some reason, they will not throw an error, instead you will receive a default object, with none of the properties set, which is the behaviour we showed earlier.

How it works in ASP.NET 4

To play with what’s going on here I created two projects, one using ASP.NET 4 and the other using the latest ASP.NET Core (so very nearly RC2). You can find them on github here and here.

In the ASP.NET WebApi project, there is a simple controller which takes a Person object and simply returns the object back as I showed in the previous section.

On a simple web page, we then make POSTs (using jQuery for convenience), sending requests either x-www-form-urlencoded (as you would get from a normal form POST) or as JSON.

 //form encoded data
 var dataType = 'application/x-www-form-urlencoded; charset=utf-8';
 var data = $('form').serialize();

 //JSON data
 var dataType = 'application/json; charset=utf-8';
 var data = {
    FirstName: 'Andrew',
    LastName: 'Lock',
    Age: 31
 }

 console.log('Submitting form...');
 $.ajax({
    type: 'POST',
    url: '/Person/Index',
    dataType: 'json',
    contentType: dataType,
    data: data,
    success: function(result) {
        console.log('Data received: ');
        console.log(result);
    }
});

This will create an HTTP request for the form encoded POST similar to (elided for brevity):

POST /api/Person/UnProtected HTTP/1.1  
Host: localhost:5000  
Accept: application/json, text/javascript, */*; q=0.01  
Content-Type: application/x-www-form-urlencoded; charset=UTF-8

FirstName=Andrew&LastName=Lock&Age=31  

and for the JSON post:

POST /api/Person/UnProtected HTTP/1.1  
Host: localhost:5000  
Accept: application/json, text/javascript, */*; q=0.01  
Content-Type: application/json; charset=UTF-8

{"FirstName":"Andrew","LastName":"Lock","Age":"31"}

Sending these two POSTs elicits the following console response:

Image of successful posts to ASP.NET 4 controller

In both cases the controller has bound to the body of the HTTP request, and the parameters we sent were returned back to us, without us having to do anything declarative. The model binders do all the magic for us. Note that although I’ve been working with a WebApi controller, the MVC controller model binders behave the same in this example, and would bind both POSTs.

The new way in ASP.NET Core

So, moving on to ASP.NET Core, we create a similar controller, using the same Person class as a parameter as before:

public class PersonController : Controller  
{
    [HttpPost]
    public IActionResult Index(Person person){
        return Json(person);   
    } 
}

Using the same HTTP requests as previously, we see the following console output, where the x-www-url-formencoded POST is bound correctly, but the JSON POST is not.

x-www-url-formencoded post is bound correctly but JSON post is not

In order to bind the JSON correctly in ASP.NET Core, you must modify your action to include the attribute [FromBody] on the parameter. This tells the framework to use the content-type header of the request to decide which of the configured IInputFormatters to use for model binding.

By default, when you call AddMvc() in Startup.cs, a JSON formatterJsonInputFormatter, is automatically configured, but you can add additional formatters if you need to, for example to bind XML to an object.

With that in mind, our new controller looks as follows:

public class PersonController : Controller  
{
    [HttpPost]
    public IActionResult Index([FromBody] Person person){
        return Json(person);   
    } 
}

And our JSON POST now works like magic again!

JSON binding working correctly with FromBody attribute

So just always include [FromBody]?

So if you were thinking you can just always use [FromBody] in your methods, hold your horses. Lets see what happens when you hit your new endpoint with a x-www-url-formencoded request:

Unsupported Media type using x-www-url-formencoded with FromBody attribute

Oh dear. In this case, we have specifically told the ModelBinder to bind the
body of the post, which is FirstName=Andrew&LastName=Lock&Age=31, using an IInputFormatter. Unfortunately, the JSON formatter is the only formatter we have and that doesn’t match our content type, so we get a 415 error response.

In order to specifically bind to the form parameters we can either remove the FromBody attribute or add the alternative FromForm attribute, both of which will allow our form data to be bound but again will prevent the JSON binding correctly.

But what if I need to bind both data types?

In some cases you may need to be able to bind both types of data to an action. In that case, you’re a little bit stuck, as it won’t be possible to have the same end point receive two different sets of data.

Instead you will need to create two different action methods which can specifically bind the data you need to send, and then delegate the processing call to a common method:

public class PersonController : Controller  
{
    //This action at /Person/Index can bind form data 
    [HttpPost]
    public IActionResult Index(Person person){
        return DoSomething(person);   
    } 

    //This action at /Person/IndexFromBody can bind JSON 
    [HttpPost]
    public IActionResult IndexFromBody([FromBody] Person person){
        return DoSomething(person);   
    } 

    private IActionResult DoSomething(Person person){
        // do something with the person here
        // ...

        return Json(person);
    }
}

You may find it inconvenient to have to use two different routes for essentially the same action. Unfortunately, routes are obviously mapped to actions before model binding has occurred, so the model binder cannot be used as a discriminator. If you try to map the two above actions to the same route you will get an error saying Request matched multiple actions resulting in ambiguity. It may be possible to create a custom route to call the appropriate action based on header values, but in all likelihood that will just be more effort than it’s worth!

Why the change?

So why has this all changed? Wasn’t it simpler and easier the old way? Well, maybe, though there are a number of gotchas to watch out for, particularly when POSTing primitive types.

The main reason, according to Damian Edwards at the community standup, is for security reasons, in particular cross-site request forgery (CSRF) prevention. I will do a later post on anti-CSRF in ASP.NET Core, but in essence, when model binding can occur from multiple different sources, as it did in ASP.NET 4, the resulting stack is not secure by default. I confess I haven’t got my head around exactly why that is yet or how it could be exploited, but I presume it is related to identifying your anti-CSRF FormToken when you are getting your data from multiple sources.

Summary

In short, if your model binding isn’t working properly, make sure it’s trying to bind from the right part of your request and you have registered the appropriate formatters. If it’s JSON binding you’re doing, adding [FromBody] to your parameters should do the trick!

References

Advertisements

Web API 2 GET by query parameter

[Origin]: https://stackoverflow.com/questions/31759979/web-api-2-get-by-query-parameter

You always have to register a route in WebApi for your controller actions, this can be done with attribute routing or with conventions based routing.

Parameters passed in the query string for GET request don’t really have to be specified explicitly in either of the routing configuration methods.

The parameters you specify on your controller action get mapped to parameters sent in the query string of a GET request.

If you are using the default WebApi conventions based setup where the routes are configured something like this:

var config = new HttpConfiguration();
// some other config setup for web api
...
...
// route config
config.Routes.MapHttpRoute(
    name: "API Default",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

Then a controller like this will work for you:

public class UsersController : ApiController {
   // this maps to a get requests to:
   // domain/api/users
   // and domain/api/users?id=someid
   // and domain/api/users?mail=somemail
   // and domain/api/users?pw=somepw
   // and domain/api/users?mail=somemail&pw=somepw
   // and domain/api/users with any query string really
   [HttpGet]
   public IHttpActionResult Get(string mail, string pw) {
      // should probably check mail and pw for empty strings and nulls
      var users = SomeStaticExampleService.FindByMailAndPw(mail, pw);
      return this.Json(users);
   }
}

Alternatively you can use attribute routing and then call your controllers and action methods whatever you want. Configure your routes like this:

var config = new HttpConfiguration();
// some other config setup for web api
...
...
// route config
config.MapHttpAttributeRoutes();

Then you can create a controller like this:

public class FooController : ApiController {
   // this maps to a get requests to:
   // domain/users
   // and domain/users?id=someid
   // and domain/users?mail=somemail
   // and domain/users?pw=somepw
   // and domain/users with any query string really
   [HttpGet]
   [Route("users")]
   public IHttpActionResult Bar(string mail, string pw) {
      // should probably check mail and pw for empty strings and nulls
      var users = SomeStaticExampleService.FindByMailAndPw(mail, pw);
      return this.Json(users);
   }
}

Bear in mind though that with Attribute Routing you have to be careful not to create clashing routes otherwise WebApi won’t know which controller and action to route a request to when a route is mapped to multiple action methods.

I’ve used this.Json in these examples to return a http response with json content to match your wcf ResponseFormat = WebMessageFormat.Json. But you can of course just return a CLR type:

   [HttpGet]
   [Route("users")]
   public IEnumerable<MyUser> Bar(string mail, string pw) {
      // should probably check mail and pw for empty strings and nulls
      var users = SomeStaticExampleService.FindByMailAndPw(mail, pw);
      return users;
   }

and let WebApi’s content negotiation handle the response message content type.

shareedit

Error posting JSON to Web API 2 : The request entity’s media type ‘text/plain’ is not supported for this resource

[Origin]: https://stackoverflow.com/questions/25935650/error-posting-json-to-web-api-2-the-request-entitys-media-type-text-plain-i

You were sending the content-type of application/json in the body, rather than as a header. So your POST request was defaulting to text/plain. The RestClient extension has a separate place to enter the header.

If you ever have a question about what’s being sent over the wire, check the Network tab in your browser’s developer tools, or use a tool such as Fiddler to see the network traffic.

Inside POSTMAN, you only need to change the setting to application/json. Refer image below.

application/json

shareedit

“The breakpoint will not currently be hit. The source code is different from the original version.” What does this mean?

[Origin]: https://stackoverflow.com/questions/2468852/the-breakpoint-will-not-currently-be-hit-the-source-code-is-different-from-the

When debugging in Visual Studio, sometimes I add a breakpoint but it’s hollow and VS says “The breakpoint will not currently be hit. The source code is different from the original version.” Obviously this prevents me from being able to debug.

What on earth does the message mean? What original version? If I’ve just opened up the solution and not made any changes whatsoever to the code, how can there be an ‘original version’?

shareedit

As it says, the “source code is different from the original version”.

Right click on the project folder inside the solution explorer and choose to Clean. Build a new version of the project and the breakpoint will work again!

shareedit

Select Debug in Solution Configurations, instead of Release

screenshot of menu

shareedit

If you have more than one projects in your solution, then make sure that the correct project is set as the StartUp Project. To set a particular project as the Startup Project of your solution, Right-click the project, choose Set As StartUp Project.

After I set my StartUp Project correctly, desired break-point was reached by the thread.

shareedit

How to Reset Identity Column Values in Sql Server

[Origin]: http://www.c-sharpcorner.com/blogs/how-to-reset-identity-column-values-in-sql-server1

Step 1: Create table.

CREATE TABLE dbo.Emp
(
  ID INT IDENTITY(1,1),
  Name VARCHAR(10)
)

Step 2: Insert some sample data.

INSERT INTO dbo.Emp(name)
VALUES ('Rakesh')
INSERT INTO dbo.Emp(Name)
VALUES ('Rakesh Kalluri')

build status
When we run above query the second insert statement will failed because of varchar(10) length.

Step 3: Check the identity column value.

DBCC CHECKIDENT (<span class="string">'Emp'</span>)

error
Even second insert was failed but the identity value is increased .if we insert the another record the identity value is 3.

INSERT INTO dbo.Emp(Name)
VALUES ('Kalluri')

SELECT * FROM Emp

id name

Step 4: Reset the identity column value.

DELETE FROM EMP WHERE ID=3

DBCC CHECKIDENT ('Emp', RESEED, 1)

INSERT INTO dbo.Emp(Name)
VALUES ('Kalluri')

SELECT * FROM Emp

id name table

difference between varchar(500) vs varchar(max) in sql server

[Origin]: https://stackoverflow.com/questions/3682821/difference-between-varchar500-vs-varcharmax-in-sql-server

In SQL Server 2000 and SQL Server 7, a row cannot exceed 8000 bytes in size. This means that a VARBINARY column can only store 8000 bytes (assuming it is the only column in a table), a VARCHAR column can store up to 8000 characters and an NVARCHAR column can store up to 4000 characters (2 bytes per unicode character). This limitation stems from the 8 KB internal page size SQL Server uses to save data to disk.

To store more data in a single column, you needed to use the TEXT, NTEXT, or IMAGE data types (BLOBs) which are stored in a collection of 8 KB data pages that are separate from the data pages that store the other data in the same table. These data pages are arranged in a B-tree structure. BLOBs are hard to work with and manipulate. They cannot be used as variables in a procedure or a function and they cannot be used inside string functions such as REPLACE, CHARINDEX or SUBSTRING. In most cases, you have to use READTEXT, WRITETEXT, and UPDATETEXT commands to manipulate BLOBs.

To solve this problem, Microsoft introduced the VARCHAR(MAX), NVARCHAR(MAX), and VARBINARY(MAX) data types in SQL Server 2005. These data types can hold the same amount of data BLOBs can hold (2 GB) and they are stored in the same type of data pages used for other data types. When data in a MAX data type exceeds 8 KB, an over-flow page is used. SQL Server 2005 automatically assigns an over-flow indicator to the page and knows how to manipulate data rows the same way it manipulates other data types. You can declare variables of MAX data types inside a stored procedure or function and even pass them as variables. You can also use them inside string functions.

Microsoft recommend using MAX data types instead of BLOBs in SQL Server 2005. In fact, BLOBs are being deprecated in future releases of SQL Server.

Credit: http://www.teratrax.com/articles/varchar_max.html


In SQL Server 2005 and SQL Server 2008, The maximum storage size for VARCHAR(MAX) is 2^31-1 bytes (2,147,483,647 bytes or 2GB – 1 bytes). The storage size is the actual length of data entered + 2 bytes. The data entered can be 0 characters in length. Since each character in a VARCHAR data type uses one byte, the maximum length for a VARCHAR(MAX) data type is 2,147,483,645.

Full Interesting read for you: http://www.sql-server-helper.com/faq/sql-server-2005-varchar-max-p01.aspx

Reference: http://msdn.microsoft.com/en-us/library/ms143432.aspx

shareedit

Why use angular’s $log instead of console.log?

[Origin]: https://stackoverflow.com/questions/24185847/why-use-angulars-log-instead-of-console-log

I understand it is a best practice in angular to use $log instead of console.log. However, I can’t find good documentation explaining the reasons. Why should a developer use $log?

$log first checks if the browser supports console.log (IE 8, for example, doesn’t). This prevents errors being displayed on IE 8. Note: this doesn’t mean it will log anything on IE 8, it simply means it won’t throw the error.

Next to that, it also allows you to decorate and mock $log for extending and testing purposes, if you are so inclined. You could for example decorate it to log to an array for IE 8 support.

A bonus feature: if you pass it a JavaScript Error instance, it will attempt to format it nicely. This can be found out by reading the source code.

EDIT: “It is not that IE 8 doesn’t support console.log. It just doesn’t create the console object until the dev tools are opened.” See comments below for more details.