How to route EVERYTHING other than Web API to /index.html
Solution 1
Use a wildcard segment:
routes.MapRoute(
name: "Default",
url: "{*anything}",
defaults: new { controller = "Home", action = "Index" }
);
Solution 2
Suggest more native approach
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404" subStatusCode="-1"/>
<error statusCode="404" prefixLanguageFilePath="" path="/index.cshtml" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
Solution 3
in my case none of these approaches worked. i was stuck in 2 error message hell. either this type of page is not served or some sort of 404.
url rewrite worked:
<system.webServer>
<rewrite>
<rules>
<rule name="AngularJS" stopProcessing="true">
<match url="[a-zA-Z]*" />
<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>
...
notice i matched on [a-zA-Z] because i don't want to rewrite any of the .js .map etc urls.
this worked in VS out of hte box, but in IIS you may need to install a url-rewrite module https://www.iis.net/downloads/microsoft/url-rewrite
Solution 4
I had a similar approach as the top few answers, but the downside is if someone is making an API call incorrectly, they'll end up getting that index page back instead of something more useful.
So I've updated mine such that it will return my index page for any request not starting with /api:
//Web Api
GlobalConfiguration.Configure(config =>
{
config.MapHttpAttributeRoutes();
});
//MVC
RouteTable.Routes.Ignore("api/{*anything}");
RouteTable.Routes.MapPageRoute("AnythingNonApi", "{*url}", "~/wwwroot/index.html");
Solution 5
I've been working with OWIN Self-Host and React Router few days ago, and incurred to probably similar issue. Here is my solution.
My workaround is simple; check if it's a file in the system; otherwise return index.html. Since you don't always want to return index.html if some other static files are requested.
In your Web API config file:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "{*anything}",
defaults: new { controller = "Home", action = "Index" }
);
and then create a HomeController as follwing...
public class HomeController: ApiController
{
[HttpGet]
[ActionName("Index")]
public HttpResponseMessage Index()
{
var requestPath = Request.RequestUri.AbsolutePath;
var filepath = "/path/to/your/directory" + requestPath;
// if the requested file exists in the system
if (File.Exists(filepath))
{
var mime = MimeMapping.GetMimeMapping(filepath);
var response = new HttpResponseMessage();
response.Content = new ByteArrayContent(File.ReadAllBytes(filepath));
response.Content.Headers.ContentType = new MediaTypeHeaderValue(mime);
return response;
}
else
{
var path = "/path/to/your/directory/index.html";
var response = new HttpResponseMessage();
response.Content = new StringContent(File.ReadAllText(path));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return response;
}
}
}
Scott R. Frost
.NET Software Developer with over 10 years of experience in ASP.NET, Windows Forms, C#, VB.NET, MS SQL (including lots of SSRS), Oracle, Crystal Reports, and even some other buzzwords!
Updated on July 24, 2022Comments
-
Scott R. Frost almost 2 years
I've been working on an AngularJS project, inside of ASP.NET MVC using Web API. It works great except when you try to go directly to an angular routed URL or refresh the page. Rather than monkeying with server config, I thought this would be something I could handle with MVC's routing engine.
Current WebAPIConfig:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }, constraints: new { id = @"^[0-9]+$" } ); config.Routes.MapHttpRoute( name: "ApiWithActionAndName", routeTemplate: "api/{controller}/{action}/{name}", defaults: null, constraints: new { name = @"^[a-z]+$" } ); config.Routes.MapHttpRoute( name: "ApiWithAction", routeTemplate: "api/{controller}/{action}", defaults: new { action = "Get" } ); } }
Current RouteConfig:
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.IgnoreRoute(""); //Allow index.html to load routes.IgnoreRoute("partials/*"); routes.IgnoreRoute("assets/*"); } }
Current Global.asax.cs:
public class WebApiApplication : HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); var formatters = GlobalConfiguration.Configuration.Formatters; formatters.Remove(formatters.XmlFormatter); GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings { Formatting = Formatting.Indented, PreserveReferencesHandling = PreserveReferencesHandling.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, }; } }
GOAL:
/api/* continues to go to WebAPI, /partials/, and /assets/ all go to file system, absolutely anything else gets routed to /index.html, which is my Angular single page app.
--EDIT--
I seem to have gotten it working. Added this to the BOTTOM OF RouteConfig.cs:
routes.MapPageRoute("Default", "{*anything}", "~/index.html");
And this change to the root web.config:
<system.web> ... <compilation debug="true" targetFramework="4.5.1"> <buildProviders> ... <add extension=".html" type="System.Web.Compilation.PageBuildProvider" /> <!-- Allows for routing everything to ~/index.html --> ... </buildProviders> </compilation> ... </system.web>
However, it smells like a hack. Any better way to do this?
-
Scott R. Frost over 10 yearsA wildcard might work, can I make it go to a flat file rather than a MVC controller? Alternatively, how would I make a Home controller with an Index action that passed execution to index.html while maintaining whatever they entered as the URL (for example /something/edit/123)?
-
Trevor Elliott over 10 yearsIn ASP.NET MVC a request always goes to a controller and a controller uses a view engine to generate a view. This can be a simple HTML file with nothing special, see the Controller.View. Just rename the file to .cshtml, place it in the Views/Home folder, and set the Layout to null at the top of the file.
-
Trevor Elliott over 10 yearsThe route in my answer will maintain whatever URL the user requested.
-
parliament almost 9 yearsworked perfect and is more in line with how other web servers would be configured. Thanks!
-
Silencer over 8 yearsNot sure if this is "the right way", but after a long search for other methods, I came back to this one. Best one so far.
-
codea over 8 yearsWorked for me as well, Since I am using only ASP.NET API, I don't have the route config available, as it is only available in System.Web.Mvc. this works perfectly. Thank you!
-
jonnybot about 8 yearsThe behavior you describe is expected, but not what the question asks for. Sure, you can serve static resources. The question is, how to map arbitrary routes to a specific static resource (index.html).
-
Sonic Soul over 7 yearsi'm getting "this type of page is not served" error after using this approach
-
Sonic Soul over 7 yearssorry made the edits to wrong answer, and now removed them :)
-
Hawk almost 7 yearsThis answer worked for me. I added the 2x RouteTable configurations to my global.asax after the web api configuration and I registered the .html PageBuilderProvider and it worked. Didn't need to register any additional web api routes apart from the default /api route. My angular 4 app deployed in the root of the application works perfectly with routing, all bundles, images and other files also behave as expected. Thumbs up!
-
Kris Coleman over 6 yearsfor me, this worked, but I had to change the pattern to
.*(api)
-
Zach Smith about 6 yearsThis isn't web api... is it?
-
Erutan409 over 2 yearsCommenting in hopes that it will help for search engine indexing if anyone is looking for a solution for VueJS Router. This worked absolutely perfectly for how I'm implementing this in C# w/ ApiController.