【问题标题】:Global Exception Handler ASP.Net Core MVC全局异常处理程序 ASP.Net Core MVC
【发布时间】:2019-06-25 18:25:21
【问题描述】:

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

【问题讨论】:

  • 您考虑过使用过滤器吗?我不确定 mvc 上的过滤是如何工作的,我在 web-api 中经常使用它们
  • 一般来说,未处理和意外的异常应该是非常不寻常的事件。所以我不会太担心它将用户重定向到另一个页面。如果您很好地测试了您的应用程序,那么几乎没有人会看到它!

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


【解决方案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返回异常:

如果 Api 返回结果:

【讨论】:

    【解决方案2】:

    来源:https://www.strathweb.com/2018/07/centralized-exception-handling-and-request-validation-in-asp-net-core/

    Asp.Net Core Web Api 3.1.5 中的全局处理异常 我在 asp.net core Web Api 3.1.5 中实现了这些代码,它为我工作

    问题详细信息.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);
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-14
      • 2018-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-01
      • 2016-02-12
      相关资源
      最近更新 更多