C# 在ASP.NET核心中的控制器上下文外部重定向
我不知道这是否真的可能,但我认为值得一试 也许还有其他更好的模式(如果你知道一个,请告诉我,我会查找它们)可以做到这一点,但我只是想知道这是否可能 当您必须调用API时,可以使用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
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引用
我想听听有关此解决方案或替代方法的一些反馈。这看起来很有趣,值得一看。