Javascriptservices: Purpose of MapSpaFallbackRoute

Created on 24 May 2017  路  11Comments  路  Source: aspnet/JavaScriptServices

What is the purpose of the following code which is automatically loaded to the Startup class with this project:

routes.MapSpaFallbackRoute(
    name: "spa-fallback",
    defaults: new { controller = "Home", action = "Index" });

This results in a 200 being thrown when a non-existent API endpoint is hit instead of a 404.

Most helpful comment

You want to use a mapWhen looking for something that starts with /api, use this below and you'll be all set:

```c#
// this part you already have
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});

        // here you can see we make sure it doesn't start with /api, if it does, it'll 404 within .NET if it can't be found
        app.MapWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
        {
            builder.UseMvc(routes =>
            {
                routes.MapSpaFallbackRoute(
                    name: "spa-fallback",
                    defaults: new { controller = "Home", action = "Index" });
            });
        });

```

Hope that helps!

All 11 comments

It's for when you want to handle the 404 within your front-end application (and as noted below, let all Routes go through so your front-end can handle them)

@MarkPieszak My use case - which I assume is very common - is to want to handle bad angular routes in the front-end (by either sending to a "404" routes or just loading a "home" route), but throw 404s on bad API calls (that is, requests whose URL follows a certain pattern eg. "/api").

Can you give me a recommendation?

You want to use a mapWhen looking for something that starts with /api, use this below and you'll be all set:

```c#
// this part you already have
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});

        // here you can see we make sure it doesn't start with /api, if it does, it'll 404 within .NET if it can't be found
        app.MapWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
        {
            builder.UseMvc(routes =>
            {
                routes.MapSpaFallbackRoute(
                    name: "spa-fallback",
                    defaults: new { controller = "Home", action = "Index" });
            });
        });

```

Hope that helps!

Thanks @MarkPieszak for the answer. If this becomes a common request, we could add a further excludePrefix option onto MapSpaFallbackRoute so you can set it to api.

Also:

It's for when you want to handle the 404 within your front-end application.

That's not the whole story. It's mostly just so that all URLs that might be handled by your front-end application cause the front-end application to be returned. For example, a request for the URL /counter (in the default template) should cause the front-end application to be loaded, which will then display a suitable UI. If you didn't have MapSpaFallbackRoute then the server would just return a 404 for /counter since there's no matching MVC controller.

@MarkPieszak Perfect. Your solution works as desired.

Fantastic responsiveness as usual.

If you didn't have MapSpaFallbackRoute then the server would just return a 404 for /counter since there's no matching MVC controller.

So umm yeah, this is REALLY an important point that I hope does not get lost :)

Meaning. I can send someone a url like this:

http://helloworlddata.azurewebsites.net/counter

..and it works even though there is really no /counter page. It exists only as an Angular route :)

That's not the whole story. It's mostly just so that all URLs that might be handled by your front-end application cause the front-end application to be returned. For example, a request for the URL /counter (in the default template) should cause the front-end application to be loaded, which will then display a suitable UI. If you didn't have MapSpaFallbackRoute then the server would just return a 404 for /counter since there's no matching MVC controller.

Yes apologies I should of mentioned that! @SteveSandersonMS

For anyone who wants to use ASP.NET Razor Pages that came out with .NET Core 2.0 I never could get map fallback route to work so I pivoted and went a different direction which implements @MarkPieszak 's solution of only routing all requests to the SPA when the route doesn't start with API.

```csharp
public void ConfigureServices(IServiceCollection services) =>
services
.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/Index", "{url:regex(^(?!api).$)}");
});
````

@buvinghausen That works well. Just be aware that every request that doesn't match a static file will get routed to your SPA - even requests with file extensions. MapSpaFallbackRoute does not match requests that look like filenames - that is, have an extension at the end.

Edit: I just realize you do account for periods in the request - but I'll leave my comment here just in case it helps others. Thanks!

Edit2: Just opened an issue with https://github.com/aspnet/JavaScriptServices with a suggestion: https://github.com/aspnet/JavaScriptServices/issues/1354

 app.UseMvc(routes =>
                {
                    routes.MapSpaFallbackRoute(
                    name: "spa-fallback-admin",
                    defaults: new { area="Admin", controller = "Home", action = "Index" });
                });

is this the correct way to using controller from some areas?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Sampath-Lokuge picture Sampath-Lokuge  路  4Comments

dantheman999301 picture dantheman999301  路  4Comments

raberana picture raberana  路  3Comments

uNkNowN92-git picture uNkNowN92-git  路  3Comments

ZeekoZhu picture ZeekoZhu  路  3Comments