我目前有一个.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>();
}