在 Azure Web App 上使用 Vue 路由器 HTML5 历史模式 URL 重写时出现问题

Trouble with URL rewrite using Vue Router HTML5 History Mode on Azure Web App

我从 Azure Web 应用程序提供 Vue SPA。没有服务器端逻辑(或 none 与此问题相关)。

我在 HTML5 历史模式下使用 Vue Router。它真的很好用。唯一的问题是,当用户尝试使用 "direct" URL(例如 https://my.app.com/contacts)访问应用程序内部的视图时会发生什么。正如预期的那样,这给出了 404 Not Found。

解决方法众所周知:使用URL重写将此类请求路由到应用程序的根目录:/或/index.html.

这就是我想要做的。对于 Azure Web Apps,方法是在 web.config 文件中使用重写规则。我的 Web.config 看起来像这样:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Handle History Mode and custom 404/500" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

这是直接从 Vue Router 的文档中提取的,here

当我现在访问网站时,我得到一个空白页面。使用开发人员控制台(在 Chrome 中)并检查网络选项卡,我发现 all 现在请求 return index.html 的内容,包括脚本文件和 css 个文件。

所以重写显然有效,只是有点太多了。

问题似乎是(否定的)IsFile 和 IsDirectory 条件。这些使用 REQUEST_FILENAME 服务器变量。从 this page 我了解到这应该是服务器文件系统上的映射路径。

由于所有的请求都被重写了,一定是因为条件无法将路径识别为有效的文件或目录路径。

编辑: 我使用失败的请求跟踪来调试 url 重写,并且学到了更多。原来rewrite模块运行时,REQUEST_FILENAME变量不正确

在 Azure Web 应用程序中,内容根位于 D:\site\wwwroot。在这个文件夹中是我的应用程序的 wwwroot 文件夹。一些谷歌搜索显示这是预期的。此外,映射工作正常 - 没有启用 url 重写,像 /img/logo.png 这样的 url 路径将 return 位于 D:\site\wwwroot\wwwroot\img\logo.png.[=23= 的静态文件]

但是当 url 重写模块运行时,REQUEST_FILENAME 将具有值 D:\site\wwwroot\img\logo.png。该文件不存在,因此 IsFile 条件失败。

那么问题来了:为什么url重写模块和静态文件提供者在路径映射上不一致?

我终于明白了。

尽管 ASP.NET 核心应用程序使用 IIS,但它们不再让 IIS 提供静态文件。静态文件保存在子目录 (wwwroot) 中,并由静态文件中间件提供服务,在 Startup.cs 中这样配置

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

静态文件中间件知道静态文件的位置(如果您愿意,可以进行其他配置),并将处理指向该目录中任何文件的请求。

在 Azure Web Apps 中,由于历史原因,.NET Core 程序集和 web.config 所在的内容根目录默认位于 D:\home\site\wwwroot,因此请求:

http:/myapp.com/img/dang.png

中间件将检查文件是否:

D:\home\site\wwwroot\wwwroot\img\dang.png

存在(是的,那是两个 wwwroot)。

但是,IIS 并不知道这一点。所以就 IIS 而言,url:

http:/myapp.com/img/dang.png

居住地:

D:\home\site\wwwroot\img\dang.png

这就是 {REQUEST_FILENAME} IIS 变量未标记的原因,以及 IsFile 和 IsDirectory 失败的原因。

因此,总结一下:IIS URL 重写在 Azure Web 应用程序中仍然有效,但是任何依赖于 url 到文件映射的匹配都将失败。

令人高兴的是(我现在发现了),存在一个替代方案 - URL 重写中间件。这是 ASP.NET 可用的核心组件 "out of the box" 作为 Microsoft.AspNetCore.App 元数据包的一部分。它可以让您完成 IIS URL 重写可以让您做的所有事情(以及更多,因为它允许使用任意编码逻辑进行扩展),但可以正确处理静态文件。文档是 here.

这是我创建的重写规则:

public class Html5HistoryRewriteRule : IRule
{
    private readonly string _rewriteTo;
    private readonly Regex _ignore;

    public Html5HistoryRewriteRule(string rewriteTo = null, string ignore = null)
    {
        _rewriteTo = rewriteTo ?? "/";
        _ignore = ignore != null ? new Regex(ignore) : null;
    }

    public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;
        var path = request.Path.Value;

        if (string.Equals(path, _rewriteTo, StringComparison.OrdinalIgnoreCase))
            return;
        if (_ignore != null && _ignore.IsMatch(path))
            return;

        var fileInfo = context.StaticFileProvider.GetFileInfo(path);
        if (!fileInfo.Exists)
        {
            request.Path = _rewriteTo;
            context.Result = RuleResult.SkipRemainingRules;
        }
    }
}

我认为使用静态 Web 应用程序服务部署 SPA 是一种更方便的方法,请在此处查看有关路由的更多信息:https://docs.microsoft.com/en-us/azure/static-web-apps/routes