Question

I have this route:
(Ignore the XML Format)

<route name="note" url="{noteId}-{title}">
    <constraints>
        <segment name="noteId" value="\d+" />
        <segment name="title" value=".+" />
    </constraints>
</route>

I want it to match urls like /1234-hello-kitty or /5578-a-b-c-ddd-f-g

MVC Routing Handler seems to be having some troubles with this.

I have been reading a lot about the subject, and I found out some interesting facts:

  • MVC first identifies the route segments and then it checks the constraints
  • MVC reads segments from right to left, meaning it first identifies title and then noteId

Taking the first example, I'm guessing MVC is identifying noteId as 1234-hello and title as kitty.

This fails when constraints are checked and therefore the route is not matched.

Is there another way to do this?

Please take in account that I want to keep both my segments noteId and title, and they should be both separated by a hyphen - (This is mandatory)

Was it helpful?

Solution

I can see couple of options for an approach to solving this issue:

URLRewriting

One possibility is to rewrite URLs (similar to mod_rewrite) to convert them from format that is imposed on you into a format that MVC can route natively. There is an IIS Module from Microsoft that does just that, and I believe (though not certain) would have the necessary functionality to accomplish the task in your case. The basic principle here is that if the format cannot be handled by MVC due to route template parsing rules, then convert the URL to something that it can manage before it even reaches the MVC route handling. URL Rewrite is an IIS Module that sits before MVC handler, examines the requests, and is able to rewrite the request from one form into another. Then, this altered form is what is seen by MVC and can be understood and parsed by it. E.g. the URL of /1234-hello-kitty can be rewritten by the module as /1234/hello-kitty and then MVC route template would be a simple {noteId}/{*title}. The downside caveat here is that generating links may not work here since generated links would look like /1234/hello-kitty rather than /1234-hello-kitty. However, mitigation may be to have a route specifically for link generation and not for routing defined as {noteId}-{title}. I believe (should be verified) that this will actually generate a link in form /1234-hello-kitty (albeit not being able to parse it on incoming request).

Custom MVC route handler

This one basically draws on the idea that if MVC doesn't do it for you, override its behavior to do what you wish it would do. The tactical aspect of this is described in SO post on how to provide your own handler. The way you would use it is you can provide your own interpretation of parsing of segments of url to route data, and provide the actual values as you parse them into requestContext.RouteData.Values["nodeId"] = /* your code that gets noteId out of URL. */. The rest of the application works as any other, knowing nothing about this surgical intervention in routing.

OTHER TIPS

I have been reading a lot about the subject, and I found out some interesting facts:

  • MVC first identifies the route segments and then it checks the constraints
  • MVC reads segments from right to left, meaning it first identifies title and then noteId

Taking the first example, I'm guessing MVC is identifying noteId as 1234-hello and title as kitty.

This fails when constraints are checked and therefore the route is not matched.

Those facts and the guess are completely correct. This is how ASP.NET routing works, unfortunately.

Why?

ASP.NET routing Simply works in two phases, first parse all routes and second try to match them for every request.

Considering your case, first parses:

  • Split a routeUrl by "/". Each segment is a path segment. You have just one: "{noteId}-{title}".
  • For each path segment, split them into sub-segments: parameters and literals. Parameters are enclosed by {} and literals are the rest. You have 3 sub-segments: {noteId}, - and {title}

Then, try to match (when having multiple sub-segments):

  • Find the last occurrence of last literal (-) and match the text after the literal to the last parameter (title).
  • Repeats 1st to finish all parameters and literals. If the URL or sub-segments are longer, the match fails.

Possible solutions

So in order to use a literal you have to make sure that your literal won't occur in parameters. Since you are stick with a dash, you may have some possible solutions.

  • You can use one parameter and no literal with a matching constraint (e.g. ^\d+-[\w-]+$), then try to parse the id inside controller action. This requires no changes in existing URL structure.
  • You can switch places of title and noteId, like /hello-kitty-1234.
  • You can try double dashes as literal, like /1234--hello-kitty.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top