ABP异常为什么是403呢?(abp的意思)
前言
在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
紧接着,我们再看:
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(); //使用工作单元中间件
个人能力有限,如果您发现有什么不对,请私信我
如果您觉得对您有用的话,可以点个赞或者加个关注,欢迎大家一起进行技术交流