自定義 ocelot 中間件輸出自定義錯誤信息
Intro
ocelot 中默認的 Response 中間件在出錯的時候只會設置 StatusCode 沒有具體的信息,想要展示自己定義的錯誤信息的時候就需要做一些自定義了,對 ocelot 中的 Response 中間件做了一些小改動,實現了輸出自定義錯誤信息的功能。
Implement
實現起來其實也很簡單,原來的有錯誤的時候,只設置了 Response 的 StatusCode,我們只需要加一下輸出錯誤信息就可以了,錯誤信息的格式完全可以自定義,實現代碼如下:
public class CustomResponseMiddleware : Ocelot.Middleware.OcelotMiddleware
{
private readonly RequestDelegate _next;
private readonly IHttpResponder _responder;
private readonly IErrorsToHttpStatusCodeMapper _codeMapper;
public CustomResponseMiddleware(
RequestDelegate next,
IHttpResponder responder,
IErrorsToHttpStatusCodeMapper codeMapper,
IOcelotLoggerFactory loggerFactory)
: base(loggerFactory.CreateLogger<UrlBasedAuthenticationMiddleware>())
{
_next = next;
_responder = responder;
_codeMapper = codeMapper;
}
public async Task Invoke(HttpContext httpContext)
{
await _next.Invoke(httpContext);
if (httpContext.Response.HasStarted)
return;
var errors = httpContext.Items.Errors();
if (errors.Count > 0)
{
Logger.LogWarning($"{errors.ToErrorString()} errors found in {MiddlewareName}. Setting error response for request path:{httpContext.Request.Path}, request method: {httpContext.Request.Method}");
var statusCode = _codeMapper.Map(errors);
var error = string.Join(",", errors.Select(x => x.Message));
httpContext.Response.StatusCode = statusCode;
// output error
await httpContext.Response.WriteAsync(error);
}
else
{
Logger.LogDebug("no pipeline errors, setting and returning completed response");
var downstreamResponse = httpContext.Items.DownstreamResponse();
await _responder.SetResponseOnHttpContext(httpContext, downstreamResponse);
}
}
}
相比之前的中間件,主要變化就是對於 Error 的處理,感覺這里 ocelot 可以抽象一下,增加一個接口 ErrorResponser
之類的,現在的 responder 沒有直接把錯誤信息直接傳進去造成一些不變,加一個 ErrorResponder
只處理 Error 相關的邏輯,把錯誤信息直接傳進去,這樣用戶也就可以更為靈活的注冊自己的服務來無侵入的修改發生錯誤時的行為
Sample
要使用這個中間件,就要自己定義 ocelot 中間件的配置,把默認的 Response 中間件替換成自己的中間件即可,示例如下:
app.UseOcelot((ocelotBuilder, ocelotConfiguration) =>
{
// this sets up the downstream context and gets the config
app.UseDownstreamContextMiddleware();
// This is registered to catch any global exceptions that are not handled
// It also sets the Request Id if anything is set globally
ocelotBuilder.UseExceptionHandlerMiddleware();
// This is registered first so it can catch any errors and issue an appropriate response
//ocelotBuilder.UseResponderMiddleware();
ocelotBuilder.UseMiddleware<CustomResponseMiddleware>();
ocelotBuilder.UseDownstreamRouteFinderMiddleware();
ocelotBuilder.UseMultiplexingMiddleware();
ocelotBuilder.UseDownstreamRequestInitialiser();
ocelotBuilder.UseRequestIdMiddleware();
// 自定義中間件,模擬沒有權限的情況
ocelotBuilder.Use((ctx, next) =>
{
ctx.Items.SetError(new UnauthorizedError("No permission"));
return Task.CompletedTask;
});
//ocelotBuilder.UseMiddleware<UrlBasedAuthenticationMiddleware>();
ocelotBuilder.UseLoadBalancingMiddleware();
ocelotBuilder.UseDownstreamUrlCreatorMiddleware();
ocelotBuilder.UseHttpRequesterMiddleware();
}).Wait();
除了上面的 Response 中間件,為了測試方便,我還加了一個中間件,直接設置了一個 Error 來方便測試,隨便訪問一個 Path 來測試一下是不是會有錯誤信息,可以看到正如預期的結果一樣,輸出了我們自定義的錯誤信息
More
完整示例可以從 Github 上獲取 https://github.com/WeihanLi/AspNetCorePlayground/tree/master/OcelotDemo
Reference
- https://github.com/WeihanLi/AspNetCorePlayground/blob/master/OcelotDemo/OcelotMiddleware/CustomResponseMiddleware.cs
- https://github.com/WeihanLi/AspNetCorePlayground/tree/master/OcelotDemo
- https://github.com/ThreeMammals/Ocelot/blob/17.0.0/src/Ocelot/Responder/HttpContextResponder.cs
- https://github.com/ThreeMammals/Ocelot/blob/17.0.0/src/Ocelot/Responder/Middleware/ResponderMiddleware.cs