[Abp 源码分析]十、异常处理

0.简介#

Abp 框架本身针对内部抛出异常进行了统一拦截,并且针对不同的异常也会采取不同的处理策略。在 Abp 当中主要提供了以下几种异常类型:

异常类型描述
AbpExceptionAbp 框架定义的基本异常类型,Abp 所有内部定义的异常类型都继承自本类。
AbpInitializationExceptionAbp 框架初始化时出现错误所抛出的异常。
AbpDbConcurrencyException当 EF Core 执行数据库操作时产生了 DbUpdateConcurrencyException 异常
的时候 Abp 会封装为本异常并且抛出。
AbpValidationException用户调用接口时,输入的DTO 参数有误会抛出本异常。
BackgroundJobException后台作业执行过程中产生的异常。
EntityNotFoundException当仓储执行 Get 操作时,实体未找到引发本异常。
UserFriendlyException如果用户需要将异常信息发送给前端,请抛出本异常。
AbpRemoteCallException远程调用一场,当使用 Abp 提供的 AbpWebApiClient 产生问题的时候
会抛出此异常。

1.启动流程#

Abp 框架针对异常拦截的处理主要使用了 ASP .NET CORE MVC 过滤器机制,当外部请求接口的时候,所有异常都会被 Abp 框架捕获。Abp 异常过滤器的实现名称叫做 AbpExceptionFilter,它在注入 Abp 框架的时候就已经被注册到了 ASP .NET Core 的 MVC Filters 当中了。

1.1 流程图#

1.2 代码流程#

注入 Abp 框架处:


   
   
Copy
public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null) where TStartupModule : AbpModule { var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction);
<span class="hljs-comment">// 配置 ASP .NET Core 参数</span>
ConfigureAspNetCore(services, abpBootstrapper.IocManager);

<span class="hljs-keyword">return</span> WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);

}

ConfigureAspNetCore() 方法内部:


  
  
Copy
private static void ConfigureAspNetCore(IServiceCollection services, IIocResolver iocResolver) { // ...省略掉的其他代码
<span class="hljs-comment">// 配置 MVC</span>
services.Configure&lt;MvcOptions&gt;(mvcOptions =&gt;
{
    mvcOptions.AddAbp(services);
});

<span class="hljs-comment">// ...省略掉的其他代码</span>

}

AbpMvcOptionsExtensions 扩展类针对 MvcOptions 提供的扩展方法 AddAbp()


  
  
Copy
public static void AddAbp(this MvcOptions options, IServiceCollection services) { AddConventions(options, services); // 添加 VC 过滤器 AddFilters(options); AddModelBinders(options); }

AddFilters() 方法内部:


  
  
Copy
private static void AddFilters(MvcOptions options) { // 权限认证过滤器 options.Filters.AddService(typeof(AbpAuthorizationFilter)); // 审计信息过滤器 options.Filters.AddService(typeof(AbpAuditActionFilter)); // 参数验证过滤器 options.Filters.AddService(typeof(AbpValidationActionFilter)); // 工作单元过滤器 options.Filters.AddService(typeof(AbpUowActionFilter)); // 异常过滤器 options.Filters.AddService(typeof(AbpExceptionFilter)); // 接口结果过滤器 options.Filters.AddService(typeof(AbpResultFilter)); }

2.代码分析#

2.1 基本定义#

Abp 框架所提供的所有异常类型都继承自 AbpException ,我们可以看一下该类型的基本定义。


  
  
Copy
// Abp 基本异常定义 [Serializable] public class AbpException : Exception { public AbpException() {
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AbpException</span>(<span class="hljs-params">SerializationInfo serializationInfo, StreamingContext context</span>)
    : <span class="hljs-title">base</span>(<span class="hljs-params">serializationInfo, context</span>)
</span>{

}

<span class="hljs-comment">// 构造函数1,接受一个异常描述信息</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AbpException</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> message</span>)
    : <span class="hljs-title">base</span>(<span class="hljs-params">message</span>)
</span>{

}

<span class="hljs-comment">// 构造函数2,接受一个异常描述信息与内部异常</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AbpException</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> message, Exception innerException</span>)
    : <span class="hljs-title">base</span>(<span class="hljs-params">message, innerException</span>)
</span>{

}

}

类型的定义是十分简单的,基本上就是继承了原有的 Exception 类型,改了一个名字罢了。

2.2 异常拦截#

Abp 本身针对异常信息的核心处理就在于它的 AbpExceptionFilter 过滤器,过滤器实现很简单。它首先继承了 IExceptionFilter 接口,实现了其 OnException() 方法,只要用户请求接口的时候出现了任何异常都会调用 OnException() 方法。而在 OnException() 方法内部,Abp 根据不同的异常类型进行了不同的异常处理。


  
  
Copy
public class AbpExceptionFilter : IExceptionFilter, ITransientDependency { // 日志记录器 public ILogger Logger { get; set; }
<span class="hljs-comment">// 事件总线</span>
<span class="hljs-keyword">public</span> IEventBus EventBus { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

<span class="hljs-comment">// 错误信息构建器</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IErrorInfoBuilder _errorInfoBuilder;
<span class="hljs-comment">// AspNetCore 相关的配置信息</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IAbpAspNetCoreConfiguration _configuration;

<span class="hljs-comment">// 注入并初始化内部成员对象</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AbpExceptionFilter</span>(<span class="hljs-params">IErrorInfoBuilder errorInfoBuilder, IAbpAspNetCoreConfiguration configuration</span>)
</span>{
    _errorInfoBuilder = errorInfoBuilder;
    _configuration = configuration;

    Logger = NullLogger.Instance;
    EventBus = NullEventBus.Instance;
}

<span class="hljs-comment">// 异常触发时会调用此方法</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnException</span>(<span class="hljs-params">ExceptionContext context</span>)
</span>{
    <span class="hljs-comment">// 判断是否由控制器触发,如果不是则不做任何处理</span>
    <span class="hljs-keyword">if</span> (!context.ActionDescriptor.IsControllerAction())
    {
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-comment">// 获得方法的包装特性。决定后续操作,如果没有指定包装特性,则使用默认特性</span>
    <span class="hljs-keyword">var</span> wrapResultAttribute =
        ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(
            context.ActionDescriptor.GetMethodInfo(),
            _configuration.DefaultWrapResultAttribute
        );

   <span class="hljs-comment">// 如果方法上面的包装特性要求记录日志,则记录日志</span>
    <span class="hljs-keyword">if</span> (wrapResultAttribute.LogError)
    {
        LogHelper.LogException(Logger, context.Exception);
    }

    <span class="hljs-comment">// 如果被调用的方法上的包装特性要求重新包装错误信息,则调用 HandleAndWrapException() 方法进行包装</span>
    <span class="hljs-keyword">if</span> (wrapResultAttribute.WrapOnError)
    {
        HandleAndWrapException(context);
    }
}

<span class="hljs-comment">// 处理并包装异常</span>
<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">HandleAndWrapException</span>(<span class="hljs-params">ExceptionContext context</span>)
</span>{
    <span class="hljs-comment">// 判断被调用接口的返回值是否符合标准,不符合则直接返回</span>
    <span class="hljs-keyword">if</span> (!ActionResultHelper.IsObjectResult(context.ActionDescriptor.GetMethodInfo().ReturnType))
    {
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-comment">// 设置 HTTP 上下文响应所返回的错误代码,由具体异常决定。</span>
    context.HttpContext.Response.StatusCode = GetStatusCode(context);

    <span class="hljs-comment">// 重新封装响应返回的具体内容。采用 AjaxResponse 进行封装</span>
    context.Result = <span class="hljs-keyword">new</span> ObjectResult(
        <span class="hljs-keyword">new</span> AjaxResponse(
            _errorInfoBuilder.BuildForException(context.Exception),
            context.Exception <span class="hljs-keyword">is</span> AbpAuthorizationException
        )
    );

    <span class="hljs-comment">// 触发异常处理事件</span>
    EventBus.Trigger(<span class="hljs-keyword">this</span>, <span class="hljs-keyword">new</span> AbpHandledExceptionData(context.Exception));
    
    <span class="hljs-comment">// 处理完成,将异常上下文的内容置为空</span>
    context.Exception = <span class="hljs-literal">null</span>; <span class="hljs-comment">//Handled!</span>
}

<span class="hljs-comment">// 根据不同的异常类型返回不同的 HTTP 错误码</span>
<span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">int</span> <span class="hljs-title">GetStatusCode</span>(<span class="hljs-params">ExceptionContext context</span>)
</span>{
    <span class="hljs-keyword">if</span> (context.Exception <span class="hljs-keyword">is</span> AbpAuthorizationException)
    {
        <span class="hljs-keyword">return</span> context.HttpContext.User.Identity.IsAuthenticated
            ? (<span class="hljs-keyword">int</span>)HttpStatusCode.Forbidden
            : (<span class="hljs-keyword">int</span>)HttpStatusCode.Unauthorized;
    }

    <span class="hljs-keyword">if</span> (context.Exception <span class="hljs-keyword">is</span> AbpValidationException)
    {
        <span class="hljs-keyword">return</span> (<span class="hljs-keyword">int</span>)HttpStatusCode.BadRequest;
    }

    <span class="hljs-keyword">if</span> (context.Exception <span class="hljs-keyword">is</span> EntityNotFoundException)
    {
        <span class="hljs-keyword">return</span> (<span class="hljs-keyword">int</span>)HttpStatusCode.NotFound;
    }

    <span class="hljs-keyword">return</span> (<span class="hljs-keyword">int</span>)HttpStatusCode.InternalServerError;
}

}

以上就是 Abp 针对异常处理的具体操作了,在这里面涉及到的 WrapResultAttributeAjaxResponseIErrorInfoBuilder 都会在后面说明,但是具体的逻辑已经在过滤器所体现了。

2.3 接口返回值包装#

Abp 针对所有 API 返回的数据都会进行一次包装,使得其返回值内容类似于下面的内容。

 
 
Copy
{ "result": { "totalCount": 0, "items": [] }, "targetUrl": null, "success": true, "error": null, "unAuthorizedRequest": false, "__abp": true }

其中的 result 节点才是你接口真正返回的内容,其余的 targetUrl 之类的都是属于 Abp 包装器给你进行封装的。

2.3.1 包装器特性#

其中,Abp 预置的包装器有两种,第一个是 WrapResultAttribute 。它有两个 bool 类型的参数,默认均为 true ,一个叫 WrapOnSuccess 一个 叫做 WrapOnError ,分别用于确定成功或则失败后是否包装具体信息。像之前的 OnException() 方法里面就有用该值进行判断是否包装异常信息。

除了 WarpResultAttribute 特性,还有一个 DontWrapResultAttribute 的特性,该特性直接继承自 WarpResultAttribute ,只不过它的 WrapOnSuccessWrapOnError 都为 fasle 状态,也就是说无论接口调用结果是成功还是失败,都不会进行结果包装。该特性可以直接打在接口方法、控制器、接口之上,类似于这样:


  
  
Copy
public class TestApplicationService : ApplicationService { [DontWrapResult] public async Task<string> Get() { return await Task.FromResult("Hello World"); } }

那么这个接口的返回值就不会带有其他附加信息,而直接会按照 Json 来序列化返回你的对象。

在拦截异常的时候,如果你没有给接口方法打上 DontWarpResult 特性,那么他就会直接使用 IAbpAspNetCoreConfigurationDefaultWrapResultAttribute 属性指定的默认特性,该默认特性如果没有显式指定则为 WrapResultAttribute


  
  
Copy
public AbpAspNetCoreConfiguration() { DefaultWrapResultAttribute = new WrapResultAttribute(); // ...IAbpAspNetCoreConfiguration 的默认实现的构造函数 // ...省略掉了其他代码 }
2.3.2 具体包装行为#

Abp 针对正常的接口数据返回与异常数据返回都是采用的 AjaxResponse 来进行封装的,转到其基类的定义可以看到在里面定义的那几个属性就是我们接口返回出来的数据。


  
  
Copy
public abstract class AjaxResponseBase { // 目标 Url 地址 public string TargetUrl { get; set; }
<span class="hljs-comment">// 接口调用是否成功</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> Success { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

<span class="hljs-comment">// 当接口调用失败时,错误信息存放在此处</span>
<span class="hljs-keyword">public</span> ErrorInfo Error { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

<span class="hljs-comment">// 是否是未授权的请求</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> UnAuthorizedRequest { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

<span class="hljs-comment">// 用于标识接口是否基于 Abp 框架开发</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> __abp { <span class="hljs-keyword">get</span>; } = <span class="hljs-literal">true</span>;

}

So,从刚才的 2.2 节 可以看到他是直接 new 了一个 AjaxResponse 对象,然后使用 IErrorInfoBuilder 来构建了一个 ErrorInfo 错误信息对象传入到 AjaxResponse 对象当中并且返回。

那么问题来了,这里的 IErrorInfoBuilder 是怎样来进行包装的呢?

2.3.3 异常包装器#

当 Abp 捕获到异常之后,会通过 IErrorInfoBuilderBuildForException() 方法来将异常转换为 ErrorInfo 对象。它的默认实现只有一个,就是 ErrorInfoBuilder ,内部结构也很简单,其 BuildForException() 方法直接通过内部的一个转换器进行转换,也就是 IExceptionToErrorInfoConverter,直接调用的 IExceptionToErrorInfoConverter.Convert() 方法。

同时它拥有另外一个方法,叫做 AddExceptionConverter(),可以传入你自己实现的异常转换器。


  
  
Copy
public class ErrorInfoBuilder : IErrorInfoBuilder, ISingletonDependency { private IExceptionToErrorInfoConverter Converter { get; set; }
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ErrorInfoBuilder</span>(<span class="hljs-params">IAbpWebCommonModuleConfiguration configuration, ILocalizationManager localizationManager</span>)
</span>{
    <span class="hljs-comment">// 异常包装器默认使用的 DefaultErrorInfoConverter 来进行转换</span>
    Converter = <span class="hljs-keyword">new</span> DefaultErrorInfoConverter(configuration, localizationManager);
}

<span class="hljs-comment">// 根据异常来构建异常信息</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> ErrorInfo <span class="hljs-title">BuildForException</span>(<span class="hljs-params">Exception exception</span>)
</span>{
    <span class="hljs-keyword">return</span> Converter.Convert(exception);
}

<span class="hljs-comment">// 添加用户自定义的异常转换器</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">AddExceptionConverter</span>(<span class="hljs-params">IExceptionToErrorInfoConverter converter</span>)
</span>{
    converter.Next = Converter;
    Converter = converter;
}

}

2.3.4 异常转换器#

Abp 要包装异常,具体的操作是由转换器来决定的,Abp 实现了一个默认的转换器,叫做 DefaultErrorInfoConverter,在其内部,注入了 IAbpWebCommonModuleConfiguration 配置项,而用户可以通过配置该选项的 SendAllExceptionsToClients 属性来决定是否将异常输出给客户端。

我们先来看一下他的 Convert() 核心方法:


  
  
Copy
public ErrorInfo Convert(Exception exception) { // 封装 ErrorInfo 对象 var errorInfo = CreateErrorInfoWithoutCode(exception);
<span class="hljs-comment">// 如果具体的异常实现有 IHasErrorCode 接口,则将错误码也封装到 ErrorInfo 对象内部</span>
<span class="hljs-keyword">if</span> (exception <span class="hljs-keyword">is</span> IHasErrorCode)
{
    errorInfo.Code = (exception <span class="hljs-keyword">as</span> IHasErrorCode).Code;
}

<span class="hljs-keyword">return</span> errorInfo;

}

核心十分简单,而 CreateErrorInfoWithoutCode() 方法内部呢也是一些具体的逻辑,根据异常类型的不同,执行不同的转换逻辑。


  
  
Copy
private ErrorInfo CreateErrorInfoWithoutCode(Exception exception) { // 如果要发送所有异常,则使用 CreateDetailedErrorInfoFromException() 方法进行封装 if (SendAllExceptionsToClients) { return CreateDetailedErrorInfoFromException(exception); }
<span class="hljs-comment">// 如果有多个异常,并且其内部异常为 UserFriendlyException 或者 AbpValidationException 则将内部异常拿出来放在最外层进行包装</span>
<span class="hljs-keyword">if</span> (exception <span class="hljs-keyword">is</span> AggregateException &amp;&amp; exception.InnerException != <span class="hljs-literal">null</span>)
{
    <span class="hljs-keyword">var</span> aggException = exception <span class="hljs-keyword">as</span> AggregateException;
    <span class="hljs-keyword">if</span> (aggException.InnerException <span class="hljs-keyword">is</span> UserFriendlyException ||
        aggException.InnerException <span class="hljs-keyword">is</span> AbpValidationException)
    {
        exception = aggException.InnerException;
    }
}

<span class="hljs-comment">// 如果一场类型为 UserFriendlyException 则直接通过 ErrorInfo 构造函数进行构建</span>
<span class="hljs-keyword">if</span> (exception <span class="hljs-keyword">is</span> UserFriendlyException)
{
    <span class="hljs-keyword">var</span> userFriendlyException = exception <span class="hljs-keyword">as</span> UserFriendlyException;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ErrorInfo(userFriendlyException.Message, userFriendlyException.Details);
}

<span class="hljs-comment">// 如果为参数类一场,则使用不同的构造函数进行构建,并且在这里可以看到他通过 L 函数调用的多语言提示</span>
<span class="hljs-keyword">if</span> (exception <span class="hljs-keyword">is</span> AbpValidationException)
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ErrorInfo(L(<span class="hljs-string">"ValidationError"</span>))
    {
        ValidationErrors = GetValidationErrorInfos(exception <span class="hljs-keyword">as</span> AbpValidationException),
        Details = GetValidationErrorNarrative(exception <span class="hljs-keyword">as</span> AbpValidationException)
    };
}

<span class="hljs-comment">// 如果是实体未找到的异常,则包含具体的实体类型信息与实体 ID 值</span>
<span class="hljs-keyword">if</span> (exception <span class="hljs-keyword">is</span> EntityNotFoundException)
{
    <span class="hljs-keyword">var</span> entityNotFoundException = exception <span class="hljs-keyword">as</span> EntityNotFoundException;

    <span class="hljs-keyword">if</span> (entityNotFoundException.EntityType != <span class="hljs-literal">null</span>)
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ErrorInfo(
            <span class="hljs-keyword">string</span>.Format(
                L(<span class="hljs-string">"EntityNotFound"</span>),
                entityNotFoundException.EntityType.Name,
                entityNotFoundException.Id
            )
        );
    }

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ErrorInfo(
        entityNotFoundException.Message
    );
}

<span class="hljs-comment">// 如果是未授权的一场,一样的执行不同的操作</span>
<span class="hljs-keyword">if</span> (exception <span class="hljs-keyword">is</span> Abp.Authorization.AbpAuthorizationException)
{
    <span class="hljs-keyword">var</span> authorizationException = exception <span class="hljs-keyword">as</span> Abp.Authorization.AbpAuthorizationException;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ErrorInfo(authorizationException.Message);
}

<span class="hljs-comment">// 除了以上这几个固定的异常需要处理之外,其他的所有异常统一返回内部服务器错误信息。</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ErrorInfo(L(<span class="hljs-string">"InternalServerError"</span>));

}

所以整体异常处理还是比较复杂的,进行了多层封装,但是结构还是十分清晰的。

3.扩展#

3.1 显示额外的异常信息#

如果你需要在调用接口而产生异常的时候展示异常的详细信息,可以通过在启动模块的 PreInitialize() (预加载方法) 当中加入 Configuration.Modules.AbpWebCommon().SendAllExceptionsToClients = true; 即可,例如:


  
  
Copy
[DependsOn(typeof(AbpAspNetCoreModule))] public class TestWebStartupModule : AbpModule { public override void PreInitialize() { Configuration.Modules.AbpWebCommon().SendAllExceptionsToClients = true; } }

3.2 监听异常事件#

使用 Abp 框架的时候,你可以随时通过监听 AbpHandledExceptionData 事件来使用自己的逻辑处理产生的异常。比如说产生异常时向监控服务报警,或者说将异常信息持久化到其他数据库等等。

你只需要编写如下代码即可实现监听异常事件:


  
  
Copy
public class ExceptionEventHandler : IEventHandler<AbpHandledExceptionData>, ITransientDependency { /// <summary> /// Handler handles the event by implementing this method. /// </summary> /// <param name="eventData">Event data</param> public void HandleEvent(AbpHandledExceptionData eventData) { Console.WriteLine($"当前异常信息为:{eventData.Exception.Message}"); } }

如果你觉得看的有点吃力的话,可以跳转到 这里 了解 Abp 的事件总线实现。

4.点此跳转到总目录#

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
权限验证在 Abp 框架中扮演着非常重要的角色。Abp 的权限验证机制支持多种类型的验证方式,包括角色权限、资源权限、特性权限等。 在 Abp 中,权限验证是通过 IPermissionChecker 接口实现的。该接口中定义了多个方法,如 IsGrantedAsync、IsGranted、CheckAsync、Check 等。这些方法可以根据不同的权限验证需求进行调用,并且可以通过依赖注入的方式在应用程序代码中调用。 在实际应用中,我们可以通过使用 Authorization 属性来限制用户对于某些操作的访问权限。例如: ```csharp [Authorize(AppPermissions.Pages_Administration_Roles)] public class RolesController : AbpController { // ... } ``` 在上面的代码中,我们使用 [Authorize] 属性来限制用户对于 RolesController 的访问权限,只有拥有 AppPermissions.Pages_Administration_Roles 权限的用户才能够访问该控制器。 除了使用属性来限制访问权限外,我们还可以通过在应用程序代码中直接调用 IPermissionChecker 接口的方法来进行权限验证。例如: ```csharp public class MyService : ITransientDependency { private readonly IPermissionChecker _permissionChecker; public MyService(IPermissionChecker permissionChecker) { _permissionChecker = permissionChecker; } public async Task DoSomethingAsync() { if (await _permissionChecker.IsGrantedAsync(AppPermissions.Pages_Administration_Users)) { // 用户拥有 Pages_Administration_Users 权限 } else { // 用户没有 Pages_Administration_Users 权限 } } } ``` 在上面的代码中,我们通过依赖注入的方式获取了 IPermissionChecker 接口的实例,并在 DoSomethingAsync 方法中调用了 IsGrantedAsync 方法进行权限验证。 总的来说,Abp 框架提供了非常灵活、易用的权限验证机制,可以满足大多数应用程序的权限验证需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值