【问题标题】:How to throwing 404 on bad .NET Core API route?如何在错误的 .NET Core API 路由上抛出 404?
【发布时间】:2017-10-17 10:15:45
【问题描述】:

我有一个具有 API 的 .NET Core Web 应用程序。我已经定义了一个基于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(或错误页面或主页,我忘了)。

我收到了一些建议在await next(context); 执行后检查context.Response.StatusCode,但它是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());
}

【问题讨论】:

  • 您可以尝试在调用await next(context); 后立即检查context.Response.StatusCode
  • @Nkosi 这就是该答案的 cmets 中所建议的。它返回一个200
  • 不要滥用该/流控制的异常。它们是针对例外而不是针对预期结果的。
  • @im1dermike,您如何以及在管道中的何处添加中间件?这应该很早就添加到管道中,如果不是,首先添加到管道中。
  • @Nkosi 已更新。当我将其设置为 Configure() 方法中的绝对第一行时,我仍然得到 200。

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


【解决方案1】:

对于后人来说,@Nkosi 帮助发现我获得 200 分的原因与 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" });
    });
});

【讨论】:

    【解决方案2】:

    引用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
    }
    

    更新基于 cmets。

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

    【讨论】:

    • await next(context); 之后,我仍然从context.Response.StatusCode 获得200
    • 我怀疑管道下游的中间件之一导致了这个问题。尝试一一删除它们并检查您是否得到相同的行为,以缩小罪魁祸首。
    • 宾果游戏!看起来是MapSpaFallbackRoute 添加的github.com/aspnet/JavaScriptServices 行。
    【解决方案3】:

    与上述答案类似,我们在 AngularASP.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" });
                    });
                });
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-25
      • 2020-06-05
      • 2020-09-23
      • 1970-01-01
      • 1970-01-01
      • 2018-02-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多