8

I'm trying to inject a custom middleware into my OWIN pipeline that wraps the StaticFileMiddleware available from MS in order to support HTML 5 mode in AngularJS. I've been following this guide: http://geekswithblogs.net/shaunxu/archive/2014/06/10/host-angularjs-html5mode-in-asp.net-vnext.aspx

From what I can gather of how this is supposed to work, my middleware is passing along requests to the static file middleware, and then if it can't resolve those requests (i.e., a request for an HTML 5 angular path, "/whatever"), it instead returns the base angular page so that a hard request for an HTML 5 path will work.

My problem is that the result of invoking the inner middleware always seems to be a 200 status code, even though in my browser I get a 404, which leaves me scratching my head. Here's my code for reference:

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

2 Answers 2

17
+50

From your question I am not sure whether you are using IIS or selfhost. If you are using IIS, there is a much cleaner/faster solution than messing up with owin middleware: You can use IIS rewrite engine, copy the following inside your web config.

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

This line allows all files to be served normally:

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

this line allows the api to be served normally

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

Everything else gets index.html

Sign up to request clarification or add additional context in comments.

4 Comments

I get a 500.19 Internal Server Error with this, any ideas?
@georgiosd the posted needs to be inside of a <configuration> block. Also make sure not to include the "..." ; D
It turned out to be that I didn't have the URL rewrite module installed. Thank you IIS for the helpful messages :)
hi @Joe can you help me convert this code in appsettings.json ?
3

I didn't want to be tied to IIS, with the way asp.net core is moving forward. Here's how I got it to work using 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); }); 

I needed to call next() before I checked for the status code, because I assume the other middleware won't set the 404 until all middleware has a chance to handle it.

DISCLAIMER: I'm only starting to explore OWIN based hosting, so while this seems to work, there could be some non-best practices.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.