【问题标题】:Wrapping StaticFileMiddleware to redirect 404 errors包装 StaticFileMiddleware 以重定向 404 错误
【发布时间】:2014-09-18 15:32:36
【问题描述】:

我正在尝试将自定义中间件注入到我的 OWIN 管道中,该管道包装了 MS 提供的 StaticFileMiddleware,以支持 AngularJS 中的 HTML 5 模式。我一直在关注本指南:http://geekswithblogs.net/shaunxu/archive/2014/06/10/host-angularjs-html5mode-in-asp.net-vnext.aspx

据我所知,这是如何工作的,我的中间件将请求传递给静态文件中间件,然后如果它无法解决这些请求(即,对 HTML 5 角度路径的请求, "/whatever"),而是返回基本的角度页面,以便对 HTML 5 路径的硬请求将起作用。

我的问题是调用内部中间件的结果似乎总是一个 200 状态码,即使在我的浏览器中我得到一个 404,这让我摸不着头脑。这是我的代码供参考:

public static class AngularServerExtension
{
    public static IAppBuilder UseAngularServer(this IAppBuilder builder, string rootPath, string entryPath)
    {
        var options = new AngularServerOptions()
        {
            FileServerOptions = new FileServerOptions()
            {
                EnableDirectoryBrowsing = false,
                FileSystem = new PhysicalFileSystem(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, rootPath))
            },
            EntryPath = new PathString(entryPath)
        };

        builder.UseDefaultFiles(options.FileServerOptions.DefaultFilesOptions);
        return builder.Use(new Func<AppFunc, AppFunc>(next => new AngularServerMiddleware(next, options).Invoke));           
    }
}

public class AngularServerMiddleware
{
    private readonly AngularServerOptions _options;
    private readonly AppFunc _next;
    private readonly StaticFileMiddleware _innerMiddleware;

    public AngularServerMiddleware(AppFunc next, AngularServerOptions options)
    {
        _next = next;
        _options = options;

        _innerMiddleware = new StaticFileMiddleware(_next, options.FileServerOptions.StaticFileOptions);
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        IOwinContext context = new OwinContext(environment);
        // try to resolve the request with default static file middleware
        await _innerMiddleware.Invoke(environment);
        Debug.WriteLine(context.Request.Path + ": " + context.Response.StatusCode);
        // *** Right here is where I would expect a 404 but I get a 200 when debugging,
        // even though my browser eventually returns a 404

        // route to root path if the status code is 404
        // and need support angular html5mode
        if (context.Response.StatusCode == 404 && _options.Html5Mode)
        {
            context.Request.Path = _options.EntryPath;
            await _innerMiddleware.Invoke(environment);
            Console.WriteLine(">> " + context.Request.Path + ": " + context.Response.StatusCode);
        }
    }
}
public class AngularServerOptions
{
    public FileServerOptions FileServerOptions { get; set; }

    public PathString EntryPath { get; set; }

    public bool Html5Mode
    {
        get
        {
            return EntryPath.HasValue;
        }
    }

    public AngularServerOptions()
    {
        FileServerOptions = new FileServerOptions();
        EntryPath = PathString.Empty;
    }
}

【问题讨论】:

    标签: asp.net angularjs owin


    【解决方案1】:

    我不想与 IIS 捆绑在一起,因为 asp.net 核心正在向前发展。以下是我使用 OWIN 使其工作的方法:

    // catch all for html5/angular2 client routing urls that need to be redirected back to index.html
    // for original, see: http://stackoverflow.com/questions/27036448/how-to-intercept-404-using-owin-middleware/30741479#30741479
    app.Use(async (ctx, next) =>
    {
        // execute the rest of the pipeline
        //  though really, we're last in this configuration
        //  but, this allows the other static file handlers
        //  and web api route handlers to fail
        await next();
    
        // double check that we have a 404
        //  we could also double check that we didn't request a file (with an extension of some sort)
        if (ctx.Response.StatusCode != 404)
        {
            return;
        }
    
        // we have a 404, serve our default index.html
        var middleware = new StaticFileMiddleware(
            env => next(), new StaticFileOptions
            {
                FileSystem = new PhysicalFileSystem("./wwwroot"),
                RequestPath = PathString.Empty
            });
    
        ctx.Request.Path = new PathString("/index.html");
        await middleware.Invoke(ctx.Environment);
    });
    

    我需要在检查状态码之前调用next(),因为我假设其他中间件不会设置 404,直到所有中间件都有机会处理它。

    免责声明:我才刚刚开始探索基于 OWIN 的托管,因此虽然这似乎可行,但可能存在一些非最佳实践。

    【讨论】:

      【解决方案2】:

      根据您的问题,我不确定您使用的是 IIS 还是 selfhost。如果您使用的是 IIS,有一个比使用 owin 中间件更干净/更快的解决方案: 您可以使用 IIS 重写引擎,将以下内容复制到您的 Web 配置中。

      <system.webServer>
      
      <rewrite>
        <rules>
          <!--Redirect selected traffic to index -->
          <rule name="Index Rule" stopProcessing="true">
            <match url=".*" />
            <conditions logicalGrouping="MatchAll">
              <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> 
              <add input="{REQUEST_URI}" matchType="Pattern" pattern="^/api/" negate="true" />
            </conditions>
            <action type="Rewrite" url="/index.html" />
          </rule>
        </rules>
      </rewrite>
      ...
      </system.webServer>
      

      此行允许所有文件正常服务:

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

      这一行允许api正常服务

      <add input="{REQUEST_URI}" matchType="Pattern" pattern="^/api/" negate="true" />
      

      其他所有内容都获取 index.html

      【讨论】:

      • 我收到 500.19 内部服务器错误,有什么想法吗?
      • @georgiosd 发布的内容需要在 块内。还要确保不包括 "..." ; D
      • 原来是我没有安装URL重写模块。感谢 IIS 提供的有用信息 :)
      • 嗨 @Joe 你能帮我把这段代码转换成 appsettings.json 吗?
      猜你喜欢
      • 2017-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-10
      • 2013-05-05
      • 2011-07-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多