前言
Minimal API(Net9)
Minimal API 是 ASP.NET Core 提供的一种“极简”风格的写法:用非常少的样板代码(通常在 Program.cs)直接注册路由和处理器,适合微服务、后端小服务、快速原型或需要极高性能/低开销的 HTTP 接口。它并不是 MVC 的替代品——大型系统仍然可能更适合 Controller + 属性滤器 的组织方式。
Minimal API 的强大之处在于:路由、参数绑定、依赖注入、OpenAPI 元数据、返回结果都可以用简短的委托(lambda 或方法)写清楚。
Minimal API 是 .NET 6/7/8/9+ 推出的一个轻量化 API 构建方式,特点是:
- 无需控制器和 MVC 框架,直接在
Program.cs里定义路由和处理逻辑; - 语法简洁,只需
app.MapGet(...)、app.MapPost(...)等方法; - 支持依赖注入、验证、OpenAPI 自动生成;
- 类型安全:方法签名里可以定义参数类型、返回类型。
1.API Version分组
v1, v2, api代表不同的 API 版本或不同的路由分组(例如 app.MapGroup("/api/v1") / app.MapGroup("/api/v2") / app.MapGroup("/api"))。
之前有提过,这里不详说了

2.MapGet(查询资源(Read))
基本语法
-
/route→ 请求路径 -
HandlerFunction→ 请求到达时执行的处理函数,可以是同步或异步
app.MapGet("/route", HandlerFunction);
功能:
- 处理 HTTP GET 请求
- 查询/获取数据,不修改服务器状态
请求与参数:
- 路径参数:
/items/{id}→ 自动绑定 - Query 参数:
/items?page=1&pageSize=10 - 依赖注入:可以直接注入服务对象
- 不支持请求体(GET 本身不应该有 body)

3.MapPost(创建资源(Create))
功能:
- 处理 HTTP POST 请求
- 创建资源或提交数据
- 可以带请求体
请求与参数:
- 请求体绑定:DTO、对象
- 依赖注入:服务对象可直接注入
- 路径参数:可有可无(一般 POST 创建新资源时不需要 ID)

4.MapPut(更新资源(Update))
功能
- 处理 HTTP PUT 请求
- 更新整个资源(覆盖原资源)
请求与参数
- 路径参数:标识资源 ID
- 请求体:完整对象 DTO
- 依赖注入:服务对象可直接注入

5.MapDelete(删除资源(Delete))
功能
- 处理 HTTP DELETE 请求
- 删除资源
请求与参数
- 路径参数:标识资源 ID
- 一般无请求体
- 依赖注入:服务对象可直接注入

6.路由参数约束类型
| 约束类型 | 示例 | 说明 |
|---|---|---|
int |
{id:int} |
只能是整数 |
long |
{id:long} |
只能是长整型 |
bool |
{flag:bool} |
只能是 true/false |
datetime |
{date:datetime} |
必须是有效日期时间 |
decimal |
{price:decimal} |
必须是小数 |
double |
{value:double} |
必须是双精度浮点 |
minlength(value) |
{name:minlength(3)} |
字符串最小长度 |
maxlength(value) |
{name:maxlength(10)} |
字符串最大长度 |
range(min,max) |
{age:range(1,120)} |
数值范围限制 |
alpha |
{name:alpha} |
只能包含字母 |
regex(pattern) |
{code:regex(^\d{4}$)} |
正则表达式匹配 |

7.参数绑定(MapGet/MapPost/MapPut/MapDelete)
| 接口方法 | 参数类型 | 来源 | 支持情况 | 使用场景 | 注意事项/限制 |
|---|---|---|---|---|---|
| MapGet | 路由参数 | URL 路径 {id} |
✅ | 标识资源 ID | 名称必须匹配路由模板;可使用约束(int、minlength 等);类型不匹配返回 404 |
| 查询参数 | URL Query | ✅ | 分页、筛选、搜索 | 可选参数可设置默认值或 ?;类型不匹配返回 400 |
|
| 请求体参数 | HTTP Body | ❌ | 不推荐 GET 请求使用 | Minimal API 默认不绑定 GET Body | |
| DI 服务 | DI 容器 | ✅ | 注入服务对象、数据库等 | 类型必须已注册到容器 | |
| MapPost | 路由参数 | URL 路径 {id} |
✅ | 标识资源或更新目标 | 可用约束限制类型 |
| 查询参数 | URL Query | ✅ | 补充额外参数 | 可选参数可设置默认值 | |
| 请求体参数 | HTTP Body | ✅ | 创建/更新资源 | 常用 DTO 或 POCO 类自动 JSON 绑定 | |
| DI 服务 | DI 容器 | ✅ | 注入服务对象、数据库等 | 支持同步或异步注入 | |
| MapPut | 路由参数 | URL 路径 {id} |
✅ | 更新资源 | 常与 Body 参数一起使用 |
| 查询参数 | URL Query | ✅ | 补充更新条件 | 可选参数可设置默认值 | |
| 请求体参数 | HTTP Body | ✅ | 更新资源 | DTO/POCO 类绑定 JSON | |
| DI 服务 | DI 容器 | ✅ | 注入服务对象、数据库等 | 支持异步或同步 | |
| MapDelete | 路由参数 | URL 路径 {id} |
✅ | 标识删除资源 | 必须匹配路由模板 |
| 查询参数 | URL Query | ✅ | 补充删除条件 | 可选参数可设置默认值 | |
| 请求体参数 | HTTP Body | ❌ | 不推荐 DELETE 使用 | DELETE 一般不绑定 Body | |
| DI 服务 | DI 容器 | ✅ | 注入服务对象、数据库等 | 支持同步或异步 |
8.Scalar接口文档配置
这四个接口比较简单,知道用处就行了
8.1WithName
WithName("接口名称") 用于给接口设置唯一标识名称,方便在系统内部引用或查找
8.2WithSummary
WithSummary("简短说明") 用于提供接口的一句话功能概述,让开发者快速了解接口作用;
8.3WithDescription
WithDescription("详细描述") 用于给接口写详细说明,包括逻辑、参数和返回结果信息,便于团队理解和维护;
8.4WithTags
WithTags("标签") 用于对接口进行分组管理,把相关接口归类到同一个标签下,方便在 Scalar 管理界面筛选和查找。
8.5总结

9.[ProducesResponseType<ProblemDetails>]
[ProducesResponseType]:告诉框架和 Scalar该 API 方法可能返回的 HTTP 状态码和响应类型。
<ProblemDetails>:ProblemDetails 是 ASP.NET Core 内置的 错误响应模型,符合 RFC 7807 标准。
- 常用字段
type:错误类型 URItitle:错误标题status:HTTP 状态码detail:错误描述instance:请求实例 URI
- 这个接口在 400 Bad Request 时,会返回一个
ProblemDetails对象给客户端。
StatusCodes.Status400BadRequest:指明返回的 HTTP 状态码是 400(请求参数错误)。
"application/problem+json":指定返回的 Content-Type。当返回 ProblemDetails 时,HTTP 响应会带上 Content-Type: application/problem+json。这符合标准 RFC 7807,方便客户端统一解析错误信息。
这行特性说明:
- 这个 API 方法可能返回 400 Bad Request
- 返回体类型是 ProblemDetails
- Content-Type 是
application/problem+json

告诉框架和 Scalar该 API 方法可能返回的 HTTP 状态码和响应类型。

10.参数绑定系统(Parameter Binding)
10.1概念
Minimal API 会根据参数的类型与绑定特性(attribute)来决定把请求的哪一部分绑定到这个参数上。常见绑定源包括:
- 路由(route path)
{id} - 查询字符串(query)
?q=... - 请求体(body)——通常是 JSON
- 表单(form / multipart)
- 请求头(header)
- DI 容器(services)
- HttpContext / CancellationToken / 特定 framework 类型
原则:简单标量类型(int、string、bool、DateTime、Guid、enum 等)优先从路由/查询读;复杂引用类型(class/record)通常从 body 绑定(除非你显式要求其它来源)。
10.2绑定规则
Route(路由)
- 如果路由模板里有同名占位符(
{id}),则会从路由数据绑定到对应参数。 - 路由约束(
{id:int})会做类型校验,不匹配返回 404。
Query(查询字符串)
- 对于简单类型(或标注了
[FromQuery]的复杂类型属性),会在 URL Query 中查找同名键并绑定。 - 可用于分页、过滤、可选参数等。
Body(请求体,通常 JSON)
- 复杂类型(class / record)默认会从请求体绑定(用于 POST/PUT 等)。
- 一个 endpoint 只能有一个 body 参数(请求体只能反序列化一次)——若需要多个部分,需合并到一个 DTO 或使用
AsParameters/FromForm等策略。
Services(DI)
- 对象类型会尝试从 DI 容器解析(当类型在容器中有注册,或显式用
[FromServices])。 - 典型:
ILogger<T>,DbContext等。
Special framework types
HttpContext,HttpRequest,HttpResponse,CancellationToken,ClaimsPrincipal等会被框架直接提供。
Header / Form / File
[FromHeader]、[FromForm]、IFormFile、IFormCollection等有专门来源。文件需要multipart/form-data。
10.3绑定属性
[FromRoute]:从路由绑定(强制)。
[FromQuery]:从查询字符串绑定。
[FromBody]:从请求体绑定(显式)。
[FromHeader]:从请求头绑定。
[FromForm]:从表单数据绑定(multipart/form-data)。
[FromServices]:从 DI 容器解析。
[AsParameters](Minimal API helper):将一个复杂类型的属性当作多个简单参数从路由/查询/Form 绑定(常用于把 DTO 的属性按字段绑定到 query/route,而不是整个对象作为 body)。
绑定属性([BindProperties] )是 MVC 的概念,Minimal API 常用的是 AsParameters 风格(记录/类包裹参数属性)。
10.4总结
简单类型(int/long/string/bool/Guid/enum):route -> query -> header
复杂类型(class/record):body(默认)(或 [FromQuery]/[AsParameters])
注入服务:从 DI(或 [FromServices])
文件:IFormFile 来自 multipart/form-data
HttpContext / CancellationToken / ClaimsPrincipal:框架直接注入
一个 endpoint 仅允许一个 body 参数
11.[AsParameters]
[AsParameters] 是 Minimal API 的新特性,用于 把一个复杂对象的多个属性自动绑定到请求参数。
换句话说,如果你有一个 DTO(数据传输对象),使用 [AsParameters] 可以让它:
- 自动从 查询字符串 (
query) 获取数据 - 自动从 路由参数 (
route) 获取数据 - 自动从 服务容器 (
services) 注入依赖
当客户端访问 /items?pageIndex=1&pageSize=10 时:
request.PageIndex = 1request.PageSize = 10
避免了每个参数单独写 [FromQuery] int pageIndex, [FromQuery] int pageSize

PaginationRequest:自动从查询字符串读取分页参数 PageIndex 和 PageSize
CatalogServices:自动从 DI 容器注入服务实例(数据库上下文、AI 服务等)
- 普通类型参数(如
int pageIndex):尝试从 路由 或 查询字符串绑定 - 复杂类型参数(如
CatalogServices):如果参数是复杂对象,且构造函数的依赖都可解析(如[FromServices]、IOptions<T>、ILogger<T>),就会按需创建对象,即使你没在全局 DI 注册整个服务类。

为什么
AsParameters可以自动注入复杂类型参数?
AsParameters执行流程:
- 参数类型检测:Minimal API 会检查
CatalogServices是一个复杂类型,而不是简单类型(int、string 等)。- 构造对象实例:
- 它会尝试调用
CatalogServices的构造函数。- 构造函数里的每个参数:
- 如果参数类型已注册到 DI 容器 → 直接从容器获取
- 如果参数是
IOptions<T>、ILogger<T>→ .NET 内置自动解析- 如果参数加了
[FromServices]→ 强制从 DI 获取- 如果参数类型无法解析 → 报错
Minimal API 会“按需”构造
CatalogServices,不需要你显式在全局注册整个类。
使用前提:构造函数的依赖类型必须可解析,即
CatalogServices构造函数中需要的对象必须已经注册到 DI 容器,或是 .NET 内置可解析类型(如ILogger<T>、IOptions<T>),否则 Minimal API 无法自动创建实例。以下的这些对象在
Programe中已经提前注入了
[AsParameters]主要用于“批量绑定查询/路由 + 注入服务”。
如果你是 POST/PUT 的请求体对象,Minimal API 会自动把 JSON 映射到你的复杂对象,所以不需要[AsParameters]。
12.[FromBody]
本质与作用
-
[FromBody]显式告诉框架把参数从 HTTP 请求体(Body)反序列化。 -
对于复杂引用类型(class / record),Minimal API 默认会把它们当作 body 绑定,即使不写
[FromBody]也会从 Body 解析。 -
[FromBody]最主要的作用是在需要明确或覆盖默认绑定行为时使用(例如:对简单类型强制从 body 读取,或与[FromQuery]等共同使用来消除歧义)。
只能有一个 Body 参数
-
HTTP 请求体只能被反序列化一次;因此 一个 endpoint 只能有一个参数从 Body 绑定。
-
如果你声明多个将从 body 绑定的参数(显式或隐式),会引发运行时错误或不可预期行为。解决方案:
-
把多个字段合并为一个 DTO(推荐)。
-
将一些字段放到 query/route/header/form 中。
-
使用
multipart/form-data并在 server 侧手动解析表单字段与文件。
-
Minimal API 默认会把CatalogItem当作 body 绑定,即使不写 [FromBody] 也会从 Body 解析。

13.HttpContext
HttpContext 表示一次 HTTP 请求的上下文(context)——包含请求、响应、用户信息、路由数据、服务容器、连接信息等。它是一次请求的“全局对象”,在中间件和 endpoint 处理器中都可访问。每个请求有且只有一个 HttpContext 实例(按请求范围生命周期)。
在 Minimal API(或 endpoint handler)方法里直接声明 HttpContext 参数是完全合法且常见的做法。框架会在每次请求执行该 handler 时,把当前请求对应的 HttpContext 自动传入。
不需要也不应该把 HttpContext 直接注入并长期保存在像 CatalogServices 这样的服务实例里。
在 Minimal API 中使用HttpContext
app.MapGet("/ping", (HttpContext ctx) => {return Results.Text($"Path: {ctx.Request.Path}");
});

在任何服务/中间件中通过构造函数注入 IHttpContextAccessor(当无法直接注入 HttpContext 时):
// 注:要使用 IHttpContextAccessor 需要在 Program.cs 注册:
builder.Services.AddHttpContextAccessor();public class MyService {private readonly IHttpContextAccessor _acc;public MyService(IHttpContextAccessor acc) => _acc = acc;public void Do() {var ctx = _acc.HttpContext;}
}
为什么
Minimal API可以直接使用HttpContext— 原理(Parameter Binding)ASP.NET Core 的 Minimal API 有一个参数绑定系统(parameter binding)。
当框架调用 handler(例如
UpdateItem)时,会根据参数类型和特性决定如何绑定每个参数:如果参数类型是HttpContext,框架把当前请求的HttpContext实例传入。
14.[Description]
类型:System.ComponentModel.DescriptionAttribute
作用
- 为方法参数提供 文本说明
- 在 自动生成 API 文档 时显示
- 增强代码可读性,方便前端或客户端开发者理解参数用途
接口属性
| 属性 | 功能 |
|---|---|
[Description("...")] |
提供参数用途说明 |
[Required] |
必填校验,缺省会返回 400 |
[MinLength(1)] |
字符串最小长度校验 |
[FromQuery] / [FromBody] |
指定参数绑定来源(Query / Body) |
看到 [Description(...), Required, MinLength(1)],可能觉得这些都属于 Description 的一部分,其实不是。这里发生的是 C# 特性的叠加(Attribute stacking),每个特性都是独立的。

15.IQueryable动态查询
IQueryable动态查询应该是EFCore的知识点,eshop项目为了方便大家学习,所以分层比较简单,数据查询也写在MiniAPI代码里,这里一起总结一下
概念
IQueryable<T> 是 延迟执行的查询接口,允许在 数据库层面动态构建 LINQ 查询。
与 IEnumerable<T> 不同:
IQueryable<T>:查询在数据库执行,支持动态条件、分页、排序。IEnumerable<T>:查询在内存执行,先拉取数据再处理,效率低。
延迟执行与 SQL 生成
IQueryable 不会立即访问数据库。SQL 查询直到调用以下方法时才生成并执行:
ToListAsync():获取数据列表LongCountAsync():获取总记录数
优点:
- 动态过滤:根据传入参数组合不同条件
- 高效分页:
Skip+Take在数据库层执行 - 减少内存占用:只查询必要的数据
强制转换 (IQueryable<CatalogItem>)
不是必须,只是 明确意图:
- 强调这是延迟查询
- 避免歧义,保证类型兼容
即使省略强转,EF Core 也会自动识别为 IQueryable<T>。
使用 IQueryable<CatalogItem> 可以在数据库层动态构建查询、延迟执行、支持分页和排序。
把查询逻辑写在这里其实是不太合理的。Minimal API 中尽量保持 Endpoint 简洁,把查询逻辑抽到 Service 层或 Query 层,是更合理的架构方式。

16.响应结果
16.1TypedResults
TypedResults 是 .NET 9+ Minimal API 提供的静态辅助类,它的作用是:
- 1.快速生成 HTTP 响应对象(实现了
IResult接口); - 2.类型安全:可以指定返回类型(如
Ok<CatalogItem>、BadRequest<ProblemDetails>); - 3.简化代码:不用手动操作
HttpContext.Response; - 4.自动支持 OpenAPI / Scalar 文档生成。
换句话说,TypedResults 就是 Minimal API 的“响应工厂”,帮你快速生成类型安全的 HTTP 响应,并配合 Results<T1,T2,...> 支持多分支返回,自动处理状态码和响应体,同时可生成 OpenAPI 文档。
常用方法及作用
| 方法 | 返回类型 | HTTP 状态码 | 用途 |
|---|---|---|---|
Ok<T>(T value) |
Ok<T> |
200 | 返回成功数据 |
Created(uri) / Created<T>(uri, T value) |
Created / Created<T> |
201 | 创建资源 |
NoContent() |
NoContent |
204 | 成功但不返回内容 |
BadRequest<T>(T value) |
BadRequest<T> |
400 | 请求无效或参数错误 |
NotFound() / NotFound<T>(T value) |
NotFound / NotFound<T> |
404 | 资源不存在 |
PhysicalFile(...) |
PhysicalFileHttpResult |
200 | 返回磁盘文件或流 |
16.2IResult
IResult 是 Minimal API 的统一返回接口,核心作用是:
-
抽象所有 HTTP 响应,包括状态码、响应体、头信息等;
-
框架执行 endpoint 时,会调用
Task ExecuteAsync(HttpContext httpContext);
把返回的 IResult 写入 HTTP 响应。
换句话说,任何 Minimal API 返回值最终都会被转成 IResult。
IResult 的核心作用
- 封装 HTTP 响应逻辑:处理状态码、响应体、Content-Type、Headers 等;例如:
Ok<T>→ 200,NotFound→ 404,NoContent→ 204。 - 类型安全:与
TypedResults配合,可以返回指定类型的数据,如Ok<CatalogItem>、BadRequest<ProblemDetails>。 - 支持多种返回类型:配合
Results<T1,T2,...>使用,表示一个 endpoint 可以返回多种不同的结果。
TypedResults 是 Minimal API 的“响应工厂”,生成各种 IResult 实例;IResult 是 Minimal API 的统一返回接口,所有 endpoint 返回的结果最终都会通过它生成 HTTP 响应。
16.3Ok<T>
Ok<T> 是 Minimal API 框架中用于返回 HTTP 200 成功响应 的一种 强类型结果(Typed Result)。
它表示:“请求已成功执行,并且响应体中包含类型为 T 的数据。”
Ok<T> 只在你主动调用时返回 200; 方法抛异常不会自动变成 200,而是会被框架捕获返回 500(或自定义错误)。
和传统 Controller 的区别
| 对比项 | Minimal API (Ok<T>) |
Controller (return Ok(obj)) |
|---|---|---|
| 所在命名空间 | Microsoft.AspNetCore.Http.HttpResults |
Microsoft.AspNetCore.Mvc |
| 返回接口 | IResult |
IActionResult |
| 编程范式 | 函数式 API(轻量) | MVC 模式(重量) |
| 特点 | 类型安全(Ok<T> 明确类型) |
通用型返回,类型不明确 |
Ok<T> 适合演示和简单场景, 生产代码建议使用 Results<...> 明确所有可能的响应。
| 写法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
Task<Ok<T>> |
简洁、直观 | 不能表达失败分支 | Demo、内部简单接口 |
Task<IResult> |
灵活、可动态返回 | Scalar 无法自动推断类型 | 原型、实验性接口 |
Task<Results<Ok<T>, NotFound, BadRequest<ProblemDetails>>> |
类型安全、文档完善、表达丰富 | 稍显冗长 | 正式生产环境(推荐) |

16.4Created
Created 是 Minimal API / Microsoft.AspNetCore.Http.HttpResults 提供的一个结果类型(实现了 IResult),表示 HTTP 201 Created 响应。
Created 实际会做什么(HTTP 行为):
-
状态码:201 Created。
-
Location header:通常会在响应头中设置
Location,指向新创建资源的 URI(比如TypedResults.Created($"/api/catalog/items/{item.Id}")就是在设置这个 header)。 -
响应体:默认
Created代表 没有返回 body(仅有 Location 和 201)。如果希望同时返回资源实体(body),可以使用Created<T>(或TypedResults.Created(uri, value)),那会返回 201 + body(并设置 Content-Type: application/json)。
推荐用 Results<...> 或 IResult
| 写法 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
Task<Created> |
简洁、直观 | 不能表达错误情况 | Demo、小型项目、保证成功的操作 |
Task<IResult> |
灵活 | 文档不自动推断返回类型 | 原型开发或动态返回 |
Task<Results<Created, BadRequest<ProblemDetails>>> |
类型安全、文档清晰、生产推荐 | 稍微啰嗦 | 生产项目、公共API(推荐) |
TypedResults.Created(...) 返回一个 Created 结果(201 + Location),Minimal API 在框架层会把它写到 HTTP 响应。

16.5NoContent
在 Minimal API 中,NoContent 是一个 强类型 HTTP 响应结果,它表示:请求成功,但没有返回内容。它对应的 HTTP 状态码是 204 No Content。在 Minimal API 中常用于 DELETE 等无需返回数据的请求。
根据 HTTP 标准:204 表示请求成功处理,但服务器没有返回任何内容,响应体为空。
常用于:
DELETE操作成功后,无需返回数据;PUT或PATCH操作更新成功但无需返回资源;- 某些异步操作启动成功时。

16.6BadRequest
BadRequest<T> 是 TypedResults 提供的一种返回类型,它实现了 IResult 接口,用于生成 HTTP 400 Bad Request 响应。
核心特点:
- 1.HTTP 状态码固定为 400(请求无效);
- 2.返回体类型可自定义,通常是
ProblemDetails,也可以是任何 DTO; - 3.配合
Results<T1,T2,...>使用,可以在一个 endpoint 返回多种状态码类型; - 4.自动生成 OpenAPI 文档。

16.7NotFound
NotFound 是 TypedResults 提供的一种返回类型,实现了 IResult 接口,用于生成 HTTP 404 Not Found 响应。
核心特点:
- 1.HTTP 状态码固定为 404;
- 2.响应体可选:可以不带 body,也可以带一个对象,如
NotFound<ProblemDetails>(...); - 3.与
Results<T1,T2,...>配合使用,表示 endpoint 可能返回 404; - 4.支持 OpenAPI 自动生成文档。

16.8Results<T1, T2, ...>
Results<T1,...> 是 Minimal API 提供的“一类可能返回结果的联合类型(discriminated-union 风格)”,用来在方法签名里明确列出该 endpoint 可能返回的几种 IResult,例如 Created、BadRequest<ProblemDetails>、NotFound<ProblemDetails> 等。
Results<TResult1, TResult2, ...> 本质上是一个实现了 IResult(并且实现了 IEndpointMetadataProvider)的“联合类型”。它的设计目的是:在编译期把所有可能的返回结果列出来,让编译器和框架都能知道这个 endpoint 会返回哪些具体结果,从而:
- 在编译期限制你只能返回声明的那些结果(返回未声明的结果会报编译错)。
- 框架可以自动把这些信息加入 endpoint 元数据(对 OpenAPI/Swagger 很有帮助),因此常常可以省掉手动
[ProducesResponseType]。
如下表示只会返回三种结果之一——201 Created、400 Bad Request(ProblemDetails) 或 404 Not Found(ProblemDetails)。编译器会在编译期约束返回类型,框架会把这些信息作为 endpoint 元数据(例如用于生成 OpenAPI)。
这个接口因为返回值使用了Results<Created, BadRequest<ProblemDetails>, NotFound<ProblemDetails>>,就省略了接口上的ProducesResponseType<ProblemDetails>特性。

运行项目,通过Scalar文档可以看到该接口可能返回的类型

16.9PhysicalFileHttpResult
PhysicalFileHttpResult 是 Minimal API/TypedResults 提供的一个 “返回物理磁盘文件的结果”,用于把磁盘上的文件以流式方式发回给客户端,同时为响应自动设置合适的 HTTP 头(Content-Type、Content-Length、Last-Modified 等),并可支持条件请求与分片(Range)请求。它是框架中 IResult 的一种实现,功能与 MVC 里的 PhysicalFileResult 类似。
核心功能
| 功能 | 说明 |
|---|---|
| 读取并返回磁盘文件 | 自动将指定路径的文件内容写入 HTTP 响应流。 |
| 设置 MIME 类型 | 根据扩展名或自定义设置 Content-Type(如 image/png)。 |
| 设置文件元信息 | 可包含 Last-Modified、ETag 等 HTTP 缓存头。 |
| 支持条件请求 | 若客户端发送 If-Modified-Since,服务器可返回 304 Not Modified。 |
| 支持分段传输 | 启用 enableRangeProcessing 时可断点续传(返回 206 Partial Content)。 |
| 高性能 | 内部使用零拷贝(sendfile)方式,避免文件读入内存。 |
常用写法
return TypedResults.PhysicalFile(path, // 文件物理路径contentType, // MIME 类型,例如 "image/png"lastModified: lastModifiedTime, // 可选:设置缓存头enableRangeProcessing: true // 可选:支持分段下载
);
常用响应头
| Header | 说明 |
|---|---|
Content-Type |
文件类型,例如 image/jpeg |
Content-Length |
文件大小 |
Last-Modified |
文件最后修改时间 |
ETag |
唯一版本标识(可选) |
Accept-Ranges: bytes |
支持范围请求 |
| 响应状态码 | 200(完整)、206(分段)、304(缓存) |
Results<PhysicalFileHttpResult,NotFound>的含义是:
- 成功时返回 文件 →
PhysicalFileHttpResult(状态码 200) - 失败时返回 404 Not Found
[ProducesResponseType<byte[]>(StatusCodes.Status200OK, "application/octet-stream", [...])]
表示:这个 API 在成功时返回 200 状态码,响应体是 二进制字节流(图片),可能的图片格式包括 PNG、JPEG、GIF、BMP、WEBP 等。

16.10总结
总结返回策略
-
单一成功返回:用
Ok<T>/Created/NoContent -
多种结果返回:用
Results<T1, T2, ...> -
返回文件:用
PhysicalFileHttpResult -
错误信息标准化:用
ProblemDetails -
文档友好:用
[ProducesResponseType]标注
Minimal API 返回类型灵活,可直接在方法上定义返回类型。
对于 CRUD 操作:
- GET:
Ok<T>或Results<Ok<T>, NotFound, BadRequest> - POST:
Created - PUT:
Results<Created, BadRequest, NotFound> - DELETE:
Results<NoContent, NotFound>
分页查询 PaginatedItems<T> 封装分页信息(总数、页码、每页数量、数据列表)。
📌 创作不易,感谢支持!
每一篇内容都凝聚了心血与热情,如果我的内容对您有帮助,欢迎请我喝杯咖啡☕,您的支持是我持续分享的最大动力!
💬 加入交流群(QQ群):576434538


