org.sonar.plugins.csharp.S6932.html Maven / Gradle / Ivy
The HttpRequest
class provides access to the raw request data through the QueryString
, Headers
, and
Forms
properties. However, whenever possible it is recommended to use model binding instead of directly accessing the input data.
Why is this an issue?
Both ASP.Net MVC implementations - Core and Framework - support model binding in a comparable fashion. Model binding streamlines the
process by automatically aligning data from HTTP requests with action method parameters, providing numerous benefits compared to manually parsing raw
incoming request data:
-
Simplicity
-
Model binding simplifies the code by automatically mapping data from HTTP requests to action method parameters. You don’t need to write any
code to manually extract values from the request.
-
Type Safety
-
Model binding provides type safety by automatically converting the incoming data into the appropriate .NET types. If the conversion fails, the
model state becomes invalid, which you can easily check using ModelState.IsValid
.
-
Validation
-
With model binding, you can easily apply validation rules to your models using data annotations. If the incoming data doesn’t comply with these
rules, the model state becomes invalid.
-
Security
-
Model binding helps protect against over-posting attacks by only including properties in the model that you explicitly bind using the
[Bind]
attribute or by using view models that only contain the properties you want to update.
-
Maintainability
-
By using model binding, your code becomes cleaner, easier to read, and maintain. It promotes the use of strongly typed views, which can provide
compile-time checking of your views.
How to fix it in ASP.NET Core
Request.Form
, Request.Form.Files
, Request.Headers
, Request.Query
and Request.RouteValues
are keyed
collections that expose data from the incoming HTTP request:
-
Request.Form
- application/x-www-form-urlencoded
form data from the HTTP request body
-
Request.Form.Files
- multipart/form-data
file data from the HTTP request body
-
Request.Headers
- HTTP Header values
-
Request.Query
- URL parameter values
-
Request.RouteValues
- Values extracted from the path portion of the URL
Model binding can bind these keyed collections to
- action method parameters by matching the key to the parameter name or
- the property of a complex type by matching the key to the property name.
To replace the keyed collection access, you can:
Replace
with parameter binding
or complex type binding
or route binding
Request.Form["id"]
optional [FromForm]
attribute on the parameter or a FormCollection
parameter
optional [FromForm]
attribute on the property
IFormFile
, IFormFileCollection
, or
IEnumerable<IFormFile>
parameter
Request.Headers["id"]
[FromHeader]
attribute on the parameter
[FromHeader]
attribute on the property
Request.Query["id"]
optional [FromQuery]
attribute on the parameter
optional [FromQuery]
attribute on the property
Request.RouteValues["id"]
optional [FromRoute]
attribute on the parameter
optional [Route("{id}")]
attribute on the
action method/controller or via conventional routing
The Model Binding in ASP.NET Core article describes the
mechanisms, conventions, and customization options for model binding in more detail. Route-based binding is described in the Routing to controller actions in ASP.NET Core document.
Code examples
Noncompliant code example
public IActionResult Post()
{
var name = Request.Form["name"]; // Noncompliant: Request.Form
var birthdate = DateTime.Parse(Request.Form["Birthdate"]); // Noncompliant: Request.Form
var origin = Request.Headers[HeaderNames.Origin]; // Noncompliant: Request.Headers
var locale = Request.Query.TryGetValue("locale", out var locales)
? locales.ToString()
: "en-US"; // Noncompliant: Request.Query
// ..
}
Compliant solution
public record User
{
[Required, StringLength(100)]
public required string Name { get; init; }
[DataType(DataType.Date)]
public DateTime? Birthdate { get; init; }
}
public IActionResult Post(User user, [FromHeader] string origin, [FromQuery] string locale = "en-US")
{
if (ModelState.IsValid)
{
// ...
}
}
How does this work?
Model binding in ASP.NET Core MVC and ASP.NET MVC 4.x works by automatically mapping data from HTTP requests to action method parameters. Here’s a
step-by-step breakdown of how it works:
- Request Data When a user submits a form or sends a request to an ASP.NET application, the request data might include form
data, query string parameters, request body, and HTTP headers.
- Model Binder The model binder’s job is to create .NET objects from the request data. It looks at each parameter in the action
method and attempts to populate it with the incoming data.
- Value Providers The model binder uses Value Providers to get data from various parts of the request, such as the query string,
form data, or route data. Each value provider tells the model binder where to find values in the request.
- Binding The model binder tries to match the keys from the incoming data with the properties of the action method’s parameters.
If a match is found, it attempts to convert the incoming data into the appropriate .NET type and assigns it to the parameter.
- Validation If the model binder can’t convert the value or if the converted value doesn’t pass any specified validation rules,
it adds an error to the
ModelState.Errors
collection. You can check ModelState.IsValid
in your action method to see if any
errors occurred during model binding.
- Action Method Execution The action method is executed with the bound parameters. If
ModelState.IsValid
is
false
, you can handle the errors in your action method and return an appropriate response.
See the links in the Resources section for more information.
How to fix it in ASP.NET MVC 4.x
Request.Form
and Request.QueryString
are keyed collections
that expose data from the incoming HTTP request:
-
Request.Form
- application/x-www-form-urlencoded
form data from the HTTP request body
-
Request.QueryString
- URL parameter values
Model binding can bind these keyed collections to
- action method parameters by matching the key to the parameter name or
- the property of a complex type by matching the key to the property name.
To replace the keyed collection access, you can:
Replace
with parameter binding
or complex type binding
Request.Form["id"]
optional [Bind]
attribute on the
parameter or a FormCollection
parameter
optional [Bind]
attribute on the
parameter or type
Request.QueryString["id"]
optional [Bind]
attribute on the
parameter
property name must match query parameter key
Code examples
Noncompliant code example
public ActionResult Post()
{
var name = Request.Form["name"]; // Noncompliant: Request.Form
Debug.WriteLine(Request.Form[0]); // Compliant: Binding by index is not supported.
var birthdate = DateTime.Parse(Request.Form["Birthdate"]); // Noncompliant: Request.Form
var cultureName = Request.QueryString["locale"] ?? "en-US"; // Noncompliant: Request.QueryString
// ..
}
Compliant solution
public class User
{
[Required, StringLength(100)]
public string Name { get; set; }
[DataType(DataType.Date)]
public DateTime? Birthdate { get; set; }
}
public ActionResult Post(User user, [Bind(Prefix = "locale")] string cultureName = "en-US")
{
if (ModelState.IsValid)
{
// ...
}
}
How does this work?
Model binding in ASP.NET Core MVC and ASP.NET MVC 4.x works by automatically mapping data from HTTP requests to action method parameters. Here’s a
step-by-step breakdown of how it works:
- Request Data When a user submits a form or sends a request to an ASP.NET application, the request data might include form
data, query string parameters, request body, and HTTP headers.
- Model Binder The model binder’s job is to create .NET objects from the request data. It looks at each parameter in the action
method and attempts to populate it with the incoming data.
- Value Providers The model binder uses Value Providers to get data from various parts of the request, such as the query string,
form data, or route data. Each value provider tells the model binder where to find values in the request.
- Binding The model binder tries to match the keys from the incoming data with the properties of the action method’s parameters.
If a match is found, it attempts to convert the incoming data into the appropriate .NET type and assigns it to the parameter.
- Validation If the model binder can’t convert the value or if the converted value doesn’t pass any specified validation rules,
it adds an error to the
ModelState.Errors
collection. You can check ModelState.IsValid
in your action method to see if any
errors occurred during model binding.
- Action Method Execution The action method is executed with the bound parameters. If
ModelState.IsValid
is
false
, you can handle the errors in your action method and return an appropriate response.
See the links in the Resources section for more information.
Resources
Documentation
- Microsoft Learn - Asp.Net Core - Model Binding in ASP.NET
Core
- Microsoft Learn - Asp.Net Core - Model validation in ASP.NET Core
MVC and Razor Pages
- Microsoft Learn - Asp.Net Core - Custom Model Binding
in ASP.NET Core
- Microsoft Learn - Asp.Net Core - HttpRequest.Form Property
- Microsoft Learn - Asp.Net Core - IFormCollection.Files Property
- Microsoft Learn - Asp.Net Core - HttpRequest.Headers Property
- Microsoft Learn - Asp.Net Core - HttpRequest.Query Property
- Microsoft Learn - Asp.Net Core - HttpRequest.RouteValues Property
- Microsoft Learn - Asp.Net Core - FormCollection
Class
- Microsoft Learn - Asp.Net Core - IFormFile
Interface
- Microsoft Learn - Asp.Net Core - IFormFileCollection Interface
- Microsoft Learn - Asp.Net Core - BindAttribute
Class
- Microsoft Learn - ASP.NET MVC 4.x - Parameter Binding in
ASP.NET Web API
- Microsoft Learn - ASP.NET MVC 4.x - Adding a New Controller
- Microsoft Learn - ASP.NET MVC 4.x - Adding a New Model
- Microsoft Learn - ASP.NET MVC 4.x - Adding Validation
- Microsoft Learn - ASP.NET MVC 4.x - Model Validation in
ASP.NET Web API
- Microsoft Learn - ASP.NET MVC 4.x - HttpRequest.Form
Property
- Microsoft Learn - ASP.NET MVC 4.x - HttpRequest.QueryString Property
- Microsoft Learn - ASP.NET MVC 4.x - BindAttribute Class
- MDN - HTTP request methods > POST
- MDN - Request header
- MDN - What is a URL?