简体   繁体   English

在ASP.Net Core 2 Web API发生异常时捕获JSON请求

[英]Capture JSON request when exception on ASP.Net Core 2 Web API

I'm creating ASP.Net Core Web API using .Net Core 2.1 and I need to create custom middleware for global exception handling. 我正在使用.Net Core 2.1创建ASP.Net Core Web API,并且需要创建用于全局异常处理的自定义中间件。 What I am trying to do is capture JSON request when exception happened in my application. 我正在尝试做的是在应用程序中发生异常时捕获JSON请求。 And since I'm using custom middleware, I want the JSON request available in my middleware. 由于我使用的是自定义中间件,因此我希望在中间件中可以使用JSON请求。 How I can accomplish this? 我怎样才能做到这一点?

I've tried creating custom middleware for centralized exception handling by following an article from Marinko Spasojevic and slightly modified it to capture the json request. 我通过遵循Marinko Spasojevic的一篇文章尝试创建用于集中式异常处理的自定义中间件,并对其进行了少许修改以捕获json请求。 It seems the request already unavailable since the exception occurred inside controllers action (not in the middleware). 似乎该请求已不可用,因为该异常发生在控制器操作内(不在中间件中)。 Here is my code: 这是我的代码:

Here is my Error Log model 这是我的错误日志模型

public class ErrorLog
{
    public DateTime LogDate { get; set; }
    public string URL { get; set; }
    public string Request { get; set; }
    public string Source { get; set; }
    public string Message { get; set; }
}

Here is standard response model used in my project 这是我的项目中使用的标准响应模型

public class BaseResponse<T> : IBaseResponse where T : class
{
    public bool Status { get; set; }
    public string Message { get; set; }
    public IEnumerable<T> Data { get; set; }
}

Here is my custom exception middleware 这是我的自定义异常中间件

public class GlobalException
{
    private readonly RequestDelegate _next;
    private readonly ICustomLogger _logger;

    public GlobalException(RequestDelegate next, ICustomLogger logger)
    {
        _logger = logger;
        _next = next;
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        try
        {
            await _next(httpContext);
        }
        catch (Exception ex)
        {
            ErrorLog log = new ErrorLog();
            log = await HandleLogError(httpContext, ex);

            _logger.LogError(log); // Custom build logger

            await HandleExceptionAsync(httpContext, ex);
        }
    }

    private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        BaseResponse<object> response = new BaseResponse<object>();
        response.Status = false;
        response.Message = "There is an exception occured.";
        response.Data = new List<object>();

        await context.Response.WriteAsync(response.Serialize());
    }

    private static async Task<ErrorLog> HandleLogError(HttpContext context, Exception exception)
    {
        Stream body = context.Request.Body;

        context.Request.EnableRewind();

        byte[] buffer = new byte[Convert.ToInt32(context.Request.ContentLength)];

        await context.Request.Body.ReadAsync(buffer, 0, buffer.Length);

        string requestText = Encoding.UTF8.GetString(buffer);

        context.Request.Body = body;

        ErrorLog log = new ErrorLog();
        UriBuilder builder = new UriBuilder();

        builder.Scheme = context.Request.Scheme;
        builder.Host = context.Request.Host.Host;
        builder.Path = context.Request.Path.ToString();
        builder.Query = context.Request.QueryString.ToString();

        log.LogDate = DateTime.Now;
        log.URL = builder.Uri.ToString();
        log.Request = requestText;
        log.Source = exception.Source;
        log.Message = exception.Message;

        return log;
    }
}

And finnally register the middleware 并最终注册中间件

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        ...
        app.UseMiddleware<GlobalException>();
        ...
    }

So, anyone can give me an enlightenment? 那么,谁能给我启发? Any help would be appreciated and thank you before. 任何帮助将不胜感激,并在此之前感谢您。

It seems the request already unavailable since the exception occurred inside controllers action (not in the middleware). 似乎该请求已不可用,因为该异常发生在控制器操作内(不在中间件中)。

Firstly, you can get the root error by ex.InnerException if this is what you wanna get. 首先,如果这是您ex.InnerException则可以通过ex.InnerException获得根错误。

What I am trying to do is capture JSON request when exception happened in my application. 我正在尝试做的是在应用程序中发生异常时捕获JSON请求。

Also, you can read and log requests & responses (in this case I assume it is serialized to json) as shown below in your error handling middleware. 另外,您可以读取和记录请求和响应(在这种情况下,我假设它已序列化为json),如下所示在您的错误处理中间件中。

public async Task InvokeAsync(HttpContext context)
{  
    var requestAsString = await FormatRequest(context.Request);

    var originalBodyStream = context.Response.Body;

    using (var responseBody = new MemoryStream())
    {
        context.Response.Body = responseBody;

        await _next(context);

        var responseString = await FormatResponse(context.Response);

        await responseBody.CopyToAsync(originalBodyStream);
    }
}

private async Task<string> FormatRequest(HttpRequest request)
{
    var body = request.Body;
    request.EnableRewind();

    var buffer = new byte[Convert.ToInt32(request.ContentLength)];
    await request.Body.ReadAsync(buffer, 0, buffer.Length);
    var bodyAsText = Encoding.UTF8.GetString(buffer);
    request.Body = body;

   return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
}

private async Task<string> FormatResponse(HttpResponse response)
{
    response.Body.Seek(0, SeekOrigin.Begin);
    var text = await new StreamReader(response.Body).ReadToEndAsync(); 
    response.Body.Seek(0, SeekOrigin.Begin);

    return $"Response {text}";
}

Btw, I have made small changes to make it fit to your problem properly, but the credits goes to this gist page. 顺便说一句,我做了一些小的改动以使其正确地适合您的问题,但是积分显示在这个要点页面上。 Hope this solves your problem. 希望这能解决您的问题。

Apparently this part of code causing the request cannot retrieved 显然,导致请求无法检索的这部分代码

...
try
{
    await _next(httpContext); // this will change the httpContext contents
}
...

Here is my final code 这是我的最终代码

public class GlobalException
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    public GlobalException(RequestDelegate next, ILogger logger)
    {
        _logger = logger;
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        HttpContext tempCtx = context; // had to contain the http context
        string request = await FormatRequest(context.Request);

        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            ErrorLog log = new ErrorLog();
            UriBuilder builder = new UriBuilder();

            builder.Scheme = tempCtx.Request.Scheme;
            builder.Host = tempCtx.Request.Host.Host;
            builder.Path = tempCtx.Request.Path.ToString();
            builder.Query = tempCtx.Request.QueryString.ToString();

            log.LogDate = DateTime.Now;
            log.URL = builder.Uri.ToString();
            log.Request = request;
            log.Source = ex.Source;
            log.Message = ex.Message;

            await _logger.LogError(log); // custom logger

            await HandleExceptionAsync(context);
        }
    }

    private async Task<string> FormatRequest(HttpRequest request)
    {
        request.EnableRewind();
        var body = request.Body;

        byte[] buffer = new byte[Convert.ToInt32(request.ContentLength)];

        await request.Body.ReadAsync(buffer, 0, buffer.Length);

        string requestBody = Encoding.UTF8.GetString(buffer);

        body.Seek(0, SeekOrigin.Begin);

        request.Body = body;

        return requestBody;
    }

    private async Task HandleExceptionAsync(HttpContext context)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        BaseResponse<object> response = new BaseResponse<object>();
        response.Status = false;
        response.Message = "There is an exception occured.";
        response.Data = new List<object>();

        await context.Response.WriteAsync(response.Serialize());
    }
}

Great thanks to Hasan for giving me an enlightenment. 非常感谢Hasan给我的启发。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 asp.net core 在 JSON web api 请求中捕获包含的属性名称? - How can I capture included property names in a JSON web api request with asp.net core? 抛出异常的 IEnumerable 在 ASP.NET 核心 Web ZDB93474238D1043CACE1 中的 OkResult 传递时返回无效的 Json - IEnumerable that throws exception returns invalid Json when passed into OkResult in ASP.NET Core Web API 全局捕获程序异常错误并写入asp.net Core 2.1 Web API中的文件 - Globally capture the program exception error and write to a file in asp.net core 2.1 web api ASP.NET 内核 Web API 使用请求/响应模式处理异常 - ASP.NET Core Web API exception handling with Request/Response Pattern ASP.net Core Web API localdb连接字符串异常 - ASP.net Core Web api localdb connection string exception ASP.NET Core Web API 异常处理 - ASP.NET Core Web API exception handling Linux 上 ASP.Net Core Web API 中未处理的异常 - Unhandled Exception in ASP.Net Core Web API on Linux ASP.NET 内核中的全局异常处理程序 Web API 3.1 - Global Exception Handler in ASP.NET Core Web API 3.1 ASP.NET 核心 Web API 必需的属性异常处理 - ASP.NET Core Web API Required Property Exception Handling ASP.NET Core Web API 尝试捕获异常问题 - ASP.NET Core Web API try catch exception question
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM