TypedResults 是 ASP.NET Core 7+ 的强类型 HTTP 响应机制,通过静态工厂方法(如 TypedResults.Ok<T>())在编译期校验响应体类型与状态码契约,替代弱类型的 ActionResult<T> 和 Ok(),要求控制器返回 IResult 并统一使用 TypedResults 命名空间下的泛型方法。

TypedResults 是 ASP.NET Core 7+ 引入的强类型 HTTP 响应封装机制,它不是泛型返回值抽象,而是专为 Web API 控制器设计的一组静态工厂方法,用于替代 ActionResult<T> 或裸 Ok(T) 等弱类型写法。它本身不改变 C# 的类型系统,但能让你在编译期就捕获更多路由/状态码与返回体不匹配的问题。
为什么 TypedResults.Ok() 比 Ok() 更安全
直接调用 Ok(data) 返回的是 ActionResult,编译器无法校验 data 类型是否与 OpenAPI 文档或客户端预期一致;而 TypedResults.Ok<User>() 明确声明了成功响应体必须是 User 类型,且该类型会参与 Swagger 生成和模型验证。
-
TypedResults.Ok<User>(user)→ 编译器强制user是User或其子类,且生成的 OpenAPI schema 中200的content类型被锁定为User -
Ok(user)→ 返回ActionResult,user可以是任意类型,OpenAPI 默认 fallback 到object,丢失契约信息 - 控制器方法签名也需同步改用
IResult:public IResult GetUser(int id) { ... },否则TypedResults的类型信息会被擦除
TypedResults 对 return 语句的影响
使用 TypedResults 后,方法不能再用 return new OkObjectResult(user) 这类手动构造的响应类型,否则会破坏类型推导链。所有分支必须统一返回 IResult 实例。
- 错误写法:
if (id == 0) return Results.NotFound(); else return TypedResults.Ok(user);——Results.NotFound()和TypedResults.Ok()都是IResult,但混用不同命名空间的工厂方法会让团队维护混乱,且Results不带泛型约束 - 推荐写法:全部使用
TypedResults,例如TypedResults.NotFound<string>()、TypedResults.BadRequest<ErrorDetail>(),确保每个状态码都绑定明确的响应体类型 - 注意:没有
TypedResults.Unauthorized<T>()这种重载,Unauthorized()本身不携带响应体,所以无需泛型参数;强行加会导致编译失败
和 OneOf<T1, T2> 混用时的常见陷阱
TypedResults 本身不解决“一个接口返回多种成功体”的问题(比如搜索接口可能返回 User 或 Company)。这时有人会想组合 OneOf<User, Company> + TypedResults.Ok<OneOf<User, Company>>(),但这是错的。
-
OneOf<User, Company>是业务逻辑层的可区分联合,适合做服务返回值;但 HTTP 层需要明确的、单一的响应结构,Swagger 无法为OneOf生成正确的多态 schema - 正确做法是分拆成多个端点,或用标准 DTO 封装差异,例如
SearchResult<User>和SearchResult<Company>共享基类,再用TypedResults.Ok<SearchResult<User>>() - 若坚持单端点多形态,应放弃
TypedResults的泛型约束,退回到IResult手动构造,或用JsonResult+ 自定义序列化器
真正难处理的不是怎么写 TypedResults.Ok(),而是当你的领域模型天然存在歧义路径(如空结果、部分失败、降级响应)时,TypedResults 的强契约反而会迫使你提前做决策——要么拆接口,要么收拢响应结构。这恰恰是它最有价值的地方:把模糊的运行时约定,变成编译期可检查的接口契约。


















