This product is not supported for your selected Datadog site. ().
Metadata
ID:csharp-best-practices/use-model-binding
Language: C#
Severity: Notice
Category: Best Practices
Description
This rule encourages developers to use model binding instead of directly accessing data from the request object, such as Request.Form or Request.Headers. Model binding provides a clean and declarative way to receive input data by mapping HTTP request data to strongly typed parameters or models in controller actions.
Using model binding improves code readability, reduces boilerplate parsing and validation logic, and leverages built-in framework features like automatic type conversion and validation attributes. It helps prevent errors related to manual data extraction and parsing, which can introduce bugs or security vulnerabilities if not handled carefully.
To comply with this rule, define a model class representing the expected input and use action method parameters with attributes like [FromForm] or [FromHeader] to bind incoming data automatically. For example, use public IActionResult Register([FromForm] UserRegistrationModel model) instead of manually reading from Request.Form. This approach ensures cleaner, safer, and more maintainable code.
Non-Compliant Code Examples
usingMicrosoft.AspNetCore.Mvc;usingSystem;usingSystem.Collections.Generic;namespaceMyApp.Controllers{publicclassUserController:Controller{ [HttpGet]publicIActionResultRegister(){returnView();}// NON-COMPLIANT: Direct access to Request.Headers [HttpPost]publicIActionResultRegister_NonCompliant(){// Noncompliant: Direct access to Request.Headersvarname=Request.Headers["X-User-Name"].ToString();varemail=Request.Headers["X-User-Email"].ToString();varpassword=Request.Headers["X-User-Password"].ToString();varbirthdateStr=Request.Headers["X-User-Birthdate"].ToString();// Manual parsing and error handlingif(!DateTime.TryParse(birthdateStr,outvarbirthdate)){returnBadRequest("Invalid birthdate");}// Manual validationif(string.IsNullOrEmpty(name)||string.IsNullOrEmpty(email)){returnBadRequest("Name and email are required");}// Process registration...returnOk($"User {name} registered successfully");}// COMPLIANT: Using FromHeader attribute (Recommended) [HttpPost("register-compliant")]publicIActionResultRegister_Compliant( [FromHeader(Name = "X-User-Name")]stringname, [FromHeader(Name = "X-User-Email")]stringemail, [FromHeader(Name = "X-User-Password")]stringpassword, [FromHeader(Name = "X-User-Birthdate")]DateTimebirthdate){// Automatic binding and type conversionif(!ModelState.IsValid){returnBadRequest(ModelState);}// Manual validation (or use Data Annotations)if(string.IsNullOrEmpty(name)||string.IsNullOrEmpty(email)){returnBadRequest("Name and email are required");}// Process registration...returnOk($"User {name} registered successfully");}}}
usingMicrosoft.AspNetCore.Mvc;usingSystem;usingSystem.Collections.Generic;namespaceMyApp.Controllers{publicclassUserController:Controller{ [HttpGet]publicIActionResultRegister(){returnView();} [HttpPost]publicIActionResultRegister_NonCompliant(){// Noncompliant: Direct access to Request.Formvarname=Request.Form["name"];varemail=Request.Form["email"];varpassword=Request.Form["password"];varbirthdateStr=Request.Form["birthdate"];// Manual parsing and error handlingif(!DateTime.TryParse(birthdateStr,outvarbirthdate)){returnBadRequest("Invalid birthdate");}// Manual validationif(string.IsNullOrEmpty(name)||string.IsNullOrEmpty(email)){returnBadRequest("Name and email are required");}// Process registration...returnOk($"User {name} registered successfully");}}}
Compliant Code Examples
usingMicrosoft.AspNetCore.Mvc;usingSystem;usingSystem.ComponentModel.DataAnnotations;namespaceMyApp.Controllers{// Model class for registrationpublicclassUserRegistrationModel{ [Required(ErrorMessage = "Name is required")] [StringLength(100, ErrorMessage = "Name cannot exceed 100 characters")]publicstringName{get;set;} [Required(ErrorMessage = "Email is required")] [EmailAddress(ErrorMessage = "Invalid email format")]publicstringEmail{get;set;} [Required(ErrorMessage = "Password is required")] [MinLength(6, ErrorMessage = "Password must be at least 6 characters")] [DataType(DataType.Password)]publicstringPassword{get;set;} [Required(ErrorMessage = "Birthdate is required")] [DataType(DataType.Date)]publicDateTimeBirthdate{get;set;}}publicclassUserController:Controller{ [HttpGet]publicIActionResultRegister(){returnView();}// COMPLIANT: Using Model Binding with [FromForm] [HttpPost]publicIActionResultRegister([FromForm]UserRegistrationModelmodel){// Automatic validation based on Data Annotationsif(!ModelState.IsValid){returnBadRequest(ModelState);}// Process registration with validated model// All type conversions are handled automatically// birthdate is already a DateTime, not a stringreturnOk($"User {model.Name} registered successfully");}// Alternative: If you want to return validation errors to a view [HttpPost("register-view")]publicIActionResultRegisterWithView([FromForm]UserRegistrationModelmodel){if(!ModelState.IsValid){// Return to view with validation errorsreturnView("Register",model);}// Process registration...TempData["SuccessMessage"]=$"User {model.Name} registered successfully";returnRedirectToAction("Success");}// Alternative: Using individual parameters (less common, but valid) [HttpPost("register-params")]publicIActionResultRegisterWithParameters( [FromForm]stringname, [FromForm]stringemail, [FromForm]stringpassword, [FromForm]DateTimebirthdate){// Manual validation needed with this approachif(string.IsNullOrEmpty(name)||string.IsNullOrEmpty(email)){returnBadRequest("Name and email are required");}if(!ModelState.IsValid){returnBadRequest(ModelState);}// Process registration...returnOk($"User {name} registered successfully");} [HttpGet("success")]publicIActionResultSuccess(){returnView();}}}
Seamless integrations. Try Datadog Code Security
Datadog Code Security
Try this rule and analyze your code with Datadog Code Security
How to use this rule
1
2
rulesets:- csharp-best-practices # Rules to enforce C# best practices.
Create a static-analysis.datadog.yml with the content above at the root of your repository
Use our free IDE Plugins or add Code Security scans to your CI pipelines