.Net?MinimalApis響應(yīng)返回值的詳細(xì)過程
前言
本文主要講 MinimalApis
中的使用自定義IResultModel
和系統(tǒng)自帶IResult
做響應(yīng)返回值。MinimalApis
支持以下類型的返回值:
string
- 這包括Task<string>
和ValueTask<string>
T
(任何其他類型)- 這包括Task<T>
和ValueTask<T>
基于
IResult
- 這包括Task<IResult>
和ValueTask<IResult>
本文的完整源代碼在文末
string 返回值
行為 | Content-Type |
---|---|
框架將字符串直接寫入響應(yīng)。 | text/plain |
200
狀態(tài)代碼與text/plain
Content-Type
標(biāo)頭和以下內(nèi)容一起返回
Hello World
T(任何其他類型)返回值
我們上面說的自定義 IResultModel就是用這種模式處理的
行為 | Content-Type |
---|---|
框架 JSON 序列化響應(yīng)。 | application/json |
MinimalApis
框架 Json
序列化全局配置如下
//通過調(diào)用 ConfigureHttpJsonOptions 來全局配置應(yīng)用的選項(xiàng) builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;//忽略循環(huán)引用 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("測(cè)試類") .Produces<User>();
返回值
{ "name": "Ruipeng", "email": "xxx@163.com", "age": 18 }
200
狀態(tài)代碼與 application/json
Content-Type
標(biāo)頭和以下內(nèi)容一起返回
這個(gè) HttpCode
狀態(tài)碼只能返回 200
,且不支持多種返回形式,比較局限
統(tǒng)一響應(yīng)格式代碼
public interface IResultModel { /// <summary> /// 是否成功 /// </summary> bool? IsSuccess { get; } /// <summary> /// 錯(cuò)誤信息 /// </summary> string? Message { get; } /// <summary> /// 業(yè)務(wù)碼,用于業(yè)務(wù)中自定義 /// </summary> int? StatusCode { get; set; } /// <summary> /// 時(shí)間戳 /// </summary> long? Timestamp { get; } } /// <summary> /// 返回結(jié)果模型泛型接口 /// </summary> /// <typeparam name="T"></typeparam> public interface IResultModel<out T> : IResultModel { /// <summary> /// 返回?cái)?shù)據(jù) /// </summary> T? Data { get; } }
實(shí)現(xiàn)
public class ResultModel<T> : IResultModel<T> { public ResultModel() { Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); } /// <summary> /// 處理是否成功 /// </summary> public bool? IsSuccess { get; set; } /// <summary> /// 錯(cuò)誤信息 /// </summary> public string? Message { get; set; } /// <summary> /// 業(yè)務(wù)碼 /// </summary> public int? StatusCode { get; set; } /// <summary> /// 時(shí)間戳 /// </summary> public long? Timestamp { get; } /// <summary> /// 返回?cái)?shù)據(jù) /// </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> /// 返回結(jié)果 /// </summary> public static class ResultModel { /// <summary> /// 數(shù)據(jù)已存在 /// </summary> /// <returns></returns> public static IResultModel<string> HasExists => Failed("data already exists"); /// <summary> /// 數(shù)據(jù)不存在 /// </summary> public static IResultModel<string> NotExists => Failed("data doesn't exist"); /// <summary> /// 成功 /// </summary> /// <param name="data">返回?cái)?shù)據(jù)</param> /// <returns></returns> public static IResultModel<T> Success<T>(T? data = default) { return new ResultModel<T>().Success(data); } /// <summary> /// 成功 /// </summary> /// <param name="task">任務(wù)</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">錯(cuò)誤信息</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> /// 根據(jù)布爾值返回結(jié)果 /// </summary> /// <param name="success"></param> /// <returns></returns> public static IResultModel<T> Result<T>(bool success) { return success ? Success<T>() : Failed<T>(); } /// <summary> /// 根據(jù)布爾值返回結(jié)果 /// </summary> /// <param name="success"></param> /// <returns></returns> public static async Task<IResultModel> Result(Task<bool> success) { return await success ? Success() : Failed(); } /// <summary> /// 根據(jù)布爾值返回結(jié)果 /// </summary> /// <param name="success"></param> /// <returns></returns> public static IResultModel<string> Result(bool success) { return success ? Success() : Failed(); } /// <summary> /// 時(shí)間戳起始日期 /// </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("測(cè)試自定義IResultModel") .Produces<IResultModel<User>>();
封裝了一個(gè)靜態(tài)類來簡(jiǎn)化自定義類的創(chuàng)建,支持多個(gè)返回類型
返回值
{ "isSuccess": true, "statusCode": 200, "timestamp": 1711001093, "data": { "name": "Ruipeng", "email": "xxx@163.com", "age": 18 }
自定義類的自動(dòng)包裝實(shí)現(xiàn)
創(chuàng)建一個(gè)Attribute
[AttributeUsage(AttributeTargets.Method)] public class EnableResponseWrapperAttribute : Attribute { }
創(chuàng)建中間件自動(dòng)包裝
public class ResponseWrapperMiddleware(RequestDelegate next) { public async Task InvokeAsync(HttpContext context) { if (context.GetEndpoint()?.Metadata.GetMetadata<EnableResponseWrapperAttribute>() is not null) { // 存儲(chǔ)原始響應(yīng)體流 var originalResponseBodyStream = context.Response.Body; try { // 創(chuàng)建內(nèi)存流以捕獲響應(yīng) using var memoryStream = new MemoryStream(); context.Response.Body = memoryStream; // 調(diào)用管道中的下一個(gè)中間件 await next(context); // 恢復(fù)原始響應(yīng)體流并寫入格式化結(jié)果 context.Response.Body = originalResponseBodyStream; // 重置內(nèi)存流位置并讀取響應(yīng)內(nèi)容 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 { // 確保在出現(xiàn)異常時(shí)恢復(fù)原始響應(yīng)體流 context.Response.Body = originalResponseBodyStream; } } else { await next(context); } } }
應(yīng)用中間件
app.UseMiddleware<ResponseWrapperMiddleware>();
創(chuàng)建測(cè)試接口
app.MapGet("/TestTestAutoWarpper", [EnableResponseWrapper] User () => new User() { Name = "Ruipeng", Email = "xxx@163.com", Age = 18 }).WithSummary("測(cè)試類") .Produces<User>();
返回值
{ "isSuccess": true, "statusCode": 200, "timestamp": 1711005201, "data": { "name": "Ruipeng", "email": "xxx@163.com", "age": 18 } }
為了方便測(cè)試在MinimalApis
的接口上如果添加了EnableResponseWrapperAttribute
則通過中間件自動(dòng)包裝返回值
IResult 返回值
行為 | Content-Type |
---|---|
框架調(diào)用 IResult.ExecuteAsync | 由 IResult 實(shí)現(xiàn)決定 |
在dotNet7
之后多了一個(gè)TypedResults
類來替代 Results
。IResult
接口定義一個(gè)表示 HTTP
終結(jié)點(diǎn)結(jié)果的協(xié)定。 靜態(tài) Results
類和靜態(tài) TypedResults
用于創(chuàng)建表示不同類型的響應(yīng)的各種 IResult
對(duì)象。
返回 TypedResults(而不是 Results)有以下優(yōu)點(diǎn):
TypedResults
幫助程序返回強(qiáng)類型對(duì)象,這可以提高代碼可讀性、改進(jìn)單元測(cè)試并減少運(yùn)行時(shí)錯(cuò)誤的可能性。- 實(shí)現(xiàn)類型會(huì)自動(dòng)為
OpenAPI
提供響應(yīng)類型元數(shù)據(jù)來描述終結(jié)點(diǎn)。
實(shí)現(xiàn)在Microsoft.AspNetCore.Http.HttpResults
//Return IResult app.MapGet("/IResult/TestResult", IResult () => Results.Ok(new User() { Name = "Ruipeng", Email = "xxx@163.com", Age = 18 }));
沒有調(diào)用擴(kuò)展方法 Produces
app.MapGet("/IResult/TestTypedResult", IResult () => TypedResults.Ok(new User() { Name = "Ruipeng", Email = "xxx@163.com", Age = 18 }));
可以看到 TypedResults 默認(rèn)就會(huì)添加路由終結(jié)點(diǎn)的元數(shù)據(jù)描述
返回多個(gè) IResult 實(shí)現(xiàn)類型
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(); });
圖簡(jiǎn)單可以直接用 IResult
返回類型 但是,由于 TypedResults
幫助程序自動(dòng)包含終結(jié)點(diǎn)的元數(shù)據(jù),因此可以改為返回 Results<Ok<User>, NotFound>
聯(lián)合類型
IResult 自定義響應(yīng)
添加 Html 擴(kuò)展
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>"));
返回結(jié)果
<!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
方法來配置全局請(qǐng)求的 Json 格式,下面則是針對(duì)單個(gè)路由終結(jié)點(diǎn)請(qǐng)求,方便一些個(gè)性化接口的處理
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 = "內(nèi)部錯(cuò)誤" }; return TypedResults.Problem(problemDetail); });
返回值
{ "type": "https://tools.ietf.org/html/rfc9110#section-15.6.1", "title": "內(nèi)部錯(cuò)誤", "status": 500 }
在Microsoft.AspNetCore.Http.Results
的擴(kuò)展下,TypedResults 有非常多擴(kuò)展的方法,比如處理文件,回調(diào),流以及登錄認(rèn)證等,大家可以根據(jù)需求使用.
最后
用那種方式還是取決于項(xiàng)目的實(shí)際情況,如果你的系統(tǒng)是業(yè)務(wù)碼
和 httpStateCode
要求分離的形式那建議用上面自定義統(tǒng)一響應(yīng)的形式,要是沒這方面的需求那dotNet
自帶的TypedResults
使用起來就更合適。
到此這篇關(guān)于.Net MinimalApis響應(yīng)返回值的文章就介紹到這了,更多相關(guān).Net MinimalApis響應(yīng)返回值內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- asp.net實(shí)現(xiàn)調(diào)用存儲(chǔ)過程并帶返回值的方法
- asp.net操作javascript:confirm返回值的兩種方式
- .Net 調(diào)用存儲(chǔ)過程取到return的返回值
- ASP.NET―001:GridView綁定List、頁面返回值具體實(shí)現(xiàn)
- asp.net彈出窗口 返回值
- asp.net EXECUTENONQUERY()返回值介紹
- asp.net利用Ajax和Jquery在前臺(tái)向后臺(tái)傳參數(shù)并返回值的實(shí)例
- ASP.NET 獲取存儲(chǔ)過程返回值的實(shí)現(xiàn)代碼
相關(guān)文章
在ASP.Net中實(shí)現(xiàn)flv視頻轉(zhuǎn)換的代碼
在ASP.Net中實(shí)現(xiàn)flv視頻轉(zhuǎn)換的代碼...2007-09-09如何在ASP.NET Core中使用Session的示例代碼
這篇文章主要介紹了如何在ASP.NET Core中使用Session的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01http轉(zhuǎn)https的實(shí)戰(zhàn)記錄(iis 7.5)
這篇文章主要給大家介紹了關(guān)于http轉(zhuǎn)https的相關(guān)資料,文中是最近的一次實(shí)戰(zhàn)記錄,基于iis7.5,通過一步步的圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2018-01-01ASP.NET下上傳圖片到數(shù)據(jù)庫,并且讀出圖片的代碼(詳細(xì)版)
上傳圖片到數(shù)據(jù)庫,從數(shù)據(jù)庫的創(chuàng)建到數(shù)據(jù)庫中圖片的現(xiàn)實(shí)都給出了具體的代碼,因?yàn)閍sp.net版本的問題,大家可能需要稍微修改下。2010-07-07Linux下部署.net core環(huán)境的步驟詳解
這篇文章主要給大家介紹了在Linux下部署.net core環(huán)境的步驟,文中給出了詳細(xì)的介紹,相信對(duì)大家的學(xué)習(xí)或者工作具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。2017-04-04關(guān)于.net環(huán)境下跨進(jìn)程、高頻率讀寫數(shù)據(jù)的問題
最近老大教給我一個(gè)項(xiàng)目,項(xiàng)目要求高頻次地讀寫數(shù)據(jù),數(shù)據(jù)量也不是很大,難點(diǎn)在于這個(gè)規(guī)模的熱點(diǎn)數(shù)據(jù),變化非常頻繁,下面把我的處理方法分享到腳本之家平臺(tái),對(duì).net跨進(jìn)程高頻率讀寫數(shù)據(jù)相關(guān)知識(shí)感興趣的朋友跟隨小編一起學(xué)習(xí)下吧2021-05-05ASP.NET導(dǎo)出Excel打開時(shí)提示:與文件擴(kuò)展名指定文件不一致解決方法
ASP.NET導(dǎo)出Excel,打開時(shí)提示“您嘗試打開文件'XXX.xls'的格式與文件擴(kuò)展名指定文件不一致” 很是郁悶,于是搜集了一些解決方法,感興趣的朋友可以了解下2013-01-01