如何在 ASP .NET Core Web API 中映射回退,以便 Blazor WASM 应用程序仅拦截不发送到 API 的请求

ben*_*min 8 iis-express asp.net-core blazor asp.net-core-staticfile blazor-client-side

我有一个 Blazor WebAssembly 解决方案,其中包含客户端项目、服务器项目和共享项目,基于 Microsoft 的默认解决方案模板。我正在使用 Google Chrome 在 Visual Studio 2019 预览版中进行编辑和调试。

该解决方案开箱即用,只有一个启动项目,即服务器应用程序。该服务器应用程序具有对客户端应用程序的项目引用。您可以通过在服务器项目属性中选中“启用 SSL”来将其设置为使用 HTTPS,我已经这样做了。

当您单击调试时,它可以完美运行。

现在我想更改它,以便 Blazor WASM 应用程序仅响应来自https://localhost:44331 的请求,而不响应对https://localhost:44331/api 的请求。这些请求应该由服务器应用程序的 API 控制器端点处理。因此,如果有人访问https://localhost:44331/api/something,并且不存在这样的 API 端点,他们应该从 API 收到 404 错误代码,并且不会被路由到通常的 Blazor 页面说“对不起,没有任何东西在这个地址。”

我想使用 URL 的这个额外的“/api”部分来保持对 API 的请求与对页面的请求分开。我认为这将更接近生产中的正常设置。我希望很清楚我要做什么。

这是一个带有路由属性的示例控制器声明:

namespace BlazorApp2.Server.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        // Etc.

        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            //etc.
        }
///etc.
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我在 Startup.cs 中尝试过的,但它不起作用。任何人都可以提出一些取悦的建议吗?

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Etc.
    app.UseStatusCodePages();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapControllers();
        // The line commented out below is the out-of-the-box behaviour for a Blazor WASM app with ASP NET Core API. This is the line I want to replace.
        // endpoints.MapFallbackToFile("index.html");

        // The line below is my (failed) attempt to get the behaviour I want.
        endpoints.MapFallback(HandleFallback);
    });
}

private async Task HandleFallback(HttpContext context)
{
    var apiPathSegment = new PathString("/api"); // Find out from the request URL if this is a request to the API or just a web page on the Blazor WASM app.

    bool isApiRequest = context.Request.Path.StartsWithSegments(apiPathSegment);

    if (!isApiRequest)
    {
        context.Response.Redirect("index.html"); // This is a request for a web page so just do the normal out-of-the-box behaviour.
    }
    else
    {
        context.Response.StatusCode = StatusCodes.Status404NotFound; // This request had nothing to do with the Blazor app. This is just an API call that went wrong.
    }
}
Run Code Online (Sandbox Code Playgroud)

有人知道如何按照我的意愿工作吗?

DLe*_*Leh 12

您可以通过仅显式映射不以 和 开头的路径的 Blazor 回退来解决此问题/api,然后只映射以 开头的 api 路径/api,就像我在对我的所有者问题的回答中提到的那样。这样做的好处是,您将获得正确的 api 响应,或者 api 通常在给定请求时返回的任何其他错误,而不是仅仅返回 if 您404尝试POST使用 api方法。GET405

//explicitly only use blazor when the path doesn't start with api
app.MapWhen(ctx => !ctx.Request.Path.StartsWithSegments("/api"), blazor =>
{
    blazor.UseBlazorFrameworkFiles();
    blazor.UseStaticFiles();

    blazor.UseRouting();
    blazor.UseEndpoints(endpoints =>
    {
        endpoints.MapFallbackToFile("index.html");
    });
});

//explicitly map api endpoints only when path starts with api
app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments("/api"), api =>
{
    //if you are not using a blazor app, you can move these files out of this closure
    api.UseStaticFiles();
    api.UseRouting();
    api.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
});
Run Code Online (Sandbox Code Playgroud)


ben*_*min 10

回顾一下这个问题,当有人提出请求时:

https://yourapp.com/api/someendpoint
Run Code Online (Sandbox Code Playgroud)

并且/api/someendpoint无法找到,它们会被带到 Blazor 页面。这种默认行为很奇怪。对于以 开头的请求/api,他们期望得到一个 HTTP 状态代码,也可能是一个 JSON 对象,但结果却得到了 HTML。也许他们甚至不使用您的应用程序。也许他们甚至不是人(更有可能他们是一个软件)。

这就是您向他们发送 HTTP 状态代码的方式。在您的控制器上:

[Route("api/[controller]")]
public class SampleController : ControllerBase
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

在 Startup.cs 中:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...
    app.UseStaticFiles();
    app.UseRouting();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapControllers();
        endpoints.Map("api/{**slug}", HandleApiFallback);
        endpoints.MapFallbackToFile("{**slug}", "index.html");
    });
}

private Task HandleApiFallback(HttpContext context)
{
    context.Response.StatusCode = StatusCodes.Status404NotFound;
    return Task.CompletedTask;
}
Run Code Online (Sandbox Code Playgroud)


Dar*_*agh 10

很确定这应该有效:

endpoints.MapFallbackToFile("{*path:regex(^(?!api).*$)}", "index.html"); // don't match paths beginning with api
Run Code Online (Sandbox Code Playgroud)

我认为这意味着只匹配路径不以 api 开头的 URL。