Shi*_*nga 5 url-routing .net-core .net-core-3.1 asp.net-core-3.1
在 3.0 之前,我可以通过访问 的HttpRequest属性HttpContext然后更改Path.
例如,要为需要更改密码的用户显示一个页面(无论用户打算访问哪个页面),我扩展了HttpContext
public static void ChangeDefaultPassword(this HttpContext context)
=> context.Request.Path = "/Account/ChangePassword";
Run Code Online (Sandbox Code Playgroud)
这段代码将用户带入到 action 方法ChangePassword中,AccountController 而不执行用户打算访问的 action 方法。
然后进入dotnet core 3.1。
在3.1中,扩展方法改变了路径。但是,它从不执行 action 方法。它忽略更新的路径。
我知道这是由于路由的变化。现在可以使用扩展方法访问端点HttpContext.GetEndpoint()。还有一个扩展方法HttpContext.SetEndpoint似乎是设置新端点的正确方法。但是,没有关于如何完成此操作的示例。
问题
如何在不执行原始路径的情况下更改请求路径?
我试过的
context.Response.Redirect("/Account/ChangePassword");. 这有效,但它首先执行用户请求的原始操作方法。这种行为违背了初衷。HttpContext.SetEndpoint,但没有可用的示例。我解决这个问题的方法是EndpointDataSource直接使用,这是一个单例服务,只要您注册了路由服务,就可以从 DI 获得该服务。只要您可以提供控制器名称和操作名称(可以在编译时指定),它就可以工作。这不需要IActionDescriptorCollectionProvider您自己使用或构建端点对象或请求委托(这非常复杂......):
public static void RerouteToActionMethod(this HttpContext context, EndpointDataSource endpointDataSource, string controllerName, string actionName)
{
var endpoint = endpointDataSource.Endpoints.FirstOrDefault(e =>
{
var descriptor = e.Metadata.GetMetadata<ControllerActionDescriptor>();
// you can add more constraints if you wish, e.g. based on HTTP method, etc
return descriptor != null
&& actionName.Equals(descriptor.ActionName, StringComparison.OrdinalIgnoreCase)
&& controllerName.Equals(descriptor.ControllerName, StringComparison.OrdinalIgnoreCase);
});
if (endpoint == null)
{
throw new Exception("No valid endpoint found.");
}
context.SetEndpoint(endpoint);
}
Run Code Online (Sandbox Code Playgroud)
我找到了一个可行的解决方案。我的解决方案通过使用扩展方法手动设置新端点来工作SetEndpoint。
这是我为解决此问题而创建的扩展方法。
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);
}
Run Code Online (Sandbox Code Playgroud)
重要的
IViewLocationExpander用法
public static void ChangeDefaultPassword(this HttpContext context)
=> context.RedirectToPath("Account","ChangePassword");
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5865 次 |
| 最近记录: |