Конвертировать HttpContext.User перед методом API - PullRequest
0 голосов
/ 05 октября 2018

В настоящее время у меня есть приложение .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, Alex

Ответы [ 2 ]

0 голосов
/ 05 октября 2018

Механизм 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){}
}

вместо IActionFilter для выполнения каждого действия вы можете использовать IAuthorizationFilter, который имеет метод public void OnAuthorization(AuthorizationFilterContext context).Это позволило бы избежать повторного вызова базы данных, но означает ли это, что вы должны где-то кэшировать currentUser, предположительно в Session.

Проблема в том, как получить доступ к вашей базе данных?Если вы идете по пути добавления Глобального фильтра, добавив его в Startup:

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

Тогда вы можете дать свой Filter конструктор и передать в базу данных.Существует также перегрузка для использования системы DependencyInjection.

Если вы не используете метод запуска, вам придется сделать некоторую самостоятельную инъекцию, например, имея статический метод для возврата DbContext.

0 голосов
/ 05 октября 2018

Вы можете переместить эту логику в службу:

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) { ... }

Зарегистрировать ее как службу Scoped в ConfigureServices в Startup.csвместе с IHttpContextAccessor (должно быть синглтоном):

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