C# 在ASP.NET核心中的控制器上下文外部重定向

C# 在ASP.NET核心中的控制器上下文外部重定向,c#,asp.net,asp.net-core-mvc,middleware,C#,Asp.net,Asp.net Core Mvc,Middleware,我不知道这是否真的可能,但我认为值得一试 也许还有其他更好的模式(如果你知道一个,请告诉我,我会查找它们)可以做到这一点,但我只是想知道这是否可能 当您必须调用API时,可以使用HttpClient在控制器内直接调用,如下所示: [Authorize] public async Task<IActionResult> Private() { //Example: get some access token to use in api call

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

也许还有其他更好的模式(如果你知道一个,请告诉我,我会查找它们)可以做到这一点,但我只是想知道这是否可能

当您必须调用API时,可以使用
HttpClient
在控制器内直接调用,如下所示:

    [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);
    }
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;
    }

}
    [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);
    }
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";
        }
    }
}
[授权]
公共异步任务专用()
{
//示例:获取一些用于api调用的访问令牌
var accessToken=await HttpContext.GetTokenAsync(“访问令牌”);
//示例:在服务中使用静态HttpClient wrapt直接执行API调用
var request=newhttprequestmessage(HttpMethod.Get)https://example.com/api/some/endpoint");
request.Headers.Authorization=新的AuthenticationHeaderValue(“承载者”,accessToken);
var response=wait_client.client.SendAsync(请求);
if(response.StatusCode==HttpStatusCode.Unauthorized)
{
//处理用户未经身份验证的情况
var redecturl=“/account/login?returnUrl=“+Request.Path;
返回重定向(redecturl);
}
if(response.StatusCode==HttpStatusCode.probled)
{
//处理用户未经授权的情况
返回null;
}
var text=await response.Content.ReadAsStringAsync();
Result=JObject.Parse(text.ToObject();
返回视图(结果);
}
当您要这样做时,您将不得不一次又一次地重用一些代码。您可以只创建一个存储库,但对于某些场景来说,这可能会有点过头,您只需要进行一些快速而肮脏的API调用

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

假设我为HttpClient创建一个中间件,如下所示:

    [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);
    }
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;
    }

}
    [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);
    }
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";
        }
    }
}
公共类ResourceGatewayMessageHandler:HttpClientHandler
{
专用只读IHttpContextAccessor\u contextAccessor;
公共资源网关MessageHandler(IHttpContextAccessor上下文)
{
_contextAccessor=context;
}
受保护的覆盖异步任务SendAsync(HttpRequestMessage请求,CancellationToken CancellationToken)
{
//从令牌存储中检索acces令牌
var accessToken=await_contextAccessor.HttpContext.GetTokenAsync(“access_token”);
//向请求添加令牌
request.Headers.Authorization=新的AuthenticationHeaderValue(“承载者”,accessToken);
//执行请求
var response=await base.sendaync(请求、取消令牌);
//当401用户可能不再登录时->重定向到登录屏幕
if(response.StatusCode==HttpStatusCode.Unauthorized)
{
//处理用户未经身份验证的情况
var context=_contextAccessor.HttpContext;
var redecturl=“/account/login?returnUrl=“+context.Request.Path;
context.Response.Redirect(redirectURL);//不工作
}
//当403用户可能没有使用端点的授权时
if(response.StatusCode==HttpStatusCode.probled)
{
//处理用户未经授权的情况
}
返回响应;
}
}
我们可以这样做:

    [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);
    }
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;
    }

}
    [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);
    }
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";
        }
    }
}
[授权]
公共异步任务专用()
{
//示例:在服务中使用中间件wrapt启动的静态HttpClient直接执行API调用
var response=await\u client.client.GetAsync(“https://example.com/api/some/endpoint");
var text=await response.Content.ReadAsStringAsync();
Result=JObject.Parse(text.ToObject();
返回视图(结果);
}

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

好的,因为没有人回答我的问题,所以我仔细考虑了这个问题,并得出以下结论:

安装程序 我们有一个资源网关(RG)。RG可以返回401或403,这意味着会话已过期(401)或用户没有足够的权限(403)。我们使用访问令牌(AT)对RG的请求进行身份验证和授权

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

授权 当我们得到403时,我们想向用户显示他没有访问权限或类似的东西

解决方案 为了处理上述问题,在不给使用API或API包装类的程序员带来麻烦的情况下,我们可以使用一个中间件,专门处理使用API或API包装引发的异常。中间件可以处理上述任何情况

创建自定义异常 抛出异常 创建包装器或使用HttpClient中间件来管理异常引发

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
中的
ConfigureServices(iSeries收集服务)
中,您可以执行以下操作:

 services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
 services.AddTransient<ResourceGatewayClient>();
注册你的中间件 转到
Startup.cs
并转到
Configure(IApplicationBuilder应用程序,IHostingEnvironment环境)
方法并添加
app.useMidleware()

这应该可以做到。目前,我正在创建一个公开的示例(经过同行评审后),我将添加一个github引用


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

这看起来很有趣,值得一看。