百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程文章 > 正文

ABP异常为什么是403呢?_abp什么意思

qiyuwang 2025-02-21 15:13 6 浏览 0 评论

前言

在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(); //使用工作单元中间件

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

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

相关推荐

Java 环境安装详细指南(java环境安装步骤)

前言...

学习笔记-Linux JDK - 安装&配置

前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统...

Ubuntu16.04.1安装Java8(ubuntu安装java的命令)

上篇文章讲解了怎么在Windows下安装Java8《Windows10安装Java8》,这里讲解下怎么在Linux下安装Java。由于之前已经安装了Ubuntu16.04.1《...

Ubuntu 下安装 JDK17(ubuntu安装jdk1.7)

JavaSE17Ubuntu下JDK的安装本文主要针对Ubuntu的环境进行Java17的JDK安装。下载地址:...

Ubuntu安装JDK(ubuntu安装jdk报错)

在Ubuntu系统上安装JDK8u441版本,可以通过多种方式实现,包括使用官方JDK的PPA仓库、下载JDK的.tar.gz文件手动安装,或者使用第三方PPA仓库如WebUpd8。以下是通过JDK...

前端资源-实用的JS插件(前端浏览器插件)

现在前端资源越来越多,有创意十足的,有实用性高的,这些对于设计师和前端人员来说都是不错的灵感和资源,所以我们可多关注这些信息,对自己的专业技术有也会帮助的。今天设计达人网为大家分享有:页面进度条、图像...

图片延迟加载,你会使用吗?给你推荐几款插件,快来学习吧

图片延迟加载延迟加载就是当真正需要的时候,才执行加载操作。延迟加载作为Web前端性能优化的一种措施,已经越来越多的应用到各种程序中,而图片的延迟加载作为使用是最广泛的一种,更应该被我们掌握,今天我就给...

突发!Vite 插件惊现图片处理黑科技

【AlarmLevel】趣味【AlarmTitle】突发!Vite插件惊现图片处理黑科技【AlarmOverview】就在昨天,GitHub上一款名为vite-plugin-imagemi...

盘点前端程序员制作网站的常用工具

网站制作时,为了能够更快速、高效地完成任务,往往需要网站制作工具来进行辅助。尤其是前端程序员,五花八门的网站制作工具。今天就来盘点前端程序员一般开发网站程序时使用的那类网站制作工具。...

MyBatis 插件原理与实战(mybatis好用的插件idea)

文章导读MyBatis插件原理与实战什么是插件?...

VisBug:助力前端开发的浏览器插件

作为前端开发者相信肯定有遇到过以下场景:...

前端插件-unplugin-auto-import真的香香

没用这个插件前:你在Vue3中写了50个组件,每个文件开头都要重复这堆代码:import{ref,computed}from'vue'import{useRoute,...

VSCode中值得推荐的常用的16个高效前端插件「主题篇」(一)

VSCode是我们前端开发的一个强大的IDE,所以选择趁手好用的插件是提高开发效率,然后剩下的时间用来摸鱼是很有必要滴。主题篇(16)VSCodeGreatIcons...

支持快速集成的前端网站反馈小插件

大家好,我是章鱼猫。...

很香的几款开源免费的流程设计器(开源流程图设计器)

1、LogicFlow(1)介绍:LogicFlow是一款流程图编辑框架,提供了一系列流程图交互、编辑所必需的功能和灵活的节点自定义、插件等拓展机制。LogicFlow支持前端研发自定义开发各种逻...

取消回复欢迎 发表评论: