Как убедиться, что контроллер и действие существуют перед выполнением перенаправления, asp.net mvc3 - PullRequest
11 голосов
/ 12 августа 2011

В одной из моей пары контроллер + действие я получаю значения другого контроллера и действия в виде строк и хочу перенаправить свое текущее действие.Перед выполнением перенаправления я хочу убедиться, что в моем приложении есть действие controller +, если нет, то перенаправить на 404. Я ищу способ сделать это.

public ActionResult MyTestAction()
{
    string controller = getFromSomewhere();
    string action = getFromSomewhereToo();

    /*
      At this point use reflection and make sure action and controller exists
      else redirect to error 404
    */ 

    return RedirectToRoute(new { action = action, controller = controller });
}

Все, что я сделал, это, но это не работает.

var cont = Assembly.GetExecutingAssembly().GetType(controller);
if (cont != null && cont.GetMethod(action) != null)
{ 
    // controller and action pair is valid
}
else
{ 
    // controller and action pair is invalid
}

Ответы [ 4 ]

8 голосов
/ 12 августа 2011

Вы можете реализовать IRouteConstraint и использовать его в своей таблице маршрутов.

Реализация этого ограничения маршрута может затем использовать отражение, чтобы проверить, существует ли контроллер / действие. Если он не существует, маршрут будет пропущен. В качестве последнего маршрута в таблице маршрутов вы можете установить маршрут, который будет перехватывать все, и сопоставить его с действием, отображающим представление 404.

Вот фрагмент кода, который поможет вам начать:

public class MyRouteConstraint : IRouteConstraint
    {
        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {

            var action = values["action"] as string;
            var controller = values["controller"] as string;

            var controllerFullName = string.Format("MvcApplication1.Controllers.{0}Controller", controller);

            var cont = Assembly.GetExecutingAssembly().GetType(controllerFullName);

            return cont != null && cont.GetMethod(action) != null;
        }
    }

Обратите внимание, что вам нужно использовать полное имя контроллера.

RouteConfig.cs

routes.MapRoute(
                "Home", // Route name
                "{controller}/{action}", // URL with parameters
                new { controller = "Home", action = "Index" }, // Parameter defaults
                new { action = new MyRouteConstraint() } //Route constraints
            );

routes.MapRoute(
                "PageNotFound", // Route name
                "{*catchall}", // URL with parameters
                new { controller = "Home", action = "PageNotFound" } // Parameter defaults
            );
4 голосов
/ 12 августа 2011

Если вы не можете получить полное имя контроллера для передачи в GetType (), вам нужно использовать GetTypes (), а затем выполнить сравнение строк по результатам.

Type[] types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes();

Type type = types.Where( t => t.Name == controller ).SingleOrDefault();

if( type != null && type.GetMethod( action ) != null )
0 голосов
/ 12 ноября 2018

Мы решили эту проблему, добавив эту строку в наш файл WebApiConfig.cs

config.Services.Replace(typeof(IHttpControllerSelector), new AcceptHeaderControllerSelector(config));

Основной метод, который я использовал, выглядит следующим образом. Этот метод был в классе AcceptHeaderControllerSelector, который расширил интерфейс IHttpControllerSelector.

Причина, по которой я сделал это следующим образом, заключается в том, что мы должны создать версию нашего API, и это был способ создать новый контроллер, например. V2 только с теми методами, которые мы создавали, и просто возвращаемся к V1, если V2 не существует

private HttpControllerDescriptor TryGetControllerWithMatchingMethod(string version, string controllerName, string actionName)
{
    var versionNumber = Convert.ToInt32(version.Substring(1, version.Length - 1));
    while(versionNumber >= 1) { 
        var controllerFullName = string.Format("Namespace.Controller.V{0}.{1}Controller, Namespace.Controller.V{0}", versionNumber, controllerName);
        Type type = Type.GetType(controllerFullName, false, true);

        var matchFound = type != null && type.GetMethod(actionName) != null;

        if (matchFound)
        {
            var key = string.Format(CultureInfo.InvariantCulture, "V{0}{1}", versionNumber, controllerName);
            HttpControllerDescriptor controllerDescriptor;
            if (_controllers.TryGetValue(key, out controllerDescriptor))
            {
                return controllerDescriptor;
            }
        }
        versionNumber--;
    }

    return null;
}

Полный файл можно увидеть ниже:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.Routing;

namespace WebApi
{

    public class AcceptHeaderControllerSelector : IHttpControllerSelector
    {
        private const string ControllerKey = "controller";
        private const string ActionKey = "action";
        private const string VersionHeaderValueNotFoundExceptionMessage = "Version not found in headers";
        private const string VersionFoundInUrlAndHeaderErrorMessage = "Version can not be in Header and Url";
        private const string CouldNotFindEndPoint = "Could not find endpoint {0} for api version number {1}";
        private readonly HttpConfiguration _configuration;
        private readonly Dictionary<string, HttpControllerDescriptor> _controllers;

        public AcceptHeaderControllerSelector(HttpConfiguration config)
        {
            _configuration = config;
            _controllers = InitializeControllerDictionary();
        }

        private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
        {
            var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);

            var assembliesResolver = _configuration.Services.GetAssembliesResolver();
            // This would seem to look at all references in the web api project and find any controller, so I had to add Controller.V2 reference in order for it to find them
            var controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();

            var controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);

            foreach (var t in controllerTypes)
            {
                var segments = t.Namespace.Split(Type.Delimiter);

                // For the dictionary key, strip "Controller" from the end of the type name.
                // This matches the behavior of DefaultHttpControllerSelector.
                var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);

                var key = string.Format(CultureInfo.InvariantCulture, "{0}{1}", segments[segments.Length - 1], controllerName);

                dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);
            }

            return dictionary;
        }

        public HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            IHttpRouteData routeData = request.GetRouteData();

            if (routeData == null)  
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            var controllerName = GetRouteVariable<string>(routeData, ControllerKey);
            var actionName = GetRouteVariable<string>(routeData, ActionKey);

            if (controllerName == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            var version = GetVersion(request);

            HttpControllerDescriptor controllerDescriptor;

            if (_controllers.TryGetValue(controllerName, out controllerDescriptor))
            {
                if (!string.IsNullOrWhiteSpace(version))
                {
                    throw new HttpResponseException(request.CreateResponse(HttpStatusCode.Forbidden, VersionFoundInUrlAndHeaderErrorMessage));
                }

                return controllerDescriptor;
            }

            controllerDescriptor = TryGetControllerWithMatchingMethod(version, controllerName, actionName);

            if (controllerDescriptor != null)
            {
                return controllerDescriptor;
            }

            var message = string.Format(CouldNotFindEndPoint, controllerName, version);

            throw new HttpResponseException(request.CreateResponse(HttpStatusCode.NotFound, message));
        }

        private HttpControllerDescriptor TryGetControllerWithMatchingMethod(string version, string controllerName, string actionName)
        {
            var versionNumber = Convert.ToInt32(version.Substring(1, version.Length - 1));
            while(versionNumber >= 1) { 
                var controllerFullName = string.Format("Namespace.Controller.V{0}.{1}Controller, Namespace.Controller.V{0}", versionNumber, controllerName);
                Type type = Type.GetType(controllerFullName, false, true);

                var matchFound = type != null && type.GetMethod(actionName) != null;

                if (matchFound)
                {
                    var key = string.Format(CultureInfo.InvariantCulture, "V{0}{1}", versionNumber, controllerName);
                    HttpControllerDescriptor controllerDescriptor;
                    if (_controllers.TryGetValue(key, out controllerDescriptor))
                    {
                        return controllerDescriptor;
                    }
                }
                versionNumber--;
            }

            return null;
        }

        public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
        {
            return _controllers;
        }

        private string GetVersion(HttpRequestMessage request)
        {
            IEnumerable<string> values;
            string apiVersion = null;

            if (request.Headers.TryGetValues(Common.Classes.Constants.ApiVersion, out values))
            {
                apiVersion = values.FirstOrDefault();
            }

            return apiVersion;
        }

        private static T GetRouteVariable<T>(IHttpRouteData routeData, string name)
        {
            object result = null;
            if (routeData.Values.TryGetValue(name, out result))
            {
                return (T)result;
            }
            return default(T);
        }
    }
}
0 голосов
/ 12 августа 2011

Отражение - дорогостоящая операция.

Вы действительно должны тестировать эти методы, чтобы убедиться, что они перенаправляются на соответствующее действие и контроллер.

например. (NUnit)

[Test]
public void MyTestAction_Redirects_To_MyOtherAction()
{
  var controller = new MyController();

  var result = (RedirectToRouteResult)controller.MyTestAction();

  Assert.That(result.RouteValues["action"], Is.EqualTo("MyOtherAction");
  Assert.That(result.RouteValues["controller"], Is.EqualTo("MyOtherController");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...