.Net MinimalApis响应返回值的详细过程

2024-04-16 0 547
目录
  • 前言
  • string返回值
  • T(任何其他类型)返回值
    • 返回 T
    • 统一响应格式代码
    • 实现
    • 自定义类的自动包装实现
  • IResult 返回值
    • 返回多个 IResult 实现类型
    • IResult 自定义响应
    • 自定义 Json 格式
    • 返回 ProblemDetail
  • 最后

    前言

    本文主要讲MinimalApis中的使用自定义IResultModel和系统自带IResult做响应返回值。MinimalApis支持以下类型的返回值:

    • string- 这包括Task<string>和ValueTask<string>

    • T(任何其他类型)- 这包括Task<T>和ValueTask<T>

    • 基于IResult- 这包括Task<IResult>和ValueTask<IResult>

      本文的完整源代码在文末

    string返回值

    行为Content-Type框架将字符串直接写入响应。text/plain

    .Net MinimalApis响应返回值的详细过程

    200状态代码与text/plainContent-Type标头和以下内容一起返回

    Hello World

    T(任何其他类型)返回值

    我们上面说的自定义 IResultModel就是用这种模式处理的

    行为Content-Type框架 JSON 序列化响应。application/json

    MinimalApis框架Json序列化全局配置如下

    //通过调用 ConfigureHttpJsonOptions 来全局配置应用的选项
    builder.Services.ConfigureHttpJsonOptions(options =>
    {
    options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;//忽略循环引用
    options.SerializerOptions.WriteIndented = true;
    options.SerializerOptions.IncludeFields = true;
    options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    });

    返回 T

    app.MapGet(\”/TestT\”, User () => new User() { Name = \”Ruipeng\”, Email = \”xxx@163.com\”, Age = 18 })
    .WithSummary(\”测试类\”)
    .Produces<User>();

    返回值

    {
    \”name\”: \”Ruipeng\”,
    \”email\”: \”xxx@163.com\”,
    \”age\”: 18
    }

    200状态代码与application/jsonContent-Type标头和以下内容一起返回

    这个HttpCode状态码只能返回200,且不支持多种返回形式,比较局限

    统一响应格式代码

    public interface IResultModel
    {
    /// <summary>
    /// 是否成功
    /// </summary>
    bool? IsSuccess { get; }
    /// <summary>
    /// 错误信息
    /// </summary>
    string? Message { get; }
    /// <summary>
    /// 业务码,用于业务中自定义
    /// </summary>
    int? StatusCode { get; set; }
    /// <summary>
    /// 时间戳
    /// </summary>
    long? Timestamp { get; }
    }
    /// <summary>
    /// 返回结果模型泛型接口
    /// </summary>
    /// <typeparam name=\”T\”></typeparam>
    public interface IResultModel<out T> : IResultModel
    {
    /// <summary>
    /// 返回数据
    /// </summary>
    T? Data { get; }
    }

    实现

    public class ResultModel<T> : IResultModel<T>
    {
    public ResultModel()
    {
    Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
    }
    /// <summary>
    /// 处理是否成功
    /// </summary>
    public bool? IsSuccess { get; set; }
    /// <summary>
    /// 错误信息
    /// </summary>
    public string? Message { get; set; }
    /// <summary>
    /// 业务码
    /// </summary>
    public int? StatusCode { get; set; }
    /// <summary>
    /// 时间戳
    /// </summary>
    public long? Timestamp { get; }
    /// <summary>
    /// 返回数据
    /// </summary>
    public T? Data { get; set; }
    /// <summary>
    /// 成功
    /// </summary>
    /// <param name=\”Data\”></param>
    public ResultModel<T> Success(T? data = default)
    {
    this.IsSuccess = true;
    StatusCode = 200;
    Data = data;
    return this;
    }
    /// <summary>
    /// 失败
    /// </summary>
    /// <param name=\”msg\”>说明</param>
    /// <param name=\”code\”></param>
    public ResultModel<T> Failed(string? msg = \”failed\”, int? code = 500)
    {
    IsSuccess = false;
    Message = msg;
    StatusCode = code;
    return this;
    }
    }
    /// <summary>
    /// 返回结果
    /// </summary>
    public static class ResultModel
    {
    /// <summary>
    /// 数据已存在
    /// </summary>
    /// <returns></returns>
    public static IResultModel<string> HasExists => Failed(\”data already exists\”);
    /// <summary>
    /// 数据不存在
    /// </summary>
    public static IResultModel<string> NotExists => Failed(\”data doesn\’t exist\”);
    /// <summary>
    /// 成功
    /// </summary>
    /// <param name=\”data\”>返回数据</param>
    /// <returns></returns>
    public static IResultModel<T> Success<T>(T? data = default)
    {
    return new ResultModel<T>().Success(data);
    }
    /// <summary>
    /// 成功
    /// </summary>
    /// <param name=\”task\”>任务</param>
    /// <returns></returns>
    public static async Task<IResultModel<T>> SuccessAsync<T>(Task<T>? task = default)
    {
    return task is not null && task != default ? new ResultModel<T>().Success(await task) : new ResultModel<T>();
    }
    /// <summary>
    /// 成功
    /// </summary>
    /// <returns></returns>
    public static IResultModel<string> Success()
    {
    return Success<string>();
    }
    /// <summary>
    /// 失败
    /// </summary>
    /// <param name=\”error\”>错误信息</param>
    /// <returns></returns>
    public static IResultModel<T> Failed<T>(string? error = null)
    {
    return new ResultModel<T>().Failed(error ?? \”failed\”);
    }
    /// <summary>
    /// 失败
    /// </summary>
    /// <returns></returns>
    public static IResultModel<string> Failed(string? error = null)
    {
    return Failed<string>(error);
    }
    /// <summary>
    /// 根据布尔值返回结果
    /// </summary>
    /// <param name=\”success\”></param>
    /// <returns></returns>
    public static IResultModel<T> Result<T>(bool success)
    {
    return success ? Success<T>() : Failed<T>();
    }
    /// <summary>
    /// 根据布尔值返回结果
    /// </summary>
    /// <param name=\”success\”></param>
    /// <returns></returns>
    public static async Task<IResultModel> Result(Task<bool> success)
    {
    return await success ? Success() : Failed();
    }
    /// <summary>
    /// 根据布尔值返回结果
    /// </summary>
    /// <param name=\”success\”></param>
    /// <returns></returns>
    public static IResultModel<string> Result(bool success)
    {
    return success ? Success() : Failed();
    }
    /// <summary>
    /// 时间戳起始日期
    /// </summary>
    public static readonly DateTime TimestampStart = new(1970, 1, 1, 0, 0, 0, 0);
    }

    定义接口

    app.MapGet(\”/TestResultModel\”, IResultModel (int age) =>
    {
    List<User> users = [new User() { Name = \”Ruipeng\”, Email = \”xxx@163.com\”, Age = 18 }];
    return users.FirstOrDefault(_ => _.Age > age) is User user ? ResultModel.Success(user) : ResultModel.Failed();
    })
    .WithSummary(\”测试自定义IResultModel\”)
    .Produces<IResultModel<User>>();

    封装了一个静态类来简化自定义类的创建,支持多个返回类型

    返回值

    {
    \”isSuccess\”: true,
    \”statusCode\”: 200,
    \”timestamp\”: 1711001093,
    \”data\”: {
    \”name\”: \”Ruipeng\”,
    \”email\”: \”xxx@163.com\”,
    \”age\”: 18
    }

    自定义类的自动包装实现

    创建一个Attribute

    [AttributeUsage(AttributeTargets.Method)]
    public class EnableResponseWrapperAttribute : Attribute { }

    创建中间件自动包装

    public class ResponseWrapperMiddleware(RequestDelegate next)
    {
    public async Task InvokeAsync(HttpContext context)
    {
    if (context.GetEndpoint()?.Metadata.GetMetadata<EnableResponseWrapperAttribute>() is not null)
    {
    // 存储原始响应体流
    var originalResponseBodyStream = context.Response.Body;
    try
    {
    // 创建内存流以捕获响应
    using var memoryStream = new MemoryStream();
    context.Response.Body = memoryStream;
    // 调用管道中的下一个中间件
    await next(context);
    // 恢复原始响应体流并写入格式化结果
    context.Response.Body = originalResponseBodyStream;
    // 重置内存流位置并读取响应内容
    memoryStream.Seek(0, SeekOrigin.Begin);
    var readToEnd = await new StreamReader(memoryStream).ReadToEndAsync();
    var objResult = JsonSerializer.Deserialize<dynamic>(readToEnd);
    var result = new ResultModel<object>
    {
    Data = objResult,
    IsSuccess = true,
    StatusCode = context.Response.StatusCode
    };
    await context.Response.WriteAsJsonAsync(result as object);
    }
    finally
    {
    // 确保在出现异常时恢复原始响应体流
    context.Response.Body = originalResponseBodyStream;
    }
    }
    else
    {
    await next(context);
    }
    }
    }

    应用中间件

    app.UseMiddleware<ResponseWrapperMiddleware>();

    创建测试接口

    app.MapGet(\”/TestTestAutoWarpper\”, [EnableResponseWrapper] User () => new User() { Name = \”Ruipeng\”, Email = \”xxx@163.com\”, Age = 18 }).WithSummary(\”测试类\”)
    .Produces<User>();

    返回值

    {
    \”isSuccess\”: true,
    \”statusCode\”: 200,
    \”timestamp\”: 1711005201,
    \”data\”: {
    \”name\”: \”Ruipeng\”,
    \”email\”: \”xxx@163.com\”,
    \”age\”: 18
    }
    }

    为了方便测试在MinimalApis的接口上如果添加了EnableResponseWrapperAttribute则通过中间件自动包装返回值

    IResult 返回值

    行为Content-Type框架调用 IResult.ExecuteAsync由 IResult 实现决定

    在dotNet7之后多了一个TypedResults类来替代Results。IResult接口定义一个表示HTTP终结点结果的协定。 静态Results类和静态TypedResults用于创建表示不同类型的响应的各种IResult对象。

    返回 TypedResults(而不是 Results)有以下优点:

    • TypedResults帮助程序返回强类型对象,这可以提高代码可读性、改进单元测试并减少运行时错误的可能性。
    • 实现类型会自动为OpenAPI提供响应类型元数据来描述终结点。实现在Microsoft.AspNetCore.Http.HttpResults

    //Return IResult
    app.MapGet(\”/IResult/TestResult\”, IResult () => Results.Ok(new User() { Name = \”Ruipeng\”, Email = \”xxx@163.com\”, Age = 18 }));

    没有调用扩展方法Produces

    .Net MinimalApis响应返回值的详细过程

    app.MapGet(\”/IResult/TestTypedResult\”, IResult () => TypedResults.Ok(new User() { Name = \”Ruipeng\”, Email = \”xxx@163.com\”, Age = 18 }));

    .Net MinimalApis响应返回值的详细过程

    可以看到 TypedResults 默认就会添加路由终结点的元数据描述

    返回多个 IResult 实现类型

    app.MapGet(\”/IResult/ReturnMultipleTypes\”, Results<Ok<User>, NotFound> (int age) =>
    {
    List<User> users = [new User() { Name = \”Ruipeng\”, Email = \”xxx@163.com\”, Age = 18 }];
    return users.FirstOrDefault(_ => _.Age > age) is User user ? TypedResults.Ok(user) : TypedResults.NotFound();
    });

    图简单可以直接用IResult返回类型 但是,由于TypedResults帮助程序自动包含终结点的元数据,因此可以改为返回Results<Ok<User>, NotFound>联合类型

    IResult 自定义响应

    添加 Html 扩展

    public static class ResultsExtensions
    {
    public static IResult Html(this IResultExtensions resultExtensions, string html)
    {
    ArgumentNullException.ThrowIfNull(resultExtensions);
    return new HtmlResult(html);
    }
    }
    class HtmlResult(string html) : IResult
    {
    private readonly string _html = html;
    public Task ExecuteAsync(HttpContext httpContext)
    {
    httpContext.Response.ContentType = MediaTypeNames.Text.Html;
    httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_html);
    return httpContext.Response.WriteAsync(_html);
    }
    }
    app.MapGet(\”/IResult/Html\”, () => Results.Extensions.Html(@$\”<!doctype html>
    <html>
    <head><title>miniHTML</title></head>
    <body>
    <h1>Hello World</h1>
    <p>The time on the server is {DateTime.Now:O}</p>
    </body>
    </html>\”));

    返回结果

    <!DOCTYPE html>
    <html>
    <head>
    <title>miniHTML</title>
    </head>
    <body>
    <h1>Hello World</h1>
    <p>The time on the server is 2024-03-21T17:31:36.2931959+08:00</p>
    </body>
    </html>

    自定义 Json 格式

    上面写了ConfigureHttpJsonOptions方法来配置全局请求的 Json 格式,下面则是针对单个路由终结点请求,方便一些个性化接口的处理

    var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
    { WriteIndented = true };
    app.MapGet(\”/IResult/CustomJsonConfig\”, () =>
    TypedResults.Json(new User() { Name = \”Ruipeng\”, Email = \”xxx@163.com\”, Age = 18 }, options));

    返回 ProblemDetail

    app.MapGet(\”/IResult/ProblemDetail\”, () =>
    {
    var problemDetail = new ProblemDetails()
    {
    Status = StatusCodes.Status500InternalServerError,
    Title = \”内部错误\”
    };
    return TypedResults.Problem(problemDetail);
    });

    返回值

    {
    \”type\”: \”https://tools.ietf.org/html/rfc9110#section-15.6.1\”,
    \”title\”: \”内部错误\”,
    \”status\”: 500
    }

    在Microsoft.AspNetCore.Http.Results的扩展下,TypedResults 有非常多扩展的方法,比如处理文件,回调,流以及登录认证等,大家可以根据需求使用.

    .Net MinimalApis响应返回值的详细过程

    最后

    用那种方式还是取决于项目的实际情况,如果你的系统是业务码和httpStateCode要求分离的形式那建议用上面自定义统一响应的形式,要是没这方面的需求那dotNet自带的TypedResults使用起来就更合适。

    到此这篇关于.NetMinimalApis响应返回值的文章就介绍到这了,更多相关.NetMinimalApis响应返回值内容请搜索悠久资源网以前的文章或继续浏览下面的相关文章希望大家以后多多支持悠久资源网!

    您可能感兴趣的文章:

    • asp.net实现调用存储过程并带返回值的方法
    • asp.net操作javascript:confirm返回值的两种方式
    • .Net 调用存储过程取到return的返回值
    • ASP.NET―001:GridView绑定List、页面返回值具体实现
    • asp.net弹出窗口 返回值
    • asp.net EXECUTENONQUERY()返回值介绍
    • asp.net利用Ajax和Jquery在前台向后台传参数并返回值的实例
    • ASP.NET 获取存储过程返回值的实现代码

    收藏 (0) 打赏

    感谢您的支持,我会继续努力的!

    打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
    点赞 (0)

    悠久资源 ASP.NET .Net MinimalApis响应返回值的详细过程 https://www.u-9.cn/biancheng/aspnet/186064.html

    常见问题

    相关文章

    发表评论
    暂无评论
    官方客服团队

    为您解决烦忧 - 24小时在线 专业服务