ASP.NET Core面试精讲系列五

B站影视 港台电影 2025-10-08 16:38 1

摘要:/实现IModelBinder用[ModelBinder(BinderType=...)]标注参数(可选)全局注册IModelBinderProviderusing Microsoft.AspNetCore.Mvc;using Microsoft.AspNet

ASP.NET Core 会把请求里的值自动映射到你的参数/模型,依据参数类型与特性选择数据源:

路由(route)

查询字符串(query)

表单(form,含文件 IFormFile)

请求体(JSON 等 application/json)

请求头(headers)

服务容器([FromServices])

[FromQuery][FromForm]、[FromBody]、[FromHeader]默认从 路由 / 查询 / 表单 绑定;可用[FromRoute][FromQuery][FromForm]指定。

复杂类型(自定义类/DTO)

[ApiController][FromBody][FromBody]参数。[HttpPost]
public IActionResult Create([FromBody] Product product) => Ok;

[HttpGet("{id}")]
public IActionResult Get([FromRoute] int id, [FromQuery] int page = 1) => Ok;
[FromRoute][FromBody]/ 实现 IModelBinder 用 [ModelBinder(BinderType=...)]标注参数 (可选)全局注册 IModelBinderProviderusing Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
publicsealedclassUserIdFromHeaderBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext ctx)
{
if (ctx.HttpContext.Request.Headers.TryGetValue("X-User-Id", outvar v)
&& long.TryParse(v, outvar id))
ctx.Result = ModelBindingResult.Success(id);
else
ctx.ModelState.AddModelError(ctx.FieldName, "Missing or invalid X-User-Id header.");
return Task.CompletedTask;
}
}
[HttpGet("me")]
public IActionResult GetMe(
[ModelBinder(BinderType = typeof(UserIdFromHeaderBinder))] long userId)
=> Ok(userId);

74. 多个绑定源特性([FromBody]、[FromQuery]等)

一个 Action 只能绑定一个 [FromBody]参数。若需接收多个字段,请封装为一个 DTO(或使用 multipart/form-data /[FromForm])。

的控制器里,复杂类型默认从 Body 绑定,简单类型默认从 Route/Query 绑定。

数据注解验证(MVC & API 通用)

public class User
{
[Required]
[StringLength(50)]
[EmailAddress]
public string Email { get; set; }
}

适用于 MVC 与 Web API 的输入验证。

带 [ApiController]时,验证失败会自动返回 400。 传统 MVC 中,使用 ModelState.IsValid判断并处理。继承ValidationAttribute定义规则:public sealedclassMustBeEvenAttribute : ValidationAttribute
{
public MustBeEvenAttribute => ErrorMessage = "Number must be even.";

public override bool IsValid(object value)
{
if (valueisnull) returntrue; // 交给 [Required]
if (valueisint i) return (i % 2) == 0;
returnint.TryParse(value.ToString, outvar n) && (n % 2) == 0;
}
}

使用方式:

用于模型级/跨字段业务校验;在数据注解之后执行,错误进入 ModelState(API 场景下[ApiController]会自动返回 400)。using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

publicclassProduct : IValidatableObject
{
publicstring Name { get; set; }
publicdecimal Price { get; set; }

public IEnumerable Validate(ValidationContext context)
{
if (Price 0)
yield return new ValidationResult("价格必须为非负数", new { nameof(Price) });

if (string.IsNullOrWhiteSpace(Name))
yield return new ValidationResult("名称必填", new { nameof(Name) });

// 跨字段示例:名称包含“高级”时,价格需≥1000
if (Name?.Contains("高级") == true && Price 1000)
yield return new ValidationResult("“高级”商品价格需 ≥ 1000",
new { nameof(Name), nameof(Price) });
}
}

安装:

dotnet add package FluentValidation.AspNetCore

验证器:

using FluentValidation;
public class UserValidator : AbstractValidatorUser>
{
public UserValidator
{
RuleFor(x => x.Email).NotEmpty.EmailAddress.MaximumLength(50);
}
}

注册:

// Program.cs
builder.Services
.AddFluentValidationAutoValidation
.AddValidatorsFromAssemblyContaining

相比数据注解:规则更清晰、可单元测试,支持条件/规则集/本地化等高级用法。

有 [ApiController](Web API):模型验证自动执行;ModelState 无效时自动返回 400 BadRequest,负载为 ValidationProblemDetails(application/problem+json)。 无 [ApiController](MVC/Razor Pages):需手动检查。if (!ModelState.IsValid) return View(model); // 回显表单与错误

示例(API 自动 400):

[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
[HttpPost]
public IActionResult Create(User dto) => Ok; // 无需手动判 ModelState
}
// Web API
if (!ModelState.IsValid)
return BadRequest(ModelState);

// MVC
// if (!ModelState.IsValid) return View(model);

ASP.NET Core 原生支持嵌套属性与集合的模型绑定(JSON 或表单)。

public class Order {
public Customer Customer { get; set; }
public List
}
public class Customer { public string Name { get; set; } }
public class Product { public int Id { get; set; } public string Name { get; set; } }
{ "customer": { "name": "Alice" }, "products": [ { "id": 1, "name": "Pen" } ] }
表单键名:Customer.Name=Alice
Products[0].Id=1
Products[0].Name=Pen

要点:属性名匹配 + 索引标记([0]、[1])即可完成集合绑定。

必填字段用 [Required];范围/格式用[Range]/[StringLength]/[EmailAddress]等。

可选值使用可空类型(string?、int?、DateTime?)。

在 API 中( [ApiController])验证失败会自动返回 400;在 MVC 中手动检查 ModelState:if (!ModelState.IsValid) return ValidationProblem(ModelState);

需要自定义提示时,设置 ErrorMessage 或使用 FluentValidation 定制错误信息。

仅对“缺少绑定值”严格要求时,可用 [BindRequired](常见于 Query/Route)。

模型绑定不会清洗输入,只是把原始数据填到模型里。

防止攻击(XSS、注入),需清理:

输出编码:默认 Razor 会 HTML 编码;谨慎使用 @Html.Raw。

输入校验/规范化:长度、格式、白名单(必要时再做“去标签”)。

防注入:始终使用参数化查询/ORM,不拼接 SQL。

文件上传:校验扩展名与 MIME、限制大小、病毒/恶意扫描、随机文件名、存放在非 Web 根目录。

(可选)开启 CSP、HttpOnly/SameSite Cookie,降低 XSS 风险。

用于表单文件上传(需;不要用)。[HttpPost]
public async Task Upload([FromForm] IFormFile file)
{
if (file == null || file.Length == 0) return BadRequest("Empty file.");

var uploads = Path.Combine(builder.Environment.ContentRootPath, "uploads");
Directory.CreateDirectory(uploads);

var safeName = Path.GetRandomFileName + Path.GetExtension(file.FileName);
var path = Path.Combine(uploads, safeName);

awaitusingvar fs = System.IO.File.Create(path);
await file.CopyToAsync(fs);

return Ok(new { file = safeName, size = file.Length });
}

要点

多文件: List

放到非 Web 根目录,使用随机文件名,白名单校验扩展名/MIME,限制大小

需要时做病毒/恶意扫描与异步流式写入

来源:opendotnet

相关推荐