簡體   English   中英

How to map fallback in ASP .NET Core Web API so that Blazor WASM app only intercepts requests that are not to the API

[英]How to map fallback in ASP .NET Core Web API so that Blazor WASM app only intercepts requests that are not to the API

我有一個 Blazor WebAssembly 解決方案,其中包含一個客戶端項目、服務器項目和共享項目,基於 Microsoft 的默認解決方案模板。 我正在使用 Google Chrome 在 Visual Studio 2019 預覽版中進行編輯和調試。

該解決方案開箱即用,只有一個啟動項目,即服務器應用程序。 該服務器應用程序具有對客戶端應用程序的項目引用。 您可以通過在服務器項目屬性中選中“啟用 SSL”將其設置為使用 HTTPS,我已經做到了。

當您單擊調試時,它可以完美運行。

現在我想更改它,以便 Blazor WASM 應用程序僅響應來自https://localhost:44331的請求,而不是對https://localhost:44331/的請求這些請求應由服務器應用程序的 API Controller 端點處理。 So, if somebody visits https://localhost:44331/api/something , and no such API endpoint exists, they should receive a 404 error code from the API and not be routed to the usual Blazor page saying "Sorry, there's nothing at這個地址。”

我想使用 URL 的這個額外的“/api”部分來保持對 API 的請求與頁面請求分開。 我認為這將更接近生產中的正常設置。 我希望很清楚我想要做什么。

這是一個帶有路由屬性的示例 Controller 聲明:

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

        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            //etc.
        }
///etc.
    }
}

這是我在 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.
    }
}

請問有人知道如何讓這個工作按我的意願工作嗎?

回顧一下這個問題,當有人請求:

https://yourapp.com/api/someendpoint

/api/someendpoint找不到,它們被帶到 Blazor 頁面。 這種默認行為很奇怪。 對於以/api開頭的請求,他們期待一個 HTTP 狀態代碼,並且可能是一個 JSON ZA8CFDE6331BD59EB2AC96F8911C4B66ZDB6Z,但是他們得到了 HTTP 狀態代碼。 也許他們甚至不使用您的應用程序。 也許他們甚至不是人類(更有可能他們是一個軟件)。

這就是您向他們發送 HTTP 狀態代碼的方式。 在您的控制器上:

[Route("api/[controller]")]
public class SampleController : ControllerBase
{
    // ...
}

在 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;
}

很確定這應該有效:

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

我認為這意味着僅匹配路徑不以 api 開頭的 URL。

如果您從 Blazor WASM 托管解決方案開始,您將獲得一個示例,只需啟動

dotnet new blazorwasm --hosted

它創建了一個包含 3 個項目的解決方案:

|-- 客戶
|-- 服務器
|-- 共享

在服務器Startup class 中,中間件管道設置如下:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseWebAssemblyDebugging();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseBlazorFrameworkFiles();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapFallbackToFile("index.html");
    });
}

controller 使用Route屬性定義其路由。 如果您想在/api/{controller}托管 controller,請使用Route屬性值api/[controller]

[ApiController]
[Route("api/[controller]")]
public class WeatherForecastController : ControllerBase

使用來自@Darragh 的代碼我得到以下錯誤:

endpoints.MapFallbackToPage("{ path:regex(^(?.api). $)}", "index.html");

System.ArgumentException:''index.html' 不是有效的頁面名稱。 頁面名稱是相對於 Razor 頁面根目錄的路徑,以正斜杠 ('/') 開頭,並且不包含文件擴展名,例如“/Users/Edit”。 (參數'pageName')'

在此處輸入圖像描述

如果我像原始代碼一樣使用MapFallbackToFile而不是MapFallbackToPage ,則代碼將運行。

但是,當我測試正則表達式時,它匹配所有內容,包括 API URL:

https://regex101.com/r/nq7FCi/1

我的正則表達式看起來像這樣: ^(?.?*?(:.\/api\/)).*$基於這個答案:

https://stackoverflow.com/a/23207219/3850405

https://regex101.com/r/qmftyc/1

測試時它無論如何都不起作用,並且包含/api/的 url 被重定向到index.html

我的最終代碼基於@benjamin 答案,但最后使用的是原始MapFallbackToFile

app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
    endpoints.MapControllers();
    endpoints.Map("api/{**slug}", HandleApiFallback);
    endpoints.MapFallbackToFile("index.html");
});

private Task HandleApiFallback(HttpContext context)
{
    context.Response.StatusCode = StatusCodes.Status404NotFound;
    return Task.CompletedTask;
}

我已經用 Blazor WebAssembly .NET 5 試過這個。發布到 IIS 后,之前建議的解決方案不起作用。

此處提供了此問題的答案。 測試和工作。

不久:

編輯wwwroot\service-worker.published.js文件並添加要排除的路徑,在本例中為/api/

const shouldServeIndexHtml = event.request.mode === 'navigate' &&
                             !event.request.url.includes('/api/')

您可以通過僅針對不以/api開頭的路徑顯式映射 Blazor 回退來解決此問題,然后僅為以 / /api開頭的路徑映射 api 路徑,就像我在此回答我的所有者問題中提到的那樣。 這帶來的好處是,如果您嘗試POSTGET api 方法,而不是僅返回404 ,您將獲得正確的 api 響應405 ,或者 Z8A5DA52ED12647D359E70 給定的任何其他錯誤通常會返回請求。

//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();
    });
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM