Modern client-side MV* frameworks have two options for handling routing: hash and HTML5. Navigating to the “customers” view of an app using hash routing might look like this: http://myapp/#customers. Accessing the same view with an HTML5 routing scheme would look like this: http://myapp/customers.
HTML5 routing looks a little cleaner, so starting around 2015, client-side frameworks like React and Angular2 began to use HTML5 routing out of the box. Older frameworks such as Angular 1.x and Durandal use hash routing by default.
Despite looking cleaner, HTML5 routing can cause some headaches if you’re using .NET WebAPI (and other frameworks) on the back end. This is because there’s no clean way to differentiate between a client-side route and a server-side route. For example, let’s say I’m using HTML5 routing and I start on the root view of “myapp”, and click a link to go to the customers view. The framework (React, Angular, etc) will set the window location to “http://myapp/customers” and load the customer view and associated model. Everything works fine…so far. But try refreshing the page, or navigating directly to the customer route. You’ll get a 404, since the browser tried to do a GET against the route and it isn’t defined on the back end.
I would recommend configuring MV* frameworks to use hash routing as it provides a clear distinction between client and server side routes. Browsers will not include the value (fragment identifier) after the hash in a request, so the back end will not attempt to resolve it. As I said, older frameworks like Durandal and Angular 1.x will use hash routing out of the box. Angular2 can be set to use hash routing by setting useHash to “true” on the router module config. And React has a hash routing module that can be referenced in.
Here’s a good example of hash routing on a virtual trading platform I wrote: http://botswarm.io/#BotDetail/27
Clicking the link above will pull up the details for the bot with an Id of 27. Since that route follows the hash, it is not passed to the back end and is resolved on the client-side only. If you look at the network traffic when the view loads, you’ll notice http://botswarm.io/BotDetail/GetBotDetails?botId=27 is requested asynchronously from the BotDetail view model. This is a server-side route and corresponds to the GetBotDetails function on the BotDetail controller. Whether a route is client or server side can be easily determined just by looking at it.