How to return 404 on wrong API url? (ASP.NET Core + SPA)

Viewed 2804

I need to return 404 on wrong api call so I can create proper response to user on client side (Angular 5). Currently backend returns status code 200 and index.html, and that results in json parse error on frontend.

I created new web project with ASP.NET Core 2.1 and Angular template project. Here's the link to published version of that template. I didn't change template code at all, I just published that on azure as it is. (In dev mode I get 404 error as expected).

As mentioned here default behaviour should be 404.

Here is the Startup.cs, and here SampleDataController.cs.

I thought of using something like this in Startup.cs:

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller}/{action}/{id?}");
        });

        app.MapWhen(x => x.Request.Path.Value.Contains("/api"), builder =>
        {
            builder.UseMvc(routes =>
            {
                routes.MapSpaFallbackRoute(
                    name: "spa-fallback",
                    defaults: new { controller = "Home", action = "Error" });
            });
        });

And in HomeController.cs:

    public IActionResult Error()
    {
        return NotFound();
    }

But that seems wrong although it works.

2 Answers

Note: Based on your links, I assume you are using the app.UseSpa() middleware.

The route middleware is executed in order, and if the request never finds a controller a 404 status is returned. The UseSpa middlware is a catch-all, and any request that gets to that point will always have the default SPA page returned. This means any request to a route that hasn't yet found a controller will always return the default SPA page, and not throw a 404.

What you will want to do is only call UseSpa() if the route is not prefixed with /api. Which essentially lets any requests that start with/api skip past that point.

Instead of:

app.UseSpa(spa =>
{
    //[Snip]
});

Use:

app.MapWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
{
    builder.UseSpa(spa =>
    {
        //[Snip]
    });
});

This will let any request that starts with /api past that point, and if no other middleware or routes are matched, a 404 will be returned.

Thanks to Douglas for his excellent answer. I improved upon it:

app.UseWhen( c => !c.Request.Path.StartsWithSegments( new PathString( "/api" ) ), app =>
    app.UseSpa( spa => {
        //[Snip]
    } ) );
Related