自定義 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