前言
簡單介紹一下異常中介軟體的使用。
正文
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
這樣寫入中介軟體哈,那麼在env環境下就會去執行UseDeveloperExceptionPage。
public static IApplicationBuilder UseDeveloperExceptionPage(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<DeveloperExceptionPageMiddleware>();
}
那麼我們應該去看DeveloperExceptionPageMiddleware中介軟體哈。
那麼這裡介紹它是如何能夠捕獲其他中介軟體的異常的哈。
裡面的invoke:
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
// 異常處理
}
}
其實它的操作是很簡單的,直接在外面套了try catch。
裡面的異常處理怎麼處理的可以直接去看DeveloperExceptionPageMiddleware 中介軟體,裡面的操作也比較簡單處理。
測試:
[HttpGet]
public int GetService([FromServices]ISelfService selfService)
{
throw new System.Exception("錯誤");
return 1;
}
結果:
因為上面說了,這個是dev環境下,那麼生產環境不能直接給使用者看到錯誤資訊。
正式環境:
app.UseExceptionHandler("/error");
將錯誤轉移到/error 處理。具體UseExceptionHandler細節篇裡面介紹,有許多可以借鑑的地方。
[ApiController]
[Route("[controller]")]
public class ErrorController : Controller
{
public ILogger<ErrorController> _logger;
public ErrorController(ILogger<ErrorController> logger)
{
this._logger = logger;
}
public IActionResult Index()
{
var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
var ex = exceptionHandlerPathFeature?.Error;
var knownException = ex as IKnownException;
if (knownException == null)
{
_logger.LogError(ex, ex.Message);
knownException = KnownException.Unknow;
}
else
{
knownException = KnownException.FromKnowException(knowException);
}
return View(knownException);
}
}
檢視:
<html>
<head>
</head>
<body>
<div>
錯誤碼: @Model.ErrorCode
</div>
<div>
錯誤資訊: @Model.Message
</div>
</body>
</html>
IKnownException:
public interface IKnownException
{
public string Message { get; }
public int ErrorCode { get; }
public object[] ErrorData { get; }
}
KnownException:
public class KnownException : IKnownException
{
public string Message
{
get; private set;
}
public int ErrorCode
{
get; private set;
}
public object[] ErrorData
{
get;
private set;
}
public readonly static IKnownException Unknow = new KnownException { Message = "未知錯誤", ErrorCode = 99 };
public static IKnownException FromKnowException(IKnownException Exception)
{
return new KnownException{Message = Exception.Message, ErrorCode = Exception.ErrorCode, ErrorData = Exception.ErrorData};
}
}
測試1:
[HttpGet]
public int GetService([FromServices]ISelfService selfService)
{
throw new System.Exception("錯誤");
return 1;
}
這種屬於未知異常,結果:
現在弄一個支付異常:
public class PayErrorException : Exception, IKnownException
{
public PayErrorException(string message, int errorCode, params object[] errorData): base(message)
{
this.ErrorCode = errorCode;
this.ErrorData = errorData;
}
public int ErrorCode { get;private set; }
public object[] ErrorData { get;private set; }
}
測試2:
[HttpGet]
public int GetService([FromServices]ISelfService selfService)
{
throw new PayErrorException("支付錯誤",405,null);
return 1;
}
將異常處理放入到中介軟體分支中。
app.UseExceptionHandler(errApp =>
{
errApp.Run(async context =>
{
var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
IKnownException knownException = exceptionHandlerPathFeature.Error as IKnownException;
if (knownException == null)
{
var logger = context.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
logger.LogError(exceptionHandlerPathFeature.Error, exceptionHandlerPathFeature.Error.Message);
knownException = KnownException.Unknown;
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
else
{
knownException = KnownException.FromKnownException(knownException);
context.Response.StatusCode = StatusCodes.Status200OK;
}
var jsonOptions = context.RequestServices.GetService<IOptions<JsonOptions>>();
context.Response.ContentType = "application/json; charset=utf-8";
await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(knownException, jsonOptions.Value.JsonSerializerOptions));
});
});
效果一樣就不演示了。如果是已知異常錯誤碼應該為200,一個是500異常是系統無法處理,系統錯誤,但是已知錯誤是屬於系統正常處理。另一個是監控系統,認為報500錯誤,是會持續放出系統警告。
還有一種區域性異常,只在mvc中生效,而不是全域性生效:
public class MyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
IKnownException knownException = context.Exception as IKnownException;
if (knownException == null)
{
var logger = context.HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
logger.LogError(context.Exception, context.Exception.Message);
knownException = KnownException.Unknown;
context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
else
{
knownException = KnownException.FromKnownException(knownException);
context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
}
context.Result = new JsonResult(knownException)
{
ContentType = "application/json; charset=utf-8"
};
}
}
在mvc 中註冊:
services.AddMvc(mvcOptions =>
{
mvcOptions.Filters.Add<MyExceptionFilter>();
}).AddJsonOptions(jsonOptions =>
{
jsonOptions.JsonSerializerOptions.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
});
最後介紹一種,只作用於某個控制器,或者action:
public class MyExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
IKnownException knownException = context.Exception as IKnownException;
if (knownException == null)
{
var logger = context.HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
logger.LogError(context.Exception, context.Exception.Message);
knownException = KnownException.Unknown;
context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
else
{
knownException = KnownException.FromKnownException(knownException);
context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
}
context.Result = new JsonResult(knownException)
{
ContentType = "application/json; charset=utf-8"
};
}
}
檢視一下ExceptionFilterAttribute頭部:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class ExceptionFilterAttribute : Attribute, IAsyncExceptionFilter, IExceptionFilter, IOrderedFilter
上面標誌了可以放於類上也可以放於方法上。所以可以放至在controller上,也可以action上,看需求了。
結
以上只是個人整理,如有錯誤,望請指點。
下一節,靜態檔案,以前寫過,重新整理一下。