如何在糟糕的.NET Core API路由上抛出404?

时间:2017-05-17 19:42:54

标签: asp.net api asp.net-core asp.net-core-webapi

我有一个.NET Core Web应用程序,它有一个API。我已根据this答案定义了一个中间件类,如下所示:

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate next;
    private readonly ILogger logger;

    public ErrorHandlingMiddleware(RequestDelegate next,
        ILoggerFactory loggerFactory)
    {
        this.next = next;
        logger = loggerFactory.CreateLogger<ErrorHandlingMiddleware>();
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            logger.LogError(0, ex, "An unhandled exception has occurred: " + ex.StackTrace);
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var code = HttpStatusCode.InternalServerError;
        var message = exception.Message;
        if (exception is BadRequestException)
        {
            code = HttpStatusCode.BadRequest;
        }
        else if (exception is NotFoundException)
        {
            code = HttpStatusCode.NotFound;
        }
        else if (exception is NotAuthorizedException)
        {
            code = HttpStatusCode.Forbidden;
        }
        else if (exception is NotAuthenticatedException)
        {
            code = HttpStatusCode.Unauthorized;
        }
        else
        {
            message = "An unexpected error occurred.";
        }

        var result = JsonConvert.SerializeObject(new { error = message });
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)code;
        return context.Response.WriteAsync(result);
    }
}

错误处理仅在代码中抛出异常时处理。不好的路线不会引发异常。问题是,如果我尝试访问不存在的API路由 - 即遵循API路由约定并以“/ api / adfasdf”开头的路径 - API将返回HTML(或错误页面或主页,我忘了)。

我已收到一些建议,要求在context.Response.StatusCode执行后检查await next(context);,但它是200

如何配置我的网络应用以识别错误的API路线并返回404?

更新 这是我在Startup类中加载中间件的地方/时间:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime, IOptions<OidcConfig> oidcConfigOptions)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    // Add Serilog to the logging pipeline
    loggerFactory.AddSerilog();

    app.UseMiddleware<ErrorHandlingMiddleware>();

    if (env.IsLocal())
    {
        app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
        {
            HotModuleReplacement = true
        });
    }

    var oidcConfig = oidcConfigOptions.Value;

    // Configure the app to use Jwt Bearer Authentication
    app.UseJwtBearerAuthentication(new JwtBearerOptions
    {
        AutomaticAuthenticate = true,
        AutomaticChallenge = true,
        Authority = oidcConfig.GetAuthority(),
        Audience = oidcConfig.ResourceAppId,
        TokenValidationParameters = new TokenValidationParameters
        {
            RequireExpirationTime = true,
            RequireSignedTokens = true,
            ValidateAudience = true,
            ValidIssuer = oidcConfig.GetIssuer(),
            ValidateIssuer = true,
            ValidateActor = false,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true
        },
    });

    app.UseSiteIdClaimInjection();

    app.UseStaticFiles();

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

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

    appLifetime.ApplicationStopped.Register(() => this.ApplicationContainer.Dispose());
}

3 个答案:

答案 0 :(得分:3)

对于后人,我得到200的原因是@Nkosi帮助发现与Startup类中的MVC路线定义有关。这是从https://github.com/aspnet/JavaScriptServices自动生成的。

解决方案是将我的路线配置更改为以下内容:

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

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

答案 1 :(得分:1)

引用ASP.NET Core Middleware Fundamentals - Ordering

  

在Configure方法中添加中间件组件的顺序   定义了对请求调用它们的顺序,以及   响应的逆序。这种排序至关重要   安全性,性能和功能。

     

Configure方法(如下所示)添加以下中间件   组件:

     
      
  • 异常/错误处理
  •   
  • 静态文件服务器
  •   
  • 身份验证
  •   
  • MVC
  •   
     

C#

public void Configure(IApplicationBuilder app)
{
    app.UseExceptionHandler("/Home/Error"); // Call first to catch exceptions
                                            // thrown in the following middleware.

    app.UseStaticFiles();                   // Return static files and end pipeline.

    app.UseIdentity();                     // Authenticate before you access
                                           // secure resources.

    app.UseMvcWithDefaultRoute();          // Add MVC to the request pipeline.
}
     

在上面的代码中,UseExceptionHandler是第一个中间件   组件添加到管道中 - 因此,它捕获任何异常   在以后的电话中发生。

根据OP和引用文档中提供的代码,我建议您先提前添加异常或首先将其添加到管道中。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime) {
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();
    loggerFactory.AddSerilog();

    app.UseMiddleware<ErrorHandlingMiddleware>(); // Call first to catch exceptions
                                                  // thrown in the following middleware.    
    if (env.IsLocal()) {
        app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { HotModuleReplacement = true });
    }

    //Bunch of other stuff
}

更新根据评论。

我怀疑管道中的一个中间件导致了这个问题。尝试逐个删除它们并检查是否得到相同的行为以缩小哪一个是罪魁祸首。

答案 2 :(得分:0)

与上面的答案类似,我们在 Angular ASP.NET MVC Core 项目中使用它:

        public virtual void Configure(IHostingEnvironment environment, IApplicationBuilder app)
{

            // configurations...

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

            app.MapWhen(o => !o.Request.Path.Value.StartsWith("/api"), builder =>
            {
                builder.UseMvc(routes =>
                {
                    routes.MapRoute("spa-fallback", "{*anything}", new { controller = "Home", action = "Index" });
                });
            });

}