.NET 7 中的 HttpResult 接口
Intro
在前面的文章中,我们提到了 .NET 7 引入了 Endpoint Filter 来支持 Endpoint 的过滤器,有了这个接口就想着把之前的统一 API response 的 filter 改造一下支持 endpoint filter,然而这个一直等到了 .NET 7 Preview 7 才得以实现,在 .NET 7 Preview 7 中引入了一些接口使得我们可以匹配 HttpResult
的类型
HttpResult Interface
.NET 6 开始引入了 Minimal API, 但是要匹配一系列 IResult 相关的 response 比如 Results.Ok, Results.NotFound 会比较困难,在 .NET 7 有了一些改进
在 .NET 7 Preview 3 中,出于方便单元测试的考虑,开放了一些 HttpResult
的类型比如:OkObjectHttpResult
/NotFoundHttpResult
等,但是始终没有一个类似于 MVC 里的 ObjectResult
一样的类型,使得我们如果想要匹配 response 的类型就会很麻烦,终于在 .NET 7 Preview 7,引入了一系列的接口,我们可以通过这些接口进行模式匹配来获取这些 HttpResult
中的 Value
/StatusCode
等等,新增加的接口如下:
Microsoft.AspNetCore.Http.IContentTypeHttpResult
Microsoft.AspNetCore.Http.IFileHttpResult
Microsoft.AspNetCore.Http.INestedHttpResult
Microsoft.AspNetCore.Http.IStatusCodeHttpResult
Microsoft.AspNetCore.Http.IValueHttpResult
Microsoft.AspNetCore.Http.IValueHttpResult<TValue>
可以参考 PR:https://github.com/dotnet/aspnetcore/pull/42385/files
如果我们想要匹配 response 的返回值就可以使用 IValueHttpResult
来匹配,比如:
if (result is IValueHttpResult valueHttpResult)
return valueHttpResult.Value;
也可以使用 IStatusCodeHttpResult
来匹配 response status
if (result is IStatusCodeHttpResult statusCodeResult)
return statusCodeResult.StatusCode;
ApiResultFilter
实现了一个简单的统一 response 的 ApiResultFilter
,在原来的基础上增加了 EndpointFilter
的支持,实现如下:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ApiResultFilter : Attribute
, IResultFilter, IExceptionFilter
#if NET7_0_OR_GREATER
, IEndpointFilter
#endif
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is ObjectResult { Value: not Result } objectResult)
{
var result = new Result<object>()
{
Data = objectResult.Value,
Status = HttpStatusCode2ResultStatus(objectResult.StatusCode)
};
objectResult.Value = result;
}
}
public void OnResultExecuted(ResultExecutedContext context)
{
}
public void OnException(ExceptionContext context)
{
var result = Result.Fail(context.Exception.ToString(), ResultStatus.ProcessFail);
context.Result = new ObjectResult(result) { StatusCode = 500 };
}
#if NET7_0_OR_GREATER
public async ValueTask<object> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
try
{
var result = await next(context);
if (result is Result or ObjectResult { Value: Result } or IValueHttpResult { Value: Result })
{
return result;
}
if (result is ObjectResult { Value: not Result } objectResult)
{
return new Result<object>()
{
Data = objectResult.Value, Status = HttpStatusCode2ResultStatus(objectResult.StatusCode)
};
}
if (result is IValueHttpResult { Value: not Result } valueHttpResult)
{
var status = result is IStatusCodeHttpResult statusCodeHttpResult
? HttpStatusCode2ResultStatus(statusCodeHttpResult.StatusCode)
: HttpStatusCode2ResultStatus(200);
return new Result<object>() { Data = valueHttpResult.Value, Status = status };
}
return new Result<object>()
{
Data = result, Status = HttpStatusCode2ResultStatus(context.HttpContext.Response.StatusCode)
};
}
catch (Exception ex)
{
return Result.Fail(ex.ToString(), ResultStatus.ProcessFail);
}
}
#endif
private static ResultStatus HttpStatusCode2ResultStatus(int? statusCode)
{
statusCode ??= 200;
var status = ResultStatus.Success;
if (Enum.IsDefined(typeof(ResultStatus), statusCode.Value))
{
status = (ResultStatus)statusCode.Value;
}
if (status == ResultStatus.None)
{
status = ResultStatus.Success;
}
return status;
}
}
下面看一个使用的示例:
var app = WebApplication.Create(args);
app.Map("/Hello", () => "Hello Minimal API!")
.AddEndpointFilter<ApiResultFilter>();
app.Map("/HelloV3", () => Results.Ok(new { Name = "test" }))
.AddEndpointFilter<ApiResultFilter>();
app.Map("/HelloV4", () => Results.Ok(Result.Success(new { Name = "test" })))
.AddEndpointFilter<ApiResultFilter>();
await app.RunAsync();
访问一个直接返回一个字符串的接口:
访问返回一个 IResult
的接口
访问返回一个 ResultModel
的 API
使用控制器 API 示例:
[Route("api/[controller]")]
public class ValuesController: ControllerBase
{
[HttpGet("[action]")]
public IActionResult Test()
{
return Ok(new { Name = "Amazing .NET" });
}
}
API response 示例:
References
-
https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-7-preview-7/#new-httpresults-interfaces -
https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-7-preview-3/#improved-unit-testability-for-minimal-route-handlers -
https://github.com/dotnet/aspnetcore/issues/41470 -
https://github.com/dotnet/aspnetcore/issues/42187 -
https://github.com/dotnet/aspnetcore/pull/42385/files -
https://github.com/WeihanLi/WeihanLi.Web.Extensions/tree/dev/samples/WeihanLi.Web.Extensions.Samples
原文始发于微信公众号(amazingdotnet):.NET 7 中的 HttpResult 接口
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/59675.html