.NET 7 中的 HttpResult 接口

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.NET 7 中的 HttpResult 接口相关的知识,希望对你有一定的参考价值。

.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

以上是关于.NET 7 中的 HttpResult 接口的主要内容,如果未能解决你的问题,请参考以下文章

如何在ColdFusion中访问包含特殊字符的变量名称

.NET 7 新增的 IParsable 接口介绍

.NET 中的接口分解/接口设计

centos 7 根据mac地址设置网络接口名

VB.NET接口中的读写属性

vb.net:接口中的多重继承