You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何消除ASP.NET Core REST API中的重复样板代码?

解决ASP.NET Core API中重复权限校验样板代码的方案

针对你遇到的重复校验逻辑问题,以下几种轻量方案可以有效消除样板代码,同时避免过度设计:

方案一:自定义Action过滤器(推荐)

将Workspace加载、权限校验逻辑封装成自定义Action过滤器,只需在需要校验的接口上添加特性即可完成前置校验,校验通过后还能将合法的Workspace实例传递给控制器使用。

实现自定义过滤器

public class WorkspaceAuthorizeAttribute : AsyncActionFilterAttribute
{
    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // 从路由参数解析workspaceId
        if (!context.RouteData.Values.TryGetValue("workspaceId", out var workspaceIdObj) ||
            !Guid.TryParse(workspaceIdObj.ToString(), out var workspaceId))
        {
            context.Result = new BadRequestResult();
            return;
        }

        // 获取DbContext和当前用户邮箱
        var db = context.HttpContext.RequestServices.GetRequiredService<YourDbContext>();
        var userEmail = context.HttpContext.User.Identity?.Name;

        if (string.IsNullOrEmpty(userEmail))
        {
            context.Result = new ForbidResult();
            return;
        }

        // 加载Workspace并校验权限
        var workspace = await db.Workspaces
            .Include(w => w.Members)
            .ThenInclude(m => m.User)
            .FirstOrDefaultAsync(w => w.Id == workspaceId);

        if (workspace == null)
        {
            context.Result = new NotFoundResult();
            return;
        }

        if (!workspace.Members.Any(m => m.User.Email == userEmail))
        {
            context.Result = new ForbidResult();
            return;
        }

        // 将校验通过的Workspace存入HttpContext,供控制器后续使用
        context.HttpContext.Items["AuthorizedWorkspace"] = workspace;

        // 继续执行接口业务逻辑
        await next();
    }
}

控制器中使用

[HttpPost("{workspaceId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[WorkspaceAuthorize] // 只需添加该特性完成前置校验
public async Task<IActionResult> Create(Guid workspaceId, [FromBody] AppointmentCreateModel model)
{
    // 直接获取已校验的Workspace实例
    var workspace = (Workspace)HttpContext.Items["AuthorizedWorkspace"]!;

    var appointment = new Appointment
    {
        WorkspaceId = workspaceId,
        ContactName = model.ContactName,
        OriginalName = model.ContactName,
        AppointmentTime = model.AppointmentTime,
        Source = AppointmentSource.Dashboard,
        CreatedAt = DateTime.UtcNow,
    };

    db.Appointments.Add(appointment);
    await db.SaveChangesAsync();

    return Ok(new AppointmentDto(appointment));
}

方案二:封装扩展方法

如果不想使用过滤器,可以将校验逻辑封装成DbContext的扩展方法,在控制器中直接调用即可。

扩展方法实现

public static class WorkspaceAuthorizationExtensions
{
    public static async Task<IActionResult?> ValidateWorkspaceAccessAsync(this YourDbContext db, 
        Guid workspaceId, ClaimsPrincipal user, ControllerBase controller)
    {
        var userEmail = user.Identity?.Name;
        if (string.IsNullOrEmpty(userEmail))
        {
            return controller.Forbid();
        }

        var workspace = await db.Workspaces
            .Include(w => w.Members)
            .ThenInclude(m => m.User)
            .FirstOrDefaultAsync(w => w.Id == workspaceId);

        if (workspace == null)
        {
            return controller.NotFound();
        }

        if (!workspace.Members.Any(m => m.User.Email == userEmail))
        {
            return controller.Forbid();
        }

        // 校验通过返回null,控制器继续执行业务逻辑
        return null;
    }
}

控制器中调用

[HttpPost("{workspaceId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Create(Guid workspaceId, [FromBody] AppointmentCreateModel model)
{
    var validationResult = await db.ValidateWorkspaceAccessAsync(workspaceId, User, this);
    if (validationResult != null)
    {
        return validationResult;
    }

    // 后续业务逻辑...
}

方案三:基类控制器

如果多个控制器都需要Workspace校验逻辑,可以创建一个基类控制器,将校验方法封装在基类中,业务控制器继承该基类即可。

基类控制器实现

public class WorkspaceBaseController : ControllerBase
{
    protected readonly YourDbContext db;

    public WorkspaceBaseController(YourDbContext db)
    {
        this.db = db;
    }

    protected async Task<IActionResult?> ValidateWorkspaceAccessAsync(Guid workspaceId)
    {
        var userEmail = User.Identity?.Name;
        if (string.IsNullOrEmpty(userEmail))
        {
            return Forbid();
        }

        var workspace = await db.Workspaces
            .Include(w => w.Members)
            .ThenInclude(m => m.User)
            .FirstOrDefaultAsync(w => w.Id == workspaceId);

        if (workspace == null)
        {
            return NotFound();
        }

        if (!workspace.Members.Any(m => m.User.Email == userEmail))
        {
            return Forbid();
        }

        return null;
    }
}

业务控制器继承基类

public class AppointmentsController : WorkspaceBaseController
{
    public AppointmentsController(YourDbContext db) : base(db)
    {
    }

    [HttpPost("{workspaceId}")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> Create(Guid workspaceId, [FromBody] AppointmentCreateModel model)
    {
        var validationResult = await ValidateWorkspaceAccessAsync(workspaceId);
        if (validationResult != null)
        {
            return validationResult;
        }

        // 后续业务逻辑...
    }
}

扩展说明

如果后续其他实体也需要类似的权限校验,可以将上述方案扩展为通用型(比如通过泛型、接口定义校验规则),当前规模下先解决Workspace的重复问题即可。

内容的提问来源于stack exchange,提问作者dinikai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 00:43:09