在ASP.NET Core中的Controllers上下文外部重定向 [英] Redirect outside of the Controllers context in ASP.NET Core

查看:79
本文介绍了在ASP.NET Core中的Controllers上下文外部重定向的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不知道这是否真的可能,但是我认为值得一试.

I do not know if this is actually possible, but I think it' s worth a try to find out.

也许还有其他更好的模式(如果您知道,让我知道,我会查一下),但是我很好奇这是否可行.

There are maybe other and better patterns (if you know one let me know, I will look them up) to do this, but I'm just curious to know if this is possible.

当您必须调用API时,可以使用 HttpClient 在控制器内直接执行以下操作:

When you have to call an API you could do it directly from within the controller using the HttpClient like this:

    [Authorize]
    public async Task<IActionResult> Private()
    {
        //Example: get some access token to use in api call
        var accessToken = await HttpContext.GetTokenAsync("access_token");

        //Example: do an API call direcly using a static HttpClient wrapt in a service
        var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/api/some/endpoint");
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        var response = await _client.Client.SendAsync(request);

        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            //Handle situation where user is not authenticated
            var rederectUrl = "/account/login?returnUrl="+Request.Path;
            return Redirect(rederectUrl);
        }

        if (response.StatusCode == HttpStatusCode.Forbidden)
        {
            //Handle situation where user is not authorized
            return null;
        }

        var text = await response.Content.ReadAsStringAsync();

        Result result = JObject.Parse(text).ToObject<Result>();

        return View(result);
    }

执行此操作时,您将不得不一遍又一遍地重用一些代码.您可能只创建了一个存储库,但是对于某些场景而言,这可能会显得过大,而您只想进行一些快速而肮脏的API调用.

When you would do this you'll have to reuse some code over and over again. You could just make a Repository but for some scenarios that would be overkill and you just want to make some quick and dirty API calls.

现在我想知道的是,当我们将设置Authorization标头的逻辑或在控制器外部处理401和403响应的逻辑时,如何重定向或控制控制器的动作.

Now what I want to know is, when we move the logic of setting an Authorization header or handling the 401 and 403 responses outside the controller, how do you redirect or control the controller's action.

让我们说我为HttpClient创建一个中间件,如下所示:

Lets say I create a Middleware for the HttpClient like this:

public class ResourceGatewayMessageHandler : HttpClientHandler
{
    private readonly IHttpContextAccessor _contextAccessor;

    public ResourceGatewayMessageHandler(IHttpContextAccessor context)
    {
        _contextAccessor = context;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        //Retrieve acces token from token store
        var accessToken = await _contextAccessor.HttpContext.GetTokenAsync("access_token");

        //Add token to request
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

        //Execute request
        var response = await base.SendAsync(request, cancellationToken);

        //When 401 user is probably not logged in any more -> redirect to login screen
        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            //Handle situation where user is not authenticated
            var context = _contextAccessor.HttpContext;
            var rederectUrl = "/account/login?returnUrl="+context.Request.Path;
            context.Response.Redirect(rederectUrl); //not working
        }

        //When 403 user probably does not have authorization to use endpoint
        if (response.StatusCode == HttpStatusCode.Forbidden)
        {
            //Handle situation where user is not authorized
        }

        return response;
    }

}

我们可以这样发出请求:

We can just do the request like this:

    [Authorize]
    public async Task<IActionResult> Private()
    {
        //Example: do an API call direcly using a static HttpClient initiated with Middleware wrapt in a service
        var response = await _client.Client.GetAsync("https://example.com/api/some/endpoint");

        var text = await response.Content.ReadAsStringAsync();

        Result result = JObject.Parse(text).ToObject<Result>();

        return View(result);
    }

这里的问题是 context.Response.Redirect(rederectUrl); 不起作用.它不会中断重定向流程.有可能实现这一点,您将如何解决呢?

The problem here is that context.Response.Redirect(rederectUrl); does not work. It does not break off the flow to redirect. Is it possible to implement this, and how would you solve this?

推荐答案

好吧,因为没有人回答我的问题,我对此进行了彻底的思考,并提出了以下建议:

Ok since nobody answers my question I've thought about it thoroughly and I came up with the following:

我们有一个资源网关(RG).RG可以返回401或403,表示会话已过期(401)或用户没有足够的权限(403).我们使用访问令牌(AT)对向RG的请求进行身份验证和授权.

We have a resource gateway (RG). The RG can return a 401 or 403 meaning that the session is expired (401) or the user does not have sufficient rights (403). We use an access token (AT) to authenticate and authorize our requests to the RG.

当我们得到401并且有刷新令牌(RT)时,我们想触发一些将检索新AT的东西.当没有RT或RT过期时,我们要重新认证用户.

When we get a 401 and we have a refresh token (RT) we want to trigger something that will retrieve a new AT. When there is no RT or the RT is expired we want to reauthenticate the user.

当我们收到403时,我们想向用户显示他无权访问或类似的内容.

When we get a 403 we want to show the user that he has no access or something similar like that.

要处理上述问题,而又不会给使用API​​或API包装器类的程序员带来麻烦,我们可以使用中间件,该中间件将专门处理使用API​​或API包装器引发的异常.中间件可以处理以上任何情况.

To handle the above, without making it a hassle for the programmer that uses the API or API wrapper class we can use a Middleware that will specifically handle the Exception thrown by using the API or the API wrapper. The middleware can handle any of the above situations.

public class ApiAuthenticationException : Exception
{
    public ApiAuthenticationException()
    {
    }

    public ApiAuthenticationException(string message) : base(message)
    {
    }
}

public class ApiAuthorizationException : Exception
{
    public ApiAuthorizationException()
    {
    }

    public ApiAuthorizationException(string message) : base(message)
    {
    }
}

抛出异常

创建一个包装器或使用HttpClient中间件来管理异常引发.

Throw exceptions

Create a wrapper or use the HttpClient middleware to manage the exception throwing.

public class ResourceGatewayMessageHandler : HttpClientHandler
{
    private readonly IHttpContextAccessor _contextAccessor;

    public ResourceGatewayMessageHandler(IHttpContextAccessor context)
    {
        _contextAccessor = context;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        //Retrieve acces token from token store
        var accessToken = await _contextAccessor.HttpContext.GetTokenAsync("access_token");

        //Add token to request
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

        //Execute request
        var response = await base.SendAsync(request, cancellationToken);

        //When 401 user is probably not logged in any more -> redirect to login screen
        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            throw new ApiAuthenticationException();
        }

        //When 403 user probably does not have authorization to use endpoint -> show error page
        if (response.StatusCode == HttpStatusCode.Forbidden)
        {
            throw new ApiAuthorizationException();
        }

        return response;
    }

}

现在,您必须在 Startup.cs 中设置HttpClient.有多种方法可以做到这一点.我建议使用 AddTransient 初始化将HttpClient用作静态方法的包装器类.

Now you have to setup the HttpClient inside your Startup.cs. There are multiple ways to do this. I advise to use AddTransient to innitiate a wrapper class that uses a HttpClient as a static.

您可以这样做:

public class ResourceGatewayClient : IApiClient
{
    private static HttpClient _client;
    public HttpClient Client => _client;

    public ResourceGatewayClient(IHttpContextAccessor contextAccessor)
    {
        if (_client == null)
        {
            _client = new HttpClient(new ResourceGatewayMessageHandler(contextAccessor));
            //configurate default base address
            _client.BaseAddress = "https://gateway.domain.com/api";
        }
    }
}

ConfigureServices(IServiceCollection服务)内的 Startup.cs 中,您可以执行以下操作:

And in your Startup.cs inside the ConfigureServices(IServiceCollection services) you can do:

 services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
 services.AddTransient<ResourceGatewayClient>();

现在,您可以在所需的任何控制器中使用依赖项注入.

Now you can use the dependency injection in any controller you would like.

创建类似此中间件的内容(感谢此 answer):

Create something like this middleware (with thanks to this answer):

public class ApiErrorMiddleWare
{
    private readonly RequestDelegate next;

    public ApiErrorMiddleWare(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        if (exception is ApiAuthenticationException)
        {
            context.Response.Redirect("/account/login");
        }

        if (exception is ApiAuthorizationException)
        {
            //handle not authorized
        }
    }

注册您的中间件

转到 Startup.cs 并转到 Configure(IApplicationBuilder应用程序,IHostingEnvironment env)方法,然后添加 app.UseMiddleware< ApiErrorMiddleWare>(); .

Register your middleware

Go to Startup.cs and go to the Configure(IApplicationBuilder app, IHostingEnvironment env) method and add app.UseMiddleware<ApiErrorMiddleWare>();.

这应该做到.当前,我正在创建一个示例,当它公开可用时(经过同行评审),我将添加一个github参考.

This should do it. Currently, I'm creating an example when it is publicly available (after peer review) I'll add a github reference.

我想听听一些有关此解决方案或替代方法的反馈.

I would like to hear some feedback on this solution or an alternative approach.

这篇关于在ASP.NET Core中的Controllers上下文外部重定向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆