重新整理 .net core 實踐篇—————異常中間件[二十]


前言

簡單介紹一下異常中間件的使用。

正文

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上,看需求了。

以上只是個人整理,如有錯誤,望請指點。

下一節,靜態文件,以前寫過,重新整理一下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM