代码之家  ›  专栏  ›  技术社区  ›  Ingweland

如何在asp.net core 3的blazor页面中正确重用具有授权的控制器

  •  0
  • Ingweland  · 技术社区  · 5 年前

    先决条件:

    1. 单个ASP.NET Core 3 Preview 8项目,包括Blazor服务器端和Web API控制器
    2. ASP.NET核心标识
    3. 浏览器中的授权逻辑以及从移动客户端调用api时的cookies和jwt令牌身份验证方案
    4. 为了简洁起见,这里的所有代码示例都有最少的代码量。

    以下是初始blazor页面、模型和初始控制器

    @page "/fetchdata"
    @attribute [Authorize]
    @using BlazorApp2.Controllers
    
    <button @onclick="@callController">Call controller</button>
    
    @code {
        private async Task callController()
        {
        }
    }
    
    public class InputModel
    {
        [Required]
        public string Message { get; set; }
    }
    
    [Route("api/[controller]")]
    [ApiController]
    [Authorize]
    public class MessageController : ControllerBase
    {
        [HttpPost("createMessage")]
        public async Task<ActionResult<string>> CreateMessage(InputModel model)
        {
            var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
            return $"{model.Message} {userId}";
        }
    }
    

    以“正常”的方式使用控制器(通过发送http请求)几乎没有内置的好处:自动授权;模型验证;填充httpcontext和用户属性(以及其他)。

    目标是在获取blazor页面的数据时重用控制器。

    想法1

    配置控制器并将其注入blazor页面,以便其操作的调用方式与http请求期间的调用方式类似。不知道如何实现这一点,以及这是否真的可能。用DI容器注册控制器不是问题( AddControllersAsServices() )但这不会填充控制器的 HttpContext User 注入期间的特性。我知道唯一的办法 请求上下文 控制器注入后 IHttpContextAccessor 在里面 Startup ( services.AddHttpContextAccessor(); )然后注入控制器。但这也意味着当使用http请求访问控制器时, 请求上下文 可从两个注入的 IHttpContextAccessor接口 和控制器的 请求上下文 财产。有什么可能的问题吗?此解决方案也不会在控制器中进行模型验证。同时,模型可以在从blazor页面发送之前进行验证,但我不确定在这种情况下是否需要在controller中手动重新验证它。

    
    [Route("api/[controller]")]
    [ApiController]
    [Authorize]
    public class MessageController : ControllerBase
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
    
        public MessageController(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
    
        [HttpPost("createMessage")]
        public async Task<ActionResult<string>> CreateMessage(InputModel model)
        {
            var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
            return $"{model.Message} {userId}";
        }
    }
    
    
    @page "/fetchdata"
    @using BlazorApp2.Controllers
    @attribute [Authorize]
    @inject MessageController MessageController;
    
    <button @onclick="@callController">Call controller</button>
    
    @code {
    
        private async Task callController()
        {
            var result = (await MessageController
                .CreateMessage(new InputModel {Message = "Hello"})).Value;
        }
    }
    

    想法2

    使用httpclient向控制器发出http请求。这里有两个问题。a)不知道如何登记 HttpClient 以这种方式在DI容器中,所有必需的身份验证cookie将自动添加到每个调用;b)从性能角度来看,可能不是理想的解决方案,因为调用将离开服务器应用程序(在Blazor页面中)并返回到同一个应用程序(在控制器中)。

    怎么走?还有其他更好的办法吗?我的想法漏掉了什么?

    0 回复  |  直到 5 年前
        1
  •  0
  •   enet    5 年前

    您可以将http请求与web api一起使用,也可以定义直接访问服务器资源的服务。我看不出这两种方法有什么有意义的区别。就我个人而言,我会选择加入httpclient服务。

    在我看来,将控制器注入blazor页面的想法是荒谬的,但却是可行的。实际上,如果您决定创建一个服务而不是一个web api,并将其注入blazor组件,那么结果可能与将控制器注入blazor页面非常相似。

    这里有问题。a)不知道如何在di容器中注册httpclient 以这种方式,将添加所有必需的auth cookies 自动呼叫;

    您应该使用ihttpclientfactory来发布httpclient objets。如果使用jwt身份验证,则必须将jwt令牌存储在某种缓存(如本地存储)中,在需要时检索它,将其添加到请求头中,然后将其传递到服务器,如身份验证…在我看来,你好像没有听说过blazor auth系统,是吗?

    您可以而且应该在blazor中使用标识ui。您从Razor页面了解到,Identity UI可以在Blazor中用于注册、登录、注销、用户等。它与Blazor组件协同工作,这些组件旨在为您提供访问Blazor中用户凭据、授权等功能……请看这里: https://gist.github.com/SteveSandersonMS/175a08dcdccb384a52ba760122cd2eda

    希望这有帮助,如果我不完全清楚的话,请道歉。我发了这个半睡半醒

    推荐文章