可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have the AngularJS seed project and I\'ve added
$locationProvider.html5Mode(true).hashPrefix(\'!\');
to the app.js file. I want to configure IIS 7 to route all requests to
http://localhost/app/index.html
so that this works for me. How do I do this?
Update:
I\'ve just discovered, downloaded and installed the IIS URL Rewrite module, hoping this will make it easy and obvious to achieve my goal.
Update 2:
I guess this sums up what I\'m trying to achieve (taken from the AngularJS Developer documentation):
Using this mode requires URL rewriting on server side,
basically you have to rewrite all your links to entry point of your
application (e.g. index.html)
Update 3:
I\'m still working on this and I realise I need to NOT redirect (have rules that rewrite) certain URLs such as
http://localhost/app/lib/angular/angular.js
http://localhost/app/partials/partial1.html
so anything that is in the css, js, lib or partials directories isn\'t redirected. Everything else will need to be redirected to app/index.html
Anyone know how to achieve this easily without having to add a rule for every single file?
Update 4:
I have 2 inbound rules defined in the IIS URL Rewrite module. The first rule is:
The second rule is:
Now when I navigate to localhost/app/view1 it loads the page, however the supporting files (the ones in the css, js, lib and partials directories) are also being rewritten to the app/index.html page - so everything is coming back as the index.html page no matter what URL is used. I guess this means my first rule, that is supposed to prevent these URLs from being processed by the second rule, isn\'t working.. any ideas? ...anyone? ...I feel so alone... :-(
回答1:
I write out a rule in web.config after $locationProvider.html5Mode(true)
is set in app.js
.
Hope, helps someone out.
<system.webServer>
<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>
</system.webServer>
In my index.html I added this to <head>
<base href=\"/\">
Don\'t forget to install IIS URL Rewrite on server.
Also if you use Web API and IIS, this will work if your API is at www.yourdomain.com/api
because of the third input (third line of condition).
回答2:
The IIS inbound rules as shown in the question DO work. I had to clear the browser cache and add the following line in the top of my <head>
section of the index.html page:
<base href=\"/myApplication/app/\" />
This is because I have more than one application in localhost and so requests to other partials were being taken to localhost/app/view1
instead of localhost/myApplication/app/view1
Hopefully this helps someone!
回答3:
In my case I kept getting a 403.14 after I had setup the correct rewrite rules. It turns out that I had a directory that was the same name as one of my URL routes. Once I removed the IsDirectory rewrite rule my routes worked correctly. Is there a case where removing the directory negation may cause problems? I can\'t think of any in my case. The only case I can think of is if you can browse a directory with your app.
<rule name=\"fixhtml5mode\" stopProcessing=\"true\">
<match url=\".*\"/>
<conditions logicalGrouping=\"MatchAll\">
<add input=\"{REQUEST_FILENAME}\" matchType=\"IsFile\" negate=\"true\" />
</conditions>
<action type=\"Rewrite\" url=\"/\" />
</rule>
回答4:
The issue with only having these two conditions:
<add input=\"{REQUEST_FILENAME}\" matchType=\"IsFile\" negate=\"true\" />
<add input=\"{REQUEST_FILENAME}\" matchType=\"IsDirectory\" negate=\"true\" />
is that they work only as long as the {REQUEST_FILENAME}
exists physically on disk. This means that there can be scenarios where a request for an incorrectly named partial view would return the root page instead of a 404 which would cause angular to be loaded twice (and in certain scenarios it can cause a nasty infinite loop).
Thus, some safe \"fallback\" rules would be recommended to avoid these hard to troubleshoot issues:
<add input=\"{REQUEST_FILENAME}\" pattern=\"(.*?)\\.html$\" negate=\"true\" />
<add input=\"{REQUEST_FILENAME}\" pattern=\"(.*?)\\.js$\" negate=\"true\" />
<add input=\"{REQUEST_FILENAME}\" pattern=\"(.*?)\\.css$\" negate=\"true\" />
or a condition that matches any file ending:
<conditions>
<!-- ... -->
<add input=\"{REQUEST_FILENAME}\" pattern=\".*\\.[\\d\\w]+$\" negate=\"true\" />
</conditions>
回答5:
The easiest way I found is just to redirect the requests that trigger 404 to the client. This is done by adding an hashtag even when $locationProvider.html5Mode(true)
is set.
This trick works for environments with more Web Application on the same Web Site and requiring URL integrity constraints (E.G. external authentication). Here is step by step how to do
index.html
Set the <base>
element properly
<base href=\"@(Request.ApplicationPath + \"/\")\">
web.config
First redirect 404 to a custom page, for example \"Home/Error\"
<system.web>
<customErrors mode=\"On\">
<error statusCode=\"404\" redirect=\"~/Home/Error\" />
</customErrors>
</system.web>
Home controller
Implement a simple ActionResult
to \"translate\" input in a clientside route.
public ActionResult Error(string aspxerrorpath) {
return this.Redirect(\"~/#/\" + aspxerrorpath);
}
This is the simplest way.
It is possible (advisable?) to enhance the Error function with some improved logic to redirect 404 to client only when url is valid and let the 404 trigger normally when nothing will be found on client.
Let\'s say you have these angular routes
.when(\"/\", {
templateUrl: \"Base/Home\",
controller: \"controllerHome\"
})
.when(\"/New\", {
templateUrl: \"Base/New\",
controller: \"controllerNew\"
})
.when(\"/Show/:title\", {
templateUrl: \"Base/Show\",
controller: \"controllerShow\"
})
It makes sense to redirect URL to client only when it start with \"/New\" or \"/Show/\"
public ActionResult Error(string aspxerrorpath) {
// get clientside route path
string clientPath = aspxerrorpath.Substring(Request.ApplicationPath.Length);
// create a set of valid clientside path
string[] validPaths = { \"/New\", \"/Show/\" };
// check if clientPath is valid and redirect properly
foreach (string validPath in validPaths) {
if (clientPath.StartsWith(validPath)) {
return this.Redirect(\"~/#/\" + clientPath);
}
}
return new HttpNotFoundResult();
}
This is just an example of improved logic, of course every web application has different needs