【问题标题】:ActionFilters for getting the user ID and returning error if it is nullActionFilters 用于获取用户 ID 并在为 null 时返回错误
【发布时间】:2020-02-05 04:33:13
【问题描述】:

我有一个基类,我的 (api) 控制器从该基类继承。 我这样做是为了让我可以从我的身份验证提供者那里获取用户 ID,然后用它来处理用户、更新它或获取它的数据等。

public class BaseController : ControllerBase
{
    protected readonly IBaseData _baseData;

    public BaseController(IBaseData baseData)
    {
        _baseData = baseData;
    }

    public Guid GetUserId()
    {
        string nameIdentifier = User.FindFirst(ClaimTypes.NameIdentifier).Value;
        Guid? userId = _baseData.GetInvestorId(nameIdentifier);

        return userId != null ? (Guid)userId : Guid.Empty;
    }
}

然后我在我的 API 端点中调用它:

Guid userId = GetUserId();

BaseModel m = _userData.GetBaseModel(userId);

return Ok(m);

很简单。它在控制器中的多个位置被调用。 不理想但工作正常。 但是现在我需要捕获一个错误,该错误有时会在用户不在数据库中时发生。 我可以像这样向 API 端点添加一些代码:

Guid userId = GetUserId();

        if (userId == Guid.Empty)
            return NotFound(new ResponseGapiModel { Response = Response.NotFound, Message = "user not found in DB" });

        BaseModel m = _userData.GetBaseModel(userId);

        return Ok(m);

但这意味着我会到处重复很多代码。 我一直在尝试使用动作过滤器。但我无法理解它。 我不知道如何在 actionfilter 中传递参数,比如我需要找到用户的名称标识符。也没有坦率地说如何将 ID 传回。

-更新-

我现在已经设法让 actionfilter 在找不到用户时返回失败的结果,所以我需要的一半有效。问题是现在我两次调用 DB,因为我仍然调用原始 BaseCass GetUserId 来获取要在以后的方法中使用的 ID。

为了让 ActionFilter 检查丢失的用户,我将 datacontext 注入其中:

private readonly NodeDBContext _context;

    public ValidateAuth0UserInDBAttribute(NodeDBContext context)
    {
        _context = context;
    }

以及使用 ActionExecutingContext 中的 HttpContext 来查找我的用户 Nameidentifier:

public void OnActionExecuting(ActionExecutingContext context)
    {
        //check if the user it in DB
        var id = _context.Users.SingleOrDefault(i => i.NameIdentifier == context.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value)?.Id;

        if (id == null)
        {
            context.Result = new NotFoundObjectResult(new ResponseModel { Response = Response.UserNotFound, Message = "User not found in DB" });
            return;
        }
    }

现在的问题是如何将“id”从这里传递回我的控制器?有办法吗?还是我必须调用数据库两次?

【问题讨论】:

  • 您不会将参数传递给动作过滤器(在 ctor 之外)。动作过滤器从请求中获取(部分)其参数。例如,您可以从查询字符串或表单数据中获取userId
  • 也许您可以在您的BaseController.GetUserId() 方法中使用throw new HttpResponseException(HttpStatusCode.Status401Unauthorized) 而不是return Guid.Empty
  • @maxbeaudoin 我明白了,你的意思,所以只有请求中的参数可用。我使用上下文本身进行了一些更改以检索我需要的用户 ID。但是如果没有失败,如何将信息返回到我的控制器时遇到了一些麻烦。
  • @vasily.sib 我正在尝试创建动作过滤器,这样我就不必抛出异常并在任何地方进行尝试和捕获
  • 我的意思是 - 不要试图抓住它,这应该在结果中返回 404

标签: c# asp.net-core action-filter


【解决方案1】:

现在的问题是如何将“id”从这里传回我的控制器?

尝试使用以下代码将 id 传回控制器:

public class MyActionFilter : Attribute,IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        var id = _context.Users.SingleOrDefault(i => i.NameIdentifier == context.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value)?.Id;
        if (id == null)
        {
           context.Result = new NotFoundObjectResult(new ResponseModel { Response = Response.UserNotFound, Message = "User not found in DB" });
           return;
        }

        var controller = (ControllerBase)context.Controller;
        controller.HttpContext.Items.Add("CurrentUserId", id );

    }
    public void OnActionExecuted(ActionExecutedContext context) { }
}

行动:

[MyActionFilter]
public IActionResult Get()
    {
        var id = HttpContext.Items["CurrentUserId"]?.ToString();
        //...
    }

【讨论】:

  • 谢谢,这正是我所需要的。
猜你喜欢
  • 1970-01-01
  • 2021-07-20
  • 1970-01-01
  • 1970-01-01
  • 2017-10-10
  • 2021-03-06
  • 1970-01-01
  • 2013-07-27
  • 1970-01-01
相关资源
最近更新 更多