我应该如何从MVC 6视图中访问我的ApplicationUser属性?

Tim*_*ong 19 c# asp.net-core asp.net-core-identity

我正在开发一个ASP.Net vNext/MVC6项目.我正在掌握ASP.Net Identity.

ApplicationUser类显然是在那里我应该添加任何额外的用户属性,这可与实体框架和我的附加属性获取存储在数据库中的预期.

但是,当我想从我的视图中访问当前登录用户的详细信息时,问题就出现了.具体来说,我有一个_loginPartial.cshtml我想要检索并显示用户的Gravatar图标,我需要该电子邮件地址.

Razor View基类有一个User属性,它是一个ClaimsPrincipal.如何从此User属性返回到我的ApplicationUser,以检索我的自定义属性?

请注意,我不是在询问如何查找信息; 我知道如何ApplicationUserUser.GetUserId()值中查找.这是一个关于如何合理地解决这个问题的问题.具体来说,我不想:

  • 从我的视图中执行任何类型的数据库查找(关注点分离)
  • 必须为每个控制器添加逻辑以检索当前用户的详细信息(DRY原则)
  • 必须为每个ViewModel添加User属性.

这似乎是一个"横切关注点",应该有一个集中的标准解决方案,但我觉得我错过了一块拼图游戏.从视图中获取这些自定义用户属性的最佳方法是什么?

注意:似乎MVC团队通过确保UserName属性始终设置为用户的电子邮件地址,在项目模板中侧面解决了这个问题,巧妙地避免了他们执行此查找以获取用户的电子邮件地址!这对我来说似乎有点欺骗,在我的解决方案中,用户的登录名可能是也可能不是他们的电子邮件地址,所以我不能依赖这个技巧(我怀疑还有其他属性我需要稍后访问).

Alf*_*fer 15

我认为您应该为此目的使用User的Claims属性.我找到了很好的帖子:http://benfoster.io/blog/customising-claims-transformation-in-aspnet-core-identity

用户类

public class ApplicationUser : IdentityUser
{
    public string MyProperty { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我们将MyProperty放入经过身份验证的用户声明中.为此,我们重写了UserClaimsPrincipalFactory

public class MyUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public MyUserClaimsPrincipalFactory (
        UserManager<ApplicationUser> userManager,
        RoleManager<IdentityRole> roleManager,
        IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
    {
    }

    public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var principal = await base.CreateAsync(user);

        //Putting our Property to Claims
        //I'm using ClaimType.Email, but you may use any other or your own
        ((ClaimsIdentity)principal.Identity).AddClaims(new[] {
        new Claim(ClaimTypes.Email, user.MyProperty)
    });

        return principal;
    }
}
Run Code Online (Sandbox Code Playgroud)

在Startup.cs中注册我们的UserClaimsPrincipalFactory

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, MyUserClaimsPrincipalFactory>();
    //...
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以像这样访问我们的主题

User.Claims.FirstOrDefault(v => v.Type == ClaimTypes.Email).Value;
Run Code Online (Sandbox Code Playgroud)

我们可以创建一个扩展

namespace MyProject.MyExtensions
{
    public static class MyUserPrincipalExtension
    {
        public static string MyProperty(this ClaimsPrincipal user)
        {
            if (user.Identity.IsAuthenticated)
            {
                return user.Claims.FirstOrDefault(v => v.Type == ClaimTypes.Email).Value;
            }

            return "";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我们应该将@Using添加到View(我将它添加到全局_ViewImport.cshtml)

@using MyProject.MyExtensions
Run Code Online (Sandbox Code Playgroud)

最后,我们可以在任何View中使用此属性作为方法调用

@User.MyProperty()
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您没有额外的数据库查询来获取用户信息.


wil*_*han 15

更新到原始答案:(这违反了操作系统的第一个要求,如果您有相同的要求,请参阅我的原始答案)您可以通过在Razor视图中引用FullName来修改声明并添加扩展文件(在我的原始解决方案中)如:

@UserManager.GetUserAsync(User).Result.FullName
Run Code Online (Sandbox Code Playgroud)

原答案:

这只是这个stackoverflow问题的一个较短的例子,并且在本教程之后.

假设您已经在"ApplicationUser.cs"中设置了属性以及适用的ViewModel和Views进行注册.

使用"FullName"作为额外属性的示例:

将"AccountController.cs"注册方法修改为:

    public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser {
                    UserName = model.Email,
                    Email = model.Email,
                    FullName = model.FullName //<-ADDED PROPERTY HERE!!!
                };
                var result = await _userManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    //ADD CLAIM HERE!!!!
                    await _userManager.AddClaimAsync(user, new Claim("FullName", user.FullName)); 

                    await _signInManager.SignInAsync(user, isPersistent: false);
                    _logger.LogInformation(3, "User created a new account with password.");
                    return RedirectToLocal(returnUrl);
                }
                AddErrors(result);
            }

            return View(model);
        }
Run Code Online (Sandbox Code Playgroud)

然后我添加了一个新文件"Extensions/ClaimsPrincipalExtension.cs"

using System.Linq;
using System.Security.Claims;
namespace MyProject.Extensions
    {
        public static class ClaimsPrincipalExtension
        {
            public static string GetFullName(this ClaimsPrincipal principal)
            {
                var fullName = principal.Claims.FirstOrDefault(c => c.Type == "FullName");
                return fullName?.Value;
            }   
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后在您查看您需要访问属性的位置添加:

@using MyProject.Extensions
Run Code Online (Sandbox Code Playgroud)

并在需要时通过以下方式调用:

@User.GetFullName()
Run Code Online (Sandbox Code Playgroud)

这样做的一个问题是我必须删除我当前的测试用户,然后重新注册,以便查看"FullName",即使数据库中有FullName属性.


Rob*_*Bos 0

您需要使用当前用户的名称进行搜索(例如使用实体框架):

HttpContext.Current.User.Identity.Name
Run Code Online (Sandbox Code Playgroud)