Изменение пути запроса в. Net Core 3.1 - PullRequest
0 голосов
/ 05 февраля 2020

До версии 3.0 я мог изменить путь запроса (без какой-либо формы перенаправления браузера), просто перейдя к свойству HttpRequest HttpContext, а затем изменив значение Path.

Например, чтобы отобразить страницу для пользователя, которому нужно было изменить свой пароль (независимо от страницы, которую пользователь намеревался посетить), я расширил HttpContext

public static void ChangeDefaultPassword(this HttpContext context) 
=> context.Request.Path = "/Account/ChangePassword";

Этот фрагмент кода приводит пользователя к методу действия ChangePassword в AccountController без выполнения метода действия, который пользователь намерен посетить.

Затем ввод do do tnet core 3.1.

В 3.1 метод расширения изменяет путь. Тем не менее, он никогда не выполняет метод действия. игнорирует обновленный путь.

Я знаю, что это связано с изменениями в маршрутизации. Теперь к конечной точке можно получить доступ с помощью метода расширения HttpContext.GetEndpoint(). Существует также метод расширения HttpContext.SetEndpoint, который, кажется, является правильным способом установки новой конечной точки. Однако нет примера того, как это сделать sh.

Вопрос

Как изменить путь запроса без выполнения исходного пути?

Что я пробовал

  1. Я попытался изменить путь. Кажется, что маршрутизация в do tnet core 3.1 игнорирует значение пути HttpRequest.
  2. Я попытался перенаправить с context.Response.Redirect("/Account/ChangePassword");. Это сработало, но first выполнило оригинальный метод действия, запрошенный пользователем. Такое поведение побеждало цель.
  3. Я пытался использовать метод расширения HttpContext.SetEndpoint, но не было доступных примеров для работы.

Ответы [ 2 ]

1 голос
/ 19 февраля 2020

У меня была похожая проблема перенаправления. В моем случае я хочу перенаправить пользователей в представление «у вас нет прав» при сбое AuthorationHandler. Я применил следующий код, в частности (httpContext.Response.Redirect (...)) в (. Net Core 3.1), чтобы направить меня к действию NoPermissions на домашнем контроллере.

В классе обработчика:

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

Я написал класс c stati для обработки перенаправления, передав 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);
 }
}
0 голосов
/ 25 февраля 2020

Мне удалось найти рабочее решение. Мое решение работает путем ручной установки новой конечной точки с помощью метода расширения 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);
    }

Важно

  1. Вы должны сделать доступным представление метода действия, поместив его в папку Shared . В качестве альтернативы вы можете принять решение о предоставлении пользовательской реализации IViewLocationExpander
  2. Перед доступом к конечной точке должно быть выполнено промежуточное программное обеспечение маршрутизации.

USAGE

public static void ChangeDefaultPassword(this HttpContext context) 
=> context.RedirectToPath("Account","ChangePassword");
...