Keywords: ASP.NET Core | HTTP 415 | FromForm | FromBody | Model Binding
Abstract: This article provides an in-depth analysis of HTTP 415 errors in ASP.NET Core form POST requests, focusing on the differences between [FromBody] and [FromForm] attributes. Through detailed code examples and request header analysis, it explains the root cause of media type mismatches and offers best practices for migrating from traditional ASP.NET MVC to ASP.NET Core. The article also discusses implementing custom model binders to support multiple content types, providing comprehensive solutions for developers.
Problem Background and Error Phenomenon
During ASP.NET Core development, many developers encounter HTTP 415 Unsupported Media Type responses when making form POST requests. This error commonly occurs during migration from traditional ASP.NET MVC to ASP.NET Core due to significant differences in request processing mechanisms.
Core Issue Analysis
The root cause lies in the mismatch between the [FromBody] attribute and the media type of form POST requests. In ASP.NET Core, the [FromBody] attribute expects request body content with application/json content type, while traditional form POST requests use application/x-www-form-urlencoded content type.
Here is the typical controller code that causes the problem:
public class MyController : Controller
{
[HttpPost]
public async Task<IActionResult> Submit([FromBody] MyModel model)
{
// Processing logic
}
}
The corresponding HTTP request headers show form-encoded content type:
POST /submit HTTP/1.1
Content-Type: application/x-www-form-urlencoded
...
Solution: Using the Correct Binding Attribute
For form POST requests, the correct solution is to use the [FromForm] attribute instead of [FromBody]:
public class MyController : Controller
{
[HttpPost]
public async Task<IActionResult> Submit([FromForm] MyModel model)
{
// Processing logic
}
}
The [FromForm] attribute is specifically designed to handle requests with application/x-www-form-urlencoded content type, properly binding model parameters from form data.
Detailed Attribute Usage Rules
In ASP.NET Core, binding attribute usage follows specific rules:
- When controllers are annotated with
[ApiController]attribute, binding source attributes like[FromBody],[FromForm]must be explicitly specified - For traditional view controllers, binding attributes can be omitted as the framework automatically infers based on context
[FromBody]is suitable for JSON-formatted request bodies[FromForm]is suitable for form-encoded request data
Alternative Approach: Modifying Request Content Type
If you prefer to continue using the [FromBody] attribute, you can modify the client request's content type:
Content-Type: application/json
This approach requires clients to send JSON-formatted data, which is suitable for API scenarios but not for traditional HTML form submissions.
Advanced Application: Supporting Multiple Content Types
In some scenarios, you may need a single endpoint to support multiple content types. This can be achieved through custom model binders:
public class MultiContentTypeModelBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var request = bindingContext.HttpContext.Request;
if (request.ContentType == "application/json")
{
// Read JSON data from request body
string body;
using (var reader = new StreamReader(request.Body))
{
body = await reader.ReadToEndAsync();
}
var model = JsonConvert.DeserializeObject(body, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(model);
}
else if (request.ContentType == "application/x-www-form-urlencoded")
{
// Bind model from form data
var model = Activator.CreateInstance(bindingContext.ModelType);
foreach (var property in bindingContext.ModelType.GetProperties())
{
var value = request.Form[property.Name];
if (!string.IsNullOrEmpty(value))
{
property.SetValue(model, Convert.ChangeType(value, property.PropertyType));
}
}
bindingContext.Result = ModelBindingResult.Success(model);
}
}
}
Register the custom model binder in Program.cs:
builder.Services.AddControllers(options =>
{
options.ModelBinderProviders.Insert(0, new MultiContentTypeModelBinderProvider());
});
Migration Considerations
When migrating from ASP.NET MVC 5 to ASP.NET Core, pay attention to these key differences:
- ASP.NET Core has stricter content type handling
- Default model binding behavior has changed
- The
[ApiController]attribute introduces additional constraints - Choose appropriate binding attributes based on actual usage scenarios
Best Practice Recommendations
Based on practical development experience, follow these best practices:
- Always use
[FromForm]attribute for HTML form submissions - Use
[FromBody]for API interfaces and ensure clients send JSON data - Consider custom model binders for mixed scenarios
- Explicitly specify binding sources in controllers to avoid relying on framework auto-inference
- Conduct thorough testing to ensure proper handling of different content type requests
By understanding ASP.NET Core's model binding mechanism and correctly using binding attributes, developers can effectively avoid HTTP 415 errors and build more robust web applications.