I want to use the html5Mode in a website project on VS2015. The default page is working correctly, but when I use a rout defined on angular, it shows a 404 error. I added the following code on web.config, but it does not seem to work properly
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
My routes on angular look like :
.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
$urlRouterProvider.otherwise("/countries")
$stateProvider
.state('mainpage', {
url: '/countries',
templateUrl: 'assets/app/templates/countries.html',
//controller: 'countriesController',
controller: 'countriesController as cCtrl',
resolve: {
officesReso: function (apiCommunicationFactory) {
return apiCommunicationFactory.Office.get();
}
}
})
.state('countryDetail', {
url: '/countries/:office',
controller: 'countryDetailController as countryDetailCtrl',
templateUrl: 'assets/app/templates/country.html',
})
$locationProvider.html5Mode(true);
});
Am I missing something? How can I make it work?
The Cause
You get a 404 because the request bypasses the entry point to your application (and thus angular!) meaning that the web server is left looking for one of the server's specified default docs in a folder that doesn't exist.
The Fix
1) Specify the entry point of your application by placing <base href="/index.html">
in the <head>
section of your index.html file. This tells angular where relative URLs should be resolved to.
2) Create a rewrite rule in the <system.webServer>
section of the web.Config file. This forces the web server to always hit your index.html file and allow the angular routing engine to kick in. Of course, we don't always want to hit index.html with every http request and so several conditions have been added to ignore the rule.
<rewrite>
<rules>
<rule name="AngularJS Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="api/(.*)" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
UPDATE - Re : The Rewrite Rules and Conditions
Definitely check out the official docs: URL Rewrite Module Configuration Reference
<match url=".*" />
This is where you specify when the rule will kick in. With *
or .*
it's going to match on every incoming request, and it will rewrite the URL if the conditions (if any) are passed...
<conditions logicalGrouping="MatchAll">
This is where you specify and group the conditions. Setting logicalGrouping="MatchAll"
means that all of the conditions must evaluate to true in order for the URL to be rewritten.
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
The above means: Make sure the requested url doesn't point to a file. You would need this if you were going to link to a file on your web server. I often create link to .ics
files to create public calendars for example, and you don't want to rewrite (redirect) these links, you want them to hit the file.
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
.
This means: Make sure the requested url doesn't point to a directory. You would need this if you wanted to list the files in a directory on the web server. I'm not doing this, so don't actually need it!
<add input="{REQUEST_URI}" pattern="api/(.*)" negate="true" />
This means: Make sure the requested url doesn't start with api/
i.e. that it isn't an async call to my web api, because all of my api calls start with api/
and I don't want to rewrite them to index.html, I want them to continue exactly as requested and hit my api action methods.
If any one of the above conditions evaluates to false (i.e. it is a file or it is a directory or it is an api call), then the URL won't be rewritten because logicalGrouping="MatchAll"
was specified.