当前位置:首页 > 技术分析 > 正文内容

ABP异常为什么是403呢?(abp的意思)

ruisui883个月前 (02-04)技术分析20

前言

在ABP中使用UserFriendlyException抛出异常,HTTP状态码为什么是403?

下面用这一段测试代码:

[HttpPost]
public async Task GetPeopleAsync(string sn)
{
    //这里只是测试abp的UserFriendlyException异常
    if (sn.Equals("123", StringComparison.OrdinalIgnoreCase))
    {
        throw new UserFriendlyException("测试异常");
    }

    if (sn.IsNullOrWhiteSpace())
    {
        throw new UserFriendlyException("查询单号不能为空");
    }

    IQueryable query = await _peopleRepository.GetQueryableAsync();
    query = query.WhereIf(!sn.IsNullOrWhiteSpace(), p => p.SN.Contains(sn));  //这里只是单个条件,真实环境有多个条件
    var item = await ObjectMapper.GetMapper().ProjectTo(query.AsNoTracking()).FirstOrDefaultAsync();
    if (item == null)
    {
        throw new UserFriendlyException($"没有查询到单号:{sn}数据");
    }
    return item;
}

在Swagger调用接口结果:

返回的Http状态码是403,有时候容易觉得是是在没权限调用这个接口.其实并不是.下面学习一下ABP的异常处理.

ABP是在什么时候处理异常的呢?

ABP 究竟是于何时处理异常的呢?怀揣着这一疑问,在项目中竟未见到直接处理异常的情形(于 Asp.Net Core 项目中,运用了众多中间件)。那么,我们先来查看一下启动项目中的
OnApplicationInitialization 中是否存在与 Execption 相关的中间件。

故而,只得查看项目的 Main 代码:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;

namespace BookStore.Web;

public class Program
{
    public async static Task Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
#if DEBUG
            .MinimumLevel.Information()
#else
            .MinimumLevel.Information()
#endif
            .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
            .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Information)
            .Enrich.FromLogContext()
            .WriteTo.Async(c => c.File("Logs/logs.txt"))
            .WriteTo.Async(c => c.Console())
            .CreateLogger();

        try
        {
            Log.Information("Starting web host.");
            var builder = WebApplication.CreateBuilder(args);
            builder.Host.AddAppSettingsSecretsJson()
                .UseAutofac()
                .UseSerilog();
            await builder.AddApplicationAsync();
            var app = builder.Build();
            await app.InitializeApplicationAsync();  //看来看去,是不是在这里
            await app.RunAsync();
            return 0;
        }
        catch (Exception ex)
        {
            if (ex is HostAbortedException)
            {
                throw;
            }

            Log.Fatal(ex, "Host terminated unexpectedly!");
            return 1;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
}

果不其然,在此处,于
AbpApplicationBuilderExtensions.cs 中看到了 UseAbpExcptionHandling .

//看到有异常处理的中间件
public static IApplicationBuilder UseAbpExceptionHandling(this IApplicationBuilder app)
{
    if (app.Properties.ContainsKey(ExceptionHandlingMiddlewareMarker))
    {
        return app;
    }

    app.Properties[ExceptionHandlingMiddlewareMarker] = true;
    return app.UseMiddleware();  //看来看去,应该是在这里
}

接下来,我们持续研习
AbpExceptionHandingMiddleware 中间件的源码:

public class AbpExceptionHandlingMiddleware : AbpMiddlewareBase, ITransientDependency
{
    private readonly ILogger _logger;

    private readonly Func _clearCacheHeadersDelegate;

    public AbpExceptionHandlingMiddleware(ILogger logger)
    {
        _logger = logger;

        _clearCacheHeadersDelegate = ClearCacheHeaders;
    }

    public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        try
        {
            //***如果没有异常,处理下一个中间件
            await next(context);
        }
        catch (Exception ex)
        {
            //***有异常时,捕获异常
            // We can't do anything if the response has already started, just abort.
            if (context.Response.HasStarted)
            {
                _logger.LogWarning("An exception occurred, but response has already started!");
                throw;
            }

            if (context.Items["_AbpActionInfo"] is AbpActionInfoInHttpContext actionInfo)
            {
                if (actionInfo.IsObjectResult) //TODO: Align with AbpExceptionFilter.ShouldHandleException!
                {
                    //具体处理异常
                    await HandleAndWrapException(context, ex);
                    return;
                }
            }

            throw;
        }
    }

    private async Task HandleAndWrapException(HttpContext httpContext, Exception exception)
    {
        _logger.LogException(exception);

        await httpContext
            .RequestServices
            .GetRequiredService()
            .NotifyAsync(
                new ExceptionNotificationContext(exception)
            );

        if (exception is AbpAuthorizationException)
        {
            await httpContext.RequestServices.GetRequiredService()
                .HandleAsync(exception.As(), httpContext);
        }
        else
        {
            var errorInfoConverter = httpContext.RequestServices.GetRequiredService();
            //***看到Http Exception StatusCode
            var statusCodeFinder = httpContext.RequestServices.GetRequiredService();
            var jsonSerializer = httpContext.RequestServices.GetRequiredService();
            var exceptionHandlingOptions = httpContext.RequestServices.GetRequiredService>().Value;

            httpContext.Response.Clear();
            httpContext.Response.StatusCode = (int)statusCodeFinder.GetStatusCode(httpContext, exception);
            httpContext.Response.OnStarting(_clearCacheHeadersDelegate, httpContext.Response);
            httpContext.Response.Headers.Append(AbpHttpConsts.AbpErrorFormat, "true");
            httpContext.Response.Headers.Append("Content-Type", "application/json");

            await httpContext.Response.WriteAsync(
                jsonSerializer.Serialize(
                    new RemoteServiceErrorResponse(
                        errorInfoConverter.Convert(exception, options =>
                        {
                            options.SendExceptionsDetailsToClients = exceptionHandlingOptions.SendExceptionsDetailsToClients;
                            options.SendStackTraceToClients = exceptionHandlingOptions.SendStackTraceToClients;
                        })
                    )
                )
            );
        }
    }

    private Task ClearCacheHeaders(object state)
    {
        var response = (HttpResponse)state;

        response.Headers[HeaderNames.CacheControl] = "no-cache";
        response.Headers[HeaderNames.Pragma] = "no-cache";
        response.Headers[HeaderNames.Expires] = "-1";
        response.Headers.Remove(HeaderNames.ETag);

        return Task.CompletedTask;
    }
}

紧接着,我们再看:

public class DefaultHttpExceptionStatusCodeFinder : IHttpExceptionStatusCodeFinder, ITransientDependency
{
    protected AbpExceptionHttpStatusCodeOptions Options { get; }

    public DefaultHttpExceptionStatusCodeFinder(
        IOptions options)
    {
        Options = options.Value;
    }

    public virtual HttpStatusCode GetStatusCode(HttpContext httpContext, Exception exception)
    {
        if (exception is IHasHttpStatusCode exceptionWithHttpStatusCode &&
            exceptionWithHttpStatusCode.HttpStatusCode > 0)
        {
            return (HttpStatusCode)exceptionWithHttpStatusCode.HttpStatusCode;
        }

        if (exception is IHasErrorCode exceptionWithErrorCode &&
            !exceptionWithErrorCode.Code.IsNullOrWhiteSpace())
        {
            if (Options.ErrorCodeToHttpStatusCodeMappings.TryGetValue(exceptionWithErrorCode.Code!, out var status))
            {
                return status;
            }
        }

        if (exception is AbpAuthorizationException)
        {
            return httpContext.User.Identity!.IsAuthenticated
                ? HttpStatusCode.Forbidden
                : HttpStatusCode.Unauthorized;
        }

        //TODO: Handle SecurityException..?

        if (exception is AbpValidationException)
        {
            return HttpStatusCode.BadRequest;  //400
        }

        if (exception is EntityNotFoundException)
        {
            return HttpStatusCode.NotFound;  //4040
        }

        if (exception is AbpDbConcurrencyException)
        {
            return HttpStatusCode.Conflict;  //409
        }

        if (exception is NotImplementedException)
        {
            return HttpStatusCode.NotImplemented; //501
        }

        if (exception is IBusinessException)
        {
            //403 符合我们使用UserFriendlyException响应的状态码
            return HttpStatusCode.Forbidden;  
        }

        return HttpStatusCode.InternalServerError; //500
    }
}

看到 403 状态码所对应的异常,竟然是 IBusinessException .而后,我们再回归到 UserFriendlyException ,对其源码予以查看.

//继承BusinessException,实现IUserFriendException
[Serializable]
public class UserFriendlyException : BusinessException, IUserFriendlyException
{
}

//IUserFriendlyException 实现IBusinessException接口
public interface IUserFriendlyException : IBusinessException
{
}

到这里基本上,弄明白UserFriendlyException是如何返回403状态码的.怎么对UseAbpExceptionHandling启用呢?,查找源码发现是在启用工作单元之前,先启用的UseAbpExceptionHandling.

public static IApplicationBuilder UseUnitOfWork(this IApplicationBuilder app)
{
    return app
        .UseAbpExceptionHandling()  //先启用异常处理的中间件
        .UseMiddleware(); //启用工作单元的中间件
}

这样就和Module中
OnApplicationInitialization中启用工作单元中间件衔接起来了.

app.UseUnitOfWork(); //使用工作单元中间件

个人能力有限,如果您发现有什么不对,请私信我

如果您觉得对您有用的话,可以点个赞或者加个关注,欢迎大家一起进行技术交流

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/1670.html

标签: objectmapper
分享给朋友:

“ABP异常为什么是403呢?(abp的意思)” 的相关文章

2024年10 大 Linux 桌面发行版推荐

年已过半,现在是探究 2024 年最流行的 Linux 发行版的最佳时机。Linux 是一个开源操作系统,构建在 Linux 内核上,并集成了 GNU shell 实用程序、桌面环境、应用程序、包管理系统。由于其通用性、安全性、用户友好性和多样性,它的受欢迎程度超过了其他操作系统。在本文中,我们将从...

gitlab常用命令大全

GitLab常用命令大全GitLab是一个基于Git的Web平台,它不仅提供代码托管,还集成了持续集成/持续交付(CI/CD)、代码审查、问题追踪等功能。在日常使用GitLab的过程中,我们常常需要使用一系列命令来管理代码仓库、处理分支和标签等。以下是GitLab常用的Git命令大全,并附上详细解释...

一次Java内存占用高的排查案例,解释了我对内存问题的所有疑问

问题现象7月25号,我们一服务的内存占用较高,约13G,容器总内存16G,占用约85%,触发了内存报警(阈值85%),而我们是按容器内存60%(9.6G)的比例配置的JVM堆内存。看了下其它服务,同样的堆内存配置,它们内存占用约70%~79%,此服务比其它服务内存占用稍大。那为什么此服务内存占用稍大...

HTML5学习笔记三:HTML5语法规则

1.标签要小写2.属性值可加可不加””或”3.可以省略某些标签 html body head tbody4.可以省略某些结束标签 tr td li例:显示效果:5.单标签不用加结束标签img input6.废除的标签font center big7.新添加的标签将在下一HTML5学习笔记中重点阐述。...

Acustica Audio 发布模拟Roland Jupiter 双声道合成器插件 TH2

福利: Acustica Audio 发布模拟Roland Jupiter 风格的双声道合成器插件 TH2 免费下载 意大利 Acustica Audio 公司发布布模拟Roland Jupiter 风格的双声道合成器插件 TH2 ,灵感来源于Acustica Audio的THING-8系列,它是...

再来一波黑科技工具,低调使用

静读天下静读天下是一个特别优秀的电子书阅读器。它上面有多个在线书库,像古登堡计划,很多种优秀的书杂志,都可以下载来阅读。它还能智能识别章节功能,还支持外置的语音阅读功能。它支持多种文本格式,比如说txt,pdf,epub,mobi等等。为了便于阅读它还有10 种配色方式,还有夜间模式。不过免费版有广...