MVC 3 - Cross-sections and spaces in the route

I have the following route defined in my Global.asax (MVC 3 web project):

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

And my action takes a category parameter that looks like this:

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

This works fine and will pass the category through the controller, filtering my results accordingly.

My problem is that one of the categories I created looks like this:

Projects / Laboratory

(pay attention to spaces and slashes)

This creates a URL similar to this:

/ Blog / Category / Projects% 20 /% 20Lab

And when I follow the link, I get this error:

Description: HTTP 404. The resource you are looking for (or one of its dependencies) could be deleted if their name was changed or Temporarily unavailable. Look at the following URL and make sure it is spelled correctly.

Requested URL: / Blog / Category / Projects / Laboratory

It never reaches the index when debugging.

My question is: how can I do this work, or do I need to do some input validation when creating category names so that this does not happen?

+4
source share
2 answers

First of all, thanks to Tirius, Romias and eth0 for their suggestions.

I decided that I did not want to use Id for the category, and I did not want to create a route handler, since this does not solve the root problem.

Instead, I create a validation attribute called "UsedAsUrl" and apply it to my .Name category in my domain model. This has the advantages of built-in validation (good for the end user) and good reuse from a developer's point of view.

So, my model model now looks like this (note the [UsedAsUrl] attribute):

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

And the attribute I created looks like this:

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

Now when I go to add a category:

adding a category that I know will fail validation

I get this answer automatically:

Failed response with validation

JS is not working yet, but my controller can pick up the state of the model, and this is a response from it, so the attribute works correctly.

This basically checks for a slash with spaces on either side. Please note that this is not an exhaustive mechanism for checking URLs, but it will be used for my current project. If anyone has any improvements, let me know and I will correct this answer.

I am not good at RegExp, so I didn’t get from RegularExpressionAttribute, and also didn’t want me to have to output an error message from my model. This attribute should really be based on the fact that when using categories as URLs, more rules appear.

0
source

As Tirius said, it will be easier to pass the category identifier, but if you want to pass the name, I would suggest using Url.Encode() every time you create a link or create a custom UrlHelper to do this for you, Once it clicks on the action of your controllers, just do Url.Decode( ) to return the original string.

An easier way is to create your own route handler (implement IRouteHandler ) to do this for you.

0
source

Source: https://habr.com/ru/post/1385992/


All Articles