全局异常处理程序ASP.Net Core MVC

时间:2019-06-25 18:25:21

标签: c# asp.net-mvc asp.net-core

我正在用Razor视图开发ASP.Net Core MVC应用程序。该应用程序包含许多表单,用户应在其中填写和提交。我有一种特殊的情况,其中记录应用程序中引发的所有异常进行记录。我知道ASP.Net MVC Core带有全局异常处理程序中间件,在该中间件中,我们可以捕获应用程序中发生的所有异常,并在其中进行记录。但是同时,我必须向用户显示一个弹出窗口,指出在保存提交表单的数据时发生了错误。如果成功,则显示成功弹出窗口。如果我在控制器动作中放置了try-catch块,则可以处理该动作,但必须从动作本身中记录相同的内容。有什么方法可以在一个地方处理所有异常并向用户显示错误弹出窗口,而不是将用户重定向到另一个错误页面。

2 个答案:

答案 0 :(得分:1)

这是一个很长的故事(我使用jquery进行API调用)。 首先,我添加如下异常处理:

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate next;
    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context /* other dependencies */)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception ex)
    {
        var code = HttpStatusCode.InternalServerError; // 500 if unexpected

        var result = new BaseResponseDTO<string>()
        {
            ErrorCode = (int)HttpStatusCode.InternalServerError,
            ErrorMessage = ex.Message,
            Succeed = false,
        };

        var jsonResult = JsonConvert.SerializeObject(result);
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)code;
        return context.Response.WriteAsync(jsonResult);
    }
}

然后注册(必须在app.UseMvc()之前注册):

app.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.UseMvc();

好吧,然后,调用您的API。我总是这样返回DTO类:

public class BaseResponseDTO<T>
{
    public bool Succeed { get; set; }
    public string ErrorMessage { get; set; }
    public T Result { get; set; }
    public int? ErrorCode { get; set; }
}

现在是我的Web API:有时返回一个值,有时抛出异常。

public BaseResponseDTO<string> TestApi()
    {
        var r = new Random();
        var random = r.Next(0, 2);
        if (random == 0)
            throw new Exception("My Exception");
        else
            return new BaseResponseDTO<string>() { Succeed = true, Result = "Some result..." };
    }

最后,通过jquery调用它:

    function callApi() {
    $.ajax({
        type: 'GET',
        url: 'https://localhost:5001/Home/TestApi',
        data: null,
        dataType: 'json',
        success: function (data) {
            if (data.succeed) {
                alert(data.result);
            }
            else {
                alert(data.errorMessage);
            }
        },
        error: function (error) {
            debugger;
            alert(error.responseJSON.ErrorMessage);
        }
    });
}

如果Api返回异常:

enter image description here

如果Api返回结果:

enter image description here

答案 1 :(得分:0)

Asp.Net Core Web Api 3.1.5中的全局处理异常 我在asp.net核心Web Api 3.1.5中实现了这些代码,对我有用

ProblemDetail.cs

public class ProblemDetails
{
    public ProblemDetails();
    [JsonPropertyName("detail")]
    public string Detail { get; set; }
   
    [JsonExtensionData]
    public IDictionary<string, object> Extensions { get; }

    [JsonPropertyName("instance")]
    public string Instance { get; set; }

    [JsonPropertyName("status")]
    public int? Status { get; set; }
   
  
    [JsonPropertyName("title")]
    public string Title { get; set; }
  
    [JsonPropertyName("type")]
    public string Type { get; set; }
}

我的Startup.cs类是

public class Startup
{

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;

    }

    public IConfiguration Configuration { get; }


    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        //Data Base Configuration
        services.AddDbContext<Context>(option => option.UseSqlServer(Configuration.GetConnectionString("XYZ")));
 
        // In production, the React files will be served from this directory
        services.AddSpaStaticFiles(configuration =>
        {
            configuration.RootPath = "ClientApp/build";
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        //else
        //{
        //    app.UseExceptionHandler("/Error");
        //    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        //    app.UseHsts();
        //}

        app.ConfigureExceptionHandler();//This The Main Method For Handel Exception

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseSpaStaticFiles();
      
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}");
        });

        app.UseSpa(spa =>
        {
            spa.Options.SourcePath = "ClientApp";

            if (env.IsDevelopment())
            {
                spa.UseReactDevelopmentServer(npmScript: "start");
            }
        });
    }
}

并且方法包含在其中

public static class ExceptionMiddlewareExtensions
{
    public static void ConfigureExceptionHandler(this IApplicationBuilder app)
    {
        app.UseExceptionHandler(appError =>
        {
            appError.Run(async context =>
            {
                var errorFeature = context.Features.Get<IExceptionHandlerFeature>();
                var exception = errorFeature.Error;

                // the IsTrusted() extension method doesn't exist and
                // you should implement your own as you may want to interpret it differently
                // i.e. based on the current principal

                var problemDetails = new ProblemDetails
                {
                    Instance = $"urn:myorganization:error:{Guid.NewGuid()}"
                };

                if (exception is BadHttpRequestException badHttpRequestException)
                {
                    problemDetails.Title = "Invalid request";
                    problemDetails.Status = (int)typeof(BadHttpRequestException).GetProperty("StatusCode",
                        BindingFlags.NonPublic | BindingFlags.Instance).GetValue(badHttpRequestException);
                    problemDetails.Detail = badHttpRequestException.Message;
                }
                else
                {
                    problemDetails.Title = "An unexpected error occurred!";
                    problemDetails.Status = 500;
                    problemDetails.Detail = exception.Demystify() .ToString();//Error 1
                }

                // log the exception etc..

                context.Response.StatusCode = problemDetails.Status.Value;
                context.Response.WriteJson(problemDetails, "application/problem+json");//(Error 2)
            });
        });
    }
}

错误1解决方案

public static class ExceptionExtentions
{
    private static readonly FieldInfo stackTraceString = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);

    private static void SetStackTracesString(this Exception exception, string value)
        => stackTraceString.SetValue(exception, value);

    /// <summary>
    /// Demystifies the given <paramref name="exception"/> and tracks the original stack traces for the whole exception tree.
    /// </summary>
    public static T Demystify<T>(this T exception) where T : Exception
    {
        try
        {
            var stackTrace = new EnhancedStackTrace(exception);

            if (stackTrace.FrameCount > 0)
            {
                exception.SetStackTracesString(stackTrace.ToString());
            }

            if (exception is AggregateException aggEx)
            {
                foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
                {
                    ex.Demystify();
                }
            }

            exception.InnerException?.Demystify();
        }
        catch
        {
            // Processing exceptions shouldn't throw exceptions; if it fails
        }

        return exception;
    }

    /// <summary>
    /// Gets demystified string representation of the <paramref name="exception"/>.
    /// </summary>
    /// <remarks>
    /// <see cref="Demystify{T}"/> method mutates the exception instance that can cause
    /// issues if a system relies on the stack trace be in the specific form.
    /// Unlike <see cref="Demystify{T}"/> this method is pure. It calls <see cref="Demystify{T}"/> first,
    /// computes a demystified string representation and then restores the original state of the exception back.
    /// </remarks>
    [Pure]
    public static string ToStringDemystified(this Exception exception)
        => new StringBuilder().AppendDemystified(exception).ToString();
}

错误2解决方案

public static class HttpExtensions
{
    private static readonly JsonSerializer Serializer = new JsonSerializer { NullValueHandling = NullValueHandling.Ignore };
    public static void WriteJson<T>(this HttpResponse response, T obj, string contentType = null)
    {
        response.ContentType = contentType ?? "application/json";
        using (var writer = new HttpResponseStreamWriter(response.Body, Encoding.UTF8))
        {
            using (var jsonWriter = new JsonTextWriter(writer))
            {
                jsonWriter.CloseOutput = false;
                jsonWriter.AutoCompleteOnClose = false;
                Serializer.Serialize(jsonWriter, obj);
            }
        }
    }
}