我目前有一个.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, 亚历克斯
答案 0 :(得分:1)
用于“为每个操作执行操作”的AspNet Mvc机制为Filters。
过滤器可以在调用该方法之前运行,并且可以设置Http.Context.User
。
过滤器可以全局地应用于动作,控制器或(通过在Startup
中编写代码)。
[SwapUserToAuthorizedDatabaseUser]
public class MyController
{
public IActionResult About() => Ok(User);
}
哪个将为Controller上的每个Action调用此过滤器:
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){}
}
您可以使用具有IActionFilter
方法的IAuthorizationFilter
来代替在每个Action上运行的public void OnAuthorization(AuthorizationFilterContext context)
。这样可以省去重复调用数据库的麻烦,但这确实意味着您必须将currentUser
缓存在某个地方,大概在Session
中。
问题是,您如何访问数据库?如果您选择通过在“启动”中添加“全局过滤器”来添加路由的方法:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc(o=>o.Filters.Add(new SwapUserToAuthorizedDatabaseUserAttribute(provide a db instance here)));
}
然后,您可以为Filter提供一个构造函数,并传入数据库。使用DependencyInjection系统还有很多负担。
如果您不使用启动方法,则必须进行一些DIY注入,例如,使用静态方法来返回DbContext
。
答案 1 :(得分:0)
您可以将此逻辑移至服务:
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) { ... }
与IHttpContextAccessor
(应为单例)一起在Startup.cs的ConfigureServices中将其注册为范围服务:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<UserService>();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}