如何配置IIS以重写URL并在HTML5模式下运行AngularJS应用程序?

150
我有AngularJS种子项目,并且我已经添加了


$locationProvider.html5Mode(true).hashPrefix('!');

将内容添加到app.js文件中。我希望配置IIS 7以将所有请求路由到该文件。
http://localhost/app/index.html

为了让这对我起作用,我该怎么做?
更新:
我刚刚发现、下载并安装了IIS URL Rewrite模块,希望这可以使我轻松明了地实现我的目标。
更新2:
我想这概括了我所尝试实现的内容(来自于AngularJS开发者文档):
引用: 使用此模式需要在服务器端进行URL重写,基本上您必须将所有链接重写到您应用程序的入口点(例如,index.html)。
更新3:
我仍在解决这个问题,我意识到我需要不重定向(具有重新编写规则的URL)某些URL,例如
http://localhost/app/lib/angular/angular.js
http://localhost/app/partials/partial1.html

所以在css、js、lib或partials目录中的任何内容都不会被重定向。其他所有内容都需要重定向到app/index.html。

有人知道如何轻松实现这一点,而不必为每个文件添加规则吗?

更新4:

我在IIS URL Rewrite模块中定义了2个入站规则。第一个规则是:

IIS URL Rewrite Inbound Rule 1

第二条规则是:

IIS URL Rewrite Inbound Rule 2

现在当我导航到 localhost/app/view1,它会加载页面,然而支持文件(css、js、lib和partials目录中的文件)也被重写到了app/index.html页面上 - 所以无论使用什么URL,一切都返回为index.html页面。我想这意味着我的第一个规则,应该防止第二个规则处理这些URL,但并没有起作用..有什么想法吗?...任何人?...我感到如此孤独... :-(

第二个规则非常关键:您需要确保它路由到 app/index.html 而不是 app/,以显式触发提供 AngularJS 的页面。在我弄清楚之前,我浪费了两个小时的时间。 :-) - Dexter Legaspi
在 RouteConfig.cs 中使用 ASP.NET MVC 的另一种方法是: routes.MapRoute(name: "Default", url: "{*anything}", defaults: new { controller = "Home", action = "Index", } ); 并且您的 HomeController Index 只需返回 File("~/index-anyhinghere.html", "text/html"); 然后您的应用程序就变得与 IIS 无关了。 - Toolkit
我必须从“Web Installer”安装“URL重写模块”。如果没有URL重写模块,则在重新加载时,重写将无法正常工作。请安装URL重写模块。 :) - Bimal Das
9个回答

311

app.js中设置$locationProvider.html5Mode(true)后,我在web.config中写出一个规则。

希望能对某人有所帮助。

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

在我的index.html文件中,我在<head>标签中添加了这个内容。

<base href="/">

别忘了在服务器上安装IIS URL Rewrite

如果你使用Web API和IIS,并且你的API在www.yourdomain.com/api,那么由于第三个输入(条件的第三行),这将起作用。


3
这非常有用,我觉得它是最全面的。谢谢! - Mario
6
完美!这个问题对我来说很难,我花了几个小时来解决。谢谢!给别人的建议:我在<action type="Rewrite" url"/index.html" />中添加了index.html,然后在我的index.html(Angular SPA的根目录文件)上添加了<base href="/index.html" />。当我复制上面的代码时,它没有匹配我的<base href />,因此没有正确工作。此外,对于“/(api)”模式,要特别注意,非常感谢。 - Brad Martin
我使用IIS 7.5,并启用了$locationProvider.html5Mode(true)和<base href="/MyAppName/index.html" />,还进行了web.config规则设置,但我的后退按钮将我退出应用程序,你知道如何解决吗? - Cesar Vega
10
为了进行 Angular 路由,我们必须跳过这些繁琐的步骤,真是个笑话。这让我对 Angular 路由感到失望,真是一团糟。 - user441521
1
太好了!可惜 Angular 团队使用了你的代码,但没有提到 api 部分。 - bresleveloper
显示剩余12条评论

40

如问题所示,IIS入站规则确实有效。我不得不清除浏览器缓存并在index.html页面的<head>部分顶部添加以下行:

<base href="/myApplication/app/" />

这是因为我在本地主机上有多个应用程序,所以对其他部分的请求被发送到localhost/app/view1而不是localhost/myApplication/app/view1

希望这能帮助某些人!


1
我并不需要这个,但问题的编辑非常好。谢谢! - Ash Clarke
2
这也许会有用。在重新加载页面时对我有用。没有为每个文件设置任何特殊规则: https://coderwall.com/p/mycbiq - Per G
正如@ash-clarke所说,这并非必需,但这是一个好习惯,因为它允许页面上的链接与其所属的“子目录”无关。 - Dexter Legaspi
@PerG 我遇到了上述解决方案在重新加载时无法工作的问题,这是我的代码:https://dev59.com/aYPba4cB1Zd3GeqPnA-4 有什么想法吗? - amcdnl
我不知道或尝试过过滤某些调用。一个解决方法是在其他域上拥有API等。但也许这对您来说不是一个解决方案。但您可以继续在我的链接中提问。也许作者可以回答您? - Per G
显示剩余4条评论

14

只有这两个条件存在的问题:

  <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
  <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />

他们只在物理磁盘上存在 {REQUEST_FILENAME} 时才有效。这意味着,如果请求的局部视图名称不正确,则可能出现返回根页面而不是404的情况,从而导致angular被加载两次(在某些情况下,可能会导致令人讨厌的无限循环)。

因此,建议采用一些安全的“后备”规则以避免这些难以排除的问题:

  <add input="{REQUEST_FILENAME}" pattern="(.*?)\.html$" negate="true" />
  <add input="{REQUEST_FILENAME}" pattern="(.*?)\.js$" negate="true" />
  <add input="{REQUEST_FILENAME}" pattern="(.*?)\.css$" negate="true" />

或者一个条件,它匹配任何文件结尾

<conditions>
  <!-- ... -->
  <add input="{REQUEST_FILENAME}" pattern=".*\.[\d\w]+$" negate="true" />
</conditions>

1
有了这一切,我的网站加载得非常好,但如果我在路径上遇到问题就会出现问题。因此,如果我在浏览器中的路径是:http://10.34.34.46/jbvdev/documents/BC498484最初它显示得很好,但如果我点击刷新,一切都会崩溃,因为它开始在 http://10.34.34.46/jbvdev/documents/css 或 http://10.34.34.46/jbvdev/documents/js 寻找css和js。有人知道如何解决这个问题吗? - Michael Mahony

12
在我的情况下,即使我已经设置了正确的重写规则,仍然会不断收到403.14错误提示。原来是我的一个目录与我的URL路由名称相同。一旦我删除了“IsDirectory”重写规则,我的路由便能正常工作了。是否有这样一种情况,即删除目录否定可能会导致问题?在我的情况下,我想不出任何情况。我唯一能想到的情况是,如果您可以使用应用程序浏览目录。
<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

很不幸,这个最佳解决方案对我没有用。我使用的是.NET Framework 4.5和IIS 6.2。我通过首先按照安装Microsoft的URL重写工具的所有步骤,然后调整我的Web.config文件来包含以下内容,使其正常工作:

    <system.webServer>
    <!-- handlers etc -->
    <rewrite>
      <rules>
        <rule name="Angular Routes" stopProcessing="true">
          <match url="(^(?!.*\.[\d\w]+$).*)" />
          <conditions logicalGrouping="MatchAny">
            <add input="{REQUEST_URI}" pattern=".*\/api\/.*" negate="true" />
          </conditions>
          <action type="Rewrite" url="./index.html" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>

第一个正则表达式使用负向前瞻来不匹配URI末尾的任何文件扩展名。然后,在条件语句中,包括“/api/”在内的任何URI也被忽略。
这个配置允许我刷新Angular应用程序而不会破坏路由,并且可以访问我的API而不需要重写。

3
我曾经遇到Angular和IIS在手动刷新时抛出404状态码的类似问题,尝试了最高得票的解决方案,但对我没有用。 我还尝试了许多其他解决方案,涉及WebDAV和更改处理程序,但都没有奏效。
幸运的是,我发现了这个解决方案并且它奏效了(我去掉了不需要的部分)。因此,如果上述任何一种方法对您都无效,或者在尝试它们之前,请尝试这个解决方案,看看是否可以解决您在iis上部署angular时的问题。
将代码段添加到站点根目录下的webconfig中。据我的理解,它会从所有继承(applicationhost.config,machine.config)中删除404状态码,然后在站点级别创建一个404状态码,并将其重定向回主页作为自定义404页面。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404"/>
      <error statusCode="404" path="/index.html" responseMode="ExecuteURL"/>
    </httpErrors>
  </system.webServer>
</configuration>

在刷新的情况下它能够正常工作,其他所有路由也都正常。 - viking

1
我发现最简单的方法就是将触发404的请求重定向到客户端。即使设置了$locationProvider.html5Mode(true),也可以通过添加哈希标记来实现。
这个技巧适用于在同一网站上有多个Web应用程序且需要URL完整性约束(例如外部身份验证)的环境。以下是如何逐步操作的步骤。

index.html

正确设置 <base> 元素
<base href="@(Request.ApplicationPath + "/")">

web.config

首先将404重定向到自定义页面,例如“Home/Error”

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Home/Error" />
    </customErrors>
</system.web>

主页控制器

实现一个简单的ActionResult,用于在客户端路由中“翻译”输入。

public ActionResult Error(string aspxerrorpath) {
    return this.Redirect("~/#/" + aspxerrorpath);
}

这是最简单的方法。

有可能(建议?)通过一些改进的逻辑来增强错误函数,使其仅在URL有效时将404重定向到客户端,并在客户端上找不到任何内容时正常触发404。 假设您有这些Angular路由。

.when("/", {
    templateUrl: "Base/Home",
    controller: "controllerHome"
})
.when("/New", {
    templateUrl: "Base/New",
    controller: "controllerNew"
})
.when("/Show/:title", {
    templateUrl: "Base/Show",
    controller: "controllerShow"
})

只有当URL以“/New”或“/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();
}

这只是改进逻辑的一个例子,当然每个Web应用程序都有不同的需求。

0

我一直在尝试将一个简单的Angular 7应用程序部署到Azure Web App上。 一切都很顺利,直到刷新页面的时候出现了500错误-移动内容。 我在Angular文档和许多论坛中都看到,我需要向我的部署解决方案添加一个web.config文件,并确保重写规则回退到index.html文件。 经过几个小时的挫败和试错测试后,我发现错误非常简单:在我的文件标记周围添加了一个标签。


0

我意识到这可能是一个旧问题,但这里有另一种解决方案不使用web.config文件

我有2个独立的项目:FrontEnd(Angular)和BackEnd(Web API .NET Core)。

  1. 修改angular.json中build => options下的outputPath为"../BackEnd/wwwroot"

  2. 在Program.cs中:app.UseDefaultFiles(); app.UseStaticFiles();

    指示服务器默认在wwwroot目录中查找index.html。

  3. ng build -- 创建了一个wwwroot文件夹,并在该文件夹中创建了所有静态的angular文件。

  4. 我创建了一个新的Controller来替代web.config中的重写规则:

FallbackController.cs

[Route("api/[controller]")]
[ApiController]
public class FallbackController : ControllerBase
{
   return PhysicalFile(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "index.html"), "text/HTML");
}

在 Program.cs 中:app.MapFallbackToController("Index", "Fallback");
这告诉我们的 API 路由责任属于 Angular,并且可以在 Fallback 控制器中找到,而该控制器又指向 index.html 以获取关于路由的指示。

Program.cs

// prior code
app.UseAuthentication();
app.UseAuthorization();

app.UseDefaultFiles();
app.UseStaticFiles();

app.MapControllers();

app.MapFallbackToController("Index", "Fallback");

app.Run();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接