3.0之前,我可以通过访问HttpRequest
的HttpContext
属性,然后更改Path
的值来更改请求的路径(无需任何形式的浏览器重定向)。
作为示例,为需要更改密码的用户显示页面(与用户打算访问的页面无关),我扩展了[[HttpContext
public static void ChangeDefaultPassword(this HttpContext context)
=> context.Request.Path = "/Account/ChangePassword";
这段代码将用户带到用户打算访问的操作方法。然后进入dotnet core 3.1。ChangePassword
中的操作方法AccountController
。不执行
在3.1中,扩展方法更改路径。但是,它从不执行action方法。它
忽略
更新的路径。 我知道这是由于路由中的更改所致。现在可以使用扩展方法HttpContext.GetEndpoint()
访问该端点。还有一个扩展方法HttpContext.SetEndpoint
,它似乎是设置新端点的正确方法。但是,没有有关如何完成此操作的示例。问题
如何更改请求路径,而不执行原始路径?我尝试过的事情
context.Response.Redirect("/Account/ChangePassword");
重定向。此方法有效,但是它HttpContext.SetEndpoint
,但没有可用的示例。 protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, FooBarRequirement requirement) {
var hasAccess = await requirement.CheckAccess(context.User);
if (hasAccess)
context.Succeed(requirement);
else {
var message = "You do not have access to this Foobar function";
AuthorizeHandler.NoPermission(mHttpContextAccessor.HttpContext, context, requirement, message);
}
}
我写了一个静态类来处理重定向,传入控制器和操作所需的url以及错误消息,并将重定向永久标志设置为true:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
namespace Foo.BusinessLogic.Security {
public static class AuthorizeHandler {
public static void NoPermission(HttpContext httpContext,
AuthorizationHandlerContext context, IAuthorizationRequirement requirement, string
errorMessage) {
context.Succeed(requirement);
httpContext.Response.Redirect($"/home/nopermission/?m={errorMessage}", true);
}
}
}
最后,是处理视图和消息的控制器和动作
[AllowAnonymous]
public IActionResult NoPermission(string m) {
return View("NoPermission", m);
}
}
private static void RedirectToPath(this HttpContext context, string controllerName, string actionName )
{
// Get the old endpoint to extract the RequestDelegate
var currentEndpoint = context.GetEndpoint();
// Get access to the action descriptor collection
var actionDescriptorsProvider =
context.RequestServices.GetRequiredService<IActionDescriptorCollectionProvider>();
// Get the controller aqction with the action name and the controller name.
// You should be redirecting to a GET action method anyways. Anyone can provide a better way of achieving this.
var controllerActionDescriptor = actionDescriptorsProvider.ActionDescriptors.Items
.Where(s => s is ControllerActionDescriptor bb
&& bb.ActionName == actionName
&& bb.ControllerName == controllerName
&& (bb.ActionConstraints == null
|| (bb.ActionConstraints != null
&& bb.ActionConstraints.Any(x => x is HttpMethodActionConstraint cc
&& cc.HttpMethods.Contains(HttpMethods.Get)))))
.Select(s => s as ControllerActionDescriptor)
.FirstOrDefault();
if (controllerActionDescriptor is null) throw new Exception($"You were supposed to be redirected to {actionName} but the action descriptor could not be found.");
// Create a new route endpoint
// The route pattern is not needed but MUST be present.
var routeEndpoint = new RouteEndpoint(currentEndpoint.RequestDelegate, RoutePatternFactory.Parse(""), 1, new EndpointMetadataCollection(new object[] { controllerActionDescriptor }), controllerActionDescriptor.DisplayName);
// set the new endpoint. You are assured that the previous endpoint will never execute.
context.SetEndpoint(routeEndpoint);
}
重要
您必须将操作方法的视图放置在Shared
文件夹中,以使其可用。或者,您可以决定提供IViewLocationExpander
的自定义实现访问端点之前,路由中间件必须已执行。