MVC 3 — косые черты и пробелы в маршруте

У меня есть следующий маршрут, определенный в моем Global.asax (веб-проект MVC 3):

routes.MapRoute(
                "BlogCategory", // Route name
                "Blog/Category/{*category}", // URL with parameters
                new { controller = "Blog", action = "Index", category = "" } // Parameter defaults
            );

И мое действие принимает параметр категории, выглядящий так:

   public ViewResult Index(string category, int page = 1)
        {
            PostListViewModel viewModel;
            if (string.IsNullOrEmpty(category))
            { 
....show all cats else show only the one passed in

Это отлично работает и передаст категорию контроллеру, соответствующим образом отфильтровав мои результаты.

Моя проблема в том, что одна из созданных мной категорий выглядит так для названия категории:

Проекты / Лаборатория

(обратите внимание на пробелы и косую черту)

Это создает URL-адрес, подобный этому:

/Блог/Категория/Проекты%20/%20Lab

И когда я перехожу по ссылке, я получаю эту ошибку:

Описание: HTTP 404. Ресурс, который вы ищете (или одна из его зависимостей), мог быть удален, его имя было изменено или он временно недоступен. Просмотрите следующий URL-адрес и убедитесь, что он написан правильно.

Запрошенный URL: /Блог/Категория/Проекты/Лаборатория

Он никогда не достигает действия индекса при отладке.

Мой вопрос в том, как я могу заставить это работать, или я должен выполнить некоторую проверку ввода при создании имен категорий, чтобы этого не произошло?


person Hoecuspocus    schedule 12.12.2011    source источник
comment
Рассматривали ли вы вместо этого передачу идентификатора категории?   -  person Kyeotic    schedule 13.12.2011
comment
Вам определенно нужно сделать обрезку до введенного значения. И не только проверяйте наличие пробелов... некоторые символы не отображаются в URL-адресе (Ñ или другие неанглийские символы)... помните, что существуют и другие языки. Итак, чтобы избежать всего этого... Я предлагаю вам выбрать идентификатор категории.   -  person Romias    schedule 13.12.2011


Ответы (2)


Как предположил Тирсиус, будет проще передавать идентификатор категории, но если вы хотите передать имя, я бы предложил использовать Url.Encode() каждый раз, когда вы создаете ссылку, или создать собственный UrlHelper, чтобы сделать это за вас. Как только он попадет в действие вашего контроллера, просто выполните Url.Decode(), чтобы вернуть исходную строку.

Более чистым способом было бы создать свой собственный обработчик маршрута (реализовать IRouteHandler), чтобы сделать это за вас.

person eth0    schedule 13.12.2011
comment
@Tyrsius Просто интересно, например, при переполнении стека на этой странице - часть имени URL-адреса, кажется, автоматически заменяется / кодируется: mvc-3-slashes-and-spaces-in-route, почему этого не происходит для моего - то есть: Проекты-/-Лаборатория или что-то подобное? - person Hoecuspocus; 13.12.2011

Во-первых, спасибо Tyrsius, Romias и eth0 за их предложения.

Я решил, что не хочу использовать идентификатор для категории и не хочу создавать обработчик маршрута, так как это на самом деле не решает основную проблему.

Вместо этого я создаю атрибут проверки с именем "UsedAsUrl" и применяю его к моему Category.Name в моей модели домена. Это имеет преимущества встроенной проверки (удобно для конечного пользователя) и удобного повторного использования с точки зрения разработчика.

Итак, моя модель категорий теперь выглядит так (обратите внимание на атрибут [UsedAsUrl]):

public class Category
{
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [Required]
        [UsedAsUrl]
        [MaxLength(50, ErrorMessage = "One word or groups of words please")]
        public string Name { get; set; }

        public virtual List<Post> Posts { get; set; }
}

И созданный мной атрибут выглядит так:

using System;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;

namespace CommonWebsiteUtilities.Attributes
{
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class UsedAsUrlAttribute : ValidationAttribute
    {
        public UsedAsUrlAttribute() : base("Must not contain a slash with spaces either side") {}

        public override bool IsValid(object value)
        {
            var input = value.ToString();
            var result = true;

            if (input.Contains("/"))
            {
                input = Regex.Replace(input, @"\s+", " ");
                if (input.Contains(" /")) result = false;
                if (input.Contains("/ ")) result = false;
                if (input.Contains(" / ")) result = false;
            }

            return result;
        }
    }
}

Теперь, когда я иду, чтобы добавить категорию:

добавление категории, которая, как я знаю, не пройдет проверку

Я получаю этот ответ автоматически:

Ошибка ответа при проверке

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

Это в основном проверяет косую черту с пробелами с любой стороны. Обратите внимание, что это не исчерпывающий валидатор URL, но он подойдет для моего текущего проекта. Если у кого-то есть какие-либо улучшения, сообщите мне, и я исправлю этот ответ.

Я не очень хорошо разбираюсь в RegExp, поэтому он не является производным от RegularExpressionAttribute, а также я не хотел предоставлять сообщение об ошибке изнутри моей модели. Этот атрибут действительно следует использовать, так как появляется больше правил при использовании категорий в качестве частей URL.

person Hoecuspocus    schedule 13.12.2011