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

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

ruisui884个月前 (02-04)技术分析29

前言

在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的意思)” 的相关文章

vue中如何在自定义组件上使用v-model和.sync

自定义事件tips推荐始终使用 kebab-case 的事件名。(v-on会将事件名自动转换为小写,避免匹配不到)changeData ×change-data √自定义组件的v-model用法:父组件定义数据源(不需要定义修改数据的方法),在子组件标签上通过v-model="data...

用IDEA开发如何用Git快速拉取指定分支代码?

1,准备空的文件夹,git init2,关联远程仓库,git remote add origin gitlab地址3,拉取远程分支代码,git pull origin 远程分支名再用IDEA打开项目即可...

摄影后期必看 | PS插件camera raw 16.4教程 | 范围蒙版

范围蒙版Camera Raw 【蒙版】模块中提供了三个范围蒙版工具,可以通过特定的范围来创建蒙版。此次新增的【范围蒙版】大大加强了acr插件对局部调整的能力。点击下拉小箭头可以看到【颜色范围】,可用于快速选择想要编辑的颜色。快捷键:Shift + C【明亮度范围】,可用于快速选择想要调整的明亮度。快...

虚幻引擎5.5发布

IT之家 11 月 13 日消息,虚幻引擎 5.5 现已发布。据介绍,新版本虚幻引擎在动画创作、虚拟制作和移动游戏开发方面取得进步;渲染、摄像机内视觉特效和开发人员迭代等领域的部分功能已可用于生产。IT之家整理部分功能亮点如下:动画Sequencer增强虚幻引擎的非线性动画编辑器 Sequencer...

全新斯柯达柯珞克Karoq深度评测:大众替代品

“斯柯达柯珞克是一款出色的全能家庭 SUV,具有许多有用的功能”价格36,605 英镑- 49,190 英镑优点方便的 VarioFlex 后排座椅非常适合家庭入住驾驶乐趣缺点保修期短保守的内饰性格比Yeti少结论——斯柯达柯珞克是一辆好车吗?斯柯达柯珞克是在辉煌的七座 斯柯达柯迪亚克之后推出的,因...

微信外H5跳转小程序——组件(vue项目)

场景有个H5(vue项目),需要实现点击商品item跳转到小程序,微信内和微信外都要支持,这里我们只介绍一下H5在微信外的跳转。如图所示,红框内是一个商品,就是点击这里,要跳转小程序:配置微信小程序云开发(云函数)1、开通云开发然后选择免费额度2、云开发权限设置找到权限设置,把这里的「未登录用户访问...