在 API 方法之前转换 HttpContext.User



我目前有一个.Net Core API应用程序,其中包含一堆API get方法。目前在每种方法中,我都需要写这一行:

[ProducesResponseType(200, Type = typeof(MetadataAttributeModel))]
[ProducesResponseType(400, Type = typeof(ValidationResultModel))]
[ProducesResponseType(500, Type = typeof(ErrorResultModel))]
public ActionResult<MetadataAttributeModel> GetAsync(string name)
{
List<Entities.DocumentAttributeView> attributes = documentAttributeViewRepo.GetByAttributeName(name);
SiteUser currentUser = new SiteUser(db, User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value);
return Unauthorized();
}

有没有办法在我开始使用该方法之前将 HttpContext.User 对象转换为我们自己的 SiteUser 对象?我不想在所有 API 方法中编写这一行:

SiteUser currentUser = new SiteUser(db, HttpContext.User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value);

TIA, 亚历克斯

用于"为每个操作执行某些操作"的 AspNet Mvc 机制是过滤器。

过滤器可以在调用方法之前运行,例如,它们可以设置Http.Context.User

过滤器可以应用于操作、控制器或(通过用Startup编写代码(全局。

[SwapUserToAuthorizedDatabaseUser]
public class MyController
{
public IActionResult About() => Ok(User);
}

这将为控制器上的每个操作调用此过滤器:

public class SwapUserToAuthorizedDatabaseUserAttribute : Attribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
SiteUser currentUser = new SiteUser(db, User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value);
if (currentUser == null)
{
context.Result= new RedirectToRouteResult("/Identity/Logout");
}
else
{
var claimsIdentity =
new ClaimsIdentity(
new Claim[]
{
new Claim("Id", currentUser.Id),
new Claim("UserName", currentUser.UserName),
new Claim("WhateverElseYourSiteUserHas", currentUser.Something.ToString()),
}
);
context.HttpContext.User = new ClaimsPrincipal(new[]{claimsIdentity});
}
}
public void OnActionExecuted(ActionExecutedContext context){}
}

如果覆盖HttpContext.User不是您需要的,那么使用的代码要少得多HttpContext.Items

public class SwapUserToAuthorizedDatabaseUserAttribute : Attribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
context.HttpContext.Items["SiteUser"]= new SiteUser(db, User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value);
}
public void OnActionExecuted(ActionExecutedContext context){}
}

您可以使用具有public void OnAuthorization(AuthorizationFilterContext context)方法的IAuthorizationFilter,而不是在每个操作上运行IActionFilter。这将节省重复调用数据库,但确实意味着您必须将currentUser缓存在某个地方,大概是Session.

问题是,您如何访问数据库?如果通过在"启动"中添加全局筛选器来添加全局筛选器:

public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc(o=>o.Filters.Add(new SwapUserToAuthorizedDatabaseUserAttribute(provide a db instance here)));
}

然后,您可以为 Filter 提供一个构造函数并传入一个数据库。使用依赖注入系统还有一个重载。

如果不使用启动方法,则必须进行一些DIY注入,例如通过静态方法返回DbContext

您可以将此逻辑移动到服务:

public class UserService : IUserService
{
private readonly HttpContext context;
private readonly Db db;
public UserService(IHttpContextAccessor context, Db db)
{
this.context = context.HttpContext;
this.db = db;
}
public SiteUser GetUser()
{
return new SiteUser(db, context.User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value);
}
}

在需要时将其注入控制器:

public MyController(IUserService userService) { ... }

在启动时的配置服务中将其注册为作用域服务.cs以及IHttpContextAccessor(应为单例(:

public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<UserService>();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}

最新更新