.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/plainContent-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)文章
.Net RabbitMQ實(shí)現(xiàn)HTTP API接口調(diào)用
RabbitMQ Management插件還提供了基于RESTful風(fēng)格的HTTP API接口來方便調(diào)用。本文就主要介紹了.Net RabbitMQ實(shí)現(xiàn)HTTP API接口調(diào)用,感興趣的可以了解一下2021-06-06
asp.net中上傳圖片文件實(shí)現(xiàn)防偽圖片水印并寫入數(shù)據(jù)庫
asp.net上傳圖片文件實(shí)現(xiàn)防偽圖片水印并寫入數(shù)據(jù)庫,需要的朋友可以參考下。2010-10-10
SqlDataReader指定轉(zhuǎn)換無效的解決方法
這篇文章主要為大家詳細(xì)介紹了SqlDataReader指定轉(zhuǎn)換無效的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
ASP.net實(shí)現(xiàn)頁面跳轉(zhuǎn)的方法
在ASP.NET進(jìn)行頁面開發(fā)時(shí)候經(jīng)常遇到需要進(jìn)行頁面跳轉(zhuǎn)的操作。這個(gè)其實(shí)并不難,關(guān)鍵是知不知道的問題。下面給出操作方法。2013-06-06
ASP.Net執(zhí)行cmd命令的實(shí)現(xiàn)代碼
ASP.Net執(zhí)行cmd命令的實(shí)現(xiàn)代碼,需要的朋友可以參考下。2011-02-02
使用EF的Code?First模式操作數(shù)據(jù)庫
這篇文章介紹了使用EF的Code?First模式操作數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03

