簡體   English   中英

Blazor wasm Web API 響應是一個 HTML 字符串

[英]Blazor wasm Web API response is an HTML string

好的,我有一個調用服務器端 web api 端點的 wasm 應用程序。 問題是我從端點的 /wwwroot 目錄中獲取 index.html 頁面作為答案。 但是當我使用 Postman 尋址端點時,我得到了 json 的預期答案。 好的,我將展示如何使用我的代碼執行此操作。

客戶端數據流

搜索.razor頁面

在這里,當在表單字段中輸入搜索文本時,我調用 Web API 端點。 這按預期工作。

... Snip

// UPDATED INFO
<div class="form-group">
    <label for="objectType">Select object type</label>
    <select id="objectType" class="form-control" @bind="@_searchByNameObjectTypeUuid">
        @if (_objectTypes != null)
        {
            @foreach (var objectType in _objectTypes)
            {
                @if (objectType.TypeName == "Music")
                {
                    @* This selection value is not set. But why?
                    <option value="@objectType.Uuid.ToString("D")" selected="selected">@objectType.TypeName</option>
                }
                else
                {
                    <option value="@objectType.Uuid.ToString("D")">@objectType.TypeName</option>
                }
            }
        }
    </select>
</div>
// UPDATED INFO END

<div class="form-group">
    <label for="objectName">Object name:<br/></label>
    <input type="text" class="form-control" id="objectName" @onkeydown="@SearchByNameOnEnter" @bind-value="@_searchByNameObjectNamePhrase" @bind-value:event="oninput"/>
</div>

...Snip

@code {
    private string _searchByNameObjectNamePhrase = string.Empty;

    private async Task SearchByNameOnEnter(KeyboardEventArgs e)
    {
        if ((e.Code == "Enter" || e.Code == "NumpadEnter") && !string.IsNullOrWhiteSpace(_searchByNameObjectNamePhrase))
        {
            _searchResult = await ServerApiClient.SearchObjectsByNamePhrase(_searchByNameObjectTypeUuid, _searchByNameObjectNamePhrase);
        }
    }
}

ServerApiClientService.cs Web API 客戶端服務

有了這個,我調用了不同的 Web API 端點,它們從后端的數據庫中獲取數據。

GetDdsObjectAttributeValueCount()方法按預期工作。

SearchObjectsByNamePhrase(string objTypeUuid, string searchTermPhrase)方法將文件 /wwwroot/index.html 作為答案發送給我。 (在代碼中顯示注釋以獲取詳細信息)

namespace WebAssembly.Client.Services
{
    public class ServerApiClientService : IServerApiClientService
    {
        #region Constants - Static fields - Fields
        private readonly HttpClient _httpClient;
        #endregion
        
        #region Constructors and Destructors
        public ServerApiClientService(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }
        #endregion
        
        #region Methods
        // This endpoint request work as expected
        public async Task<IEnumerable<ObjectAttributeValueCount>> GetDdsObjectAttributeValueCount()
        {
            IEnumerable<ObjectAttributeValueCount> result =
                await _httpClient
                    .GetFromJsonAsync<List<ObjectAttributeValueCount>>("/api/DdsDashboard/GetDdsObjectAttributeValueCount");
            return (result ?? Array.Empty<ObjectAttributeValueCount>()).AsQueryable();
        }

        // This endpoint request NOT work as expected
        public async Task<IEnumerable<SearchResultItem>> SearchObjectsByNamePhrase(string objTypeUuid, string searchTermPhrase)
        {
            // For test i have called as string and i get HTML response. wwwroot/index.html is comming back.
            var asJsonString =
                await _httpClient
                        .GetStringAsync($"/api/DdsDashboard/SearchObjectsByNamePhrase/{objTypeUuid}/{searchTermPhrase}");

            // And here i get the exception "System.Text.Json.JsonReaderException"
            // '<' is an invalid start of a value
            IEnumerable<SearchResultItem> result =
                await _httpClient
                    .GetFromJsonAsync<List<SearchResultItem>>($"/api/DdsDashboard/SearchObjectsByNamePhrase/{objTypeUuid}/{searchTermPhrase}");

            return (result ?? Array.Empty<SearchResultItem>()).AsQueryable();
        }
        #endregion
    }
}

服務器端數據流

DdsDashboardController.cs 作為 Web API Controller

當我使用 Postman 解決這些問題時,此 controller 中的所有方法(路線)都能正常工作。

路由[HttpGet("GetDdsObjectAttributeValueCount")]和路由[HttpGet("GetDdsObjectTypeStatistic")]也適用於 ServerApiClientService.cs。

只有路線 [HttpGet ("SearchObjectsByNamePhrase / {objTypeId} / {searchTerm}")] 僅適用於 Postman。

namespace WebAssembly.Server.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class DdsDashboardController : ControllerBase
    {
    #region Constants - Static fields - Fields

        private readonly IDdsRepository _repository;

    #endregion

    #region Constructors and Destructors

        public DdsDashboardController(IDdsRepository repo)
        {
            _repository = repo;
        }

    #endregion

    #region Methods

        [HttpGet("GetDdsObjectAttributeValueCount")]
        public async Task<IEnumerable<ObjectAttributeValueCount>> GetDdsObjectAttributeValueCount()
        {
            return await _repository.GetDdsObjectAttributeValueCount();
        }

        [HttpGet("GetDdsObjectTypeStatistic")]
        public async Task<IEnumerable<ObjectTypeStatistic>> GetDdsObjectTypeStatistic()
        {
            return await _repository.GetDdsObjectTypeStatistic();
        }

        // This method is called and worked as expected. When i call this endpoint with Postman all is fine. Correct JSON response.
        [HttpGet("SearchObjectsByNamePhrase/{objTypeId}/{searchTerm}")]
        public async Task<IEnumerable<SearchResultItem>> SearchObjectsByNamePhrase(string objTypeId, string searchTerm)
        {
            // Correct result from my database. I have checked with an breakpoint.
            var result = await _repository.SearchObjectsByNamePhrase(objTypeId, searchTerm);

            return result;
        }

    #endregion
    }
}

啟動.cs

配置方法

public void Configure(IApplicationBuilder app)
{

    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.MapRazorPages();
                         endpoints.MapControllers();
                         endpoints.MapFallbackToFile("index.html");
                     });
}

配置服務方法

public void ConfigureServices(IServiceCollection services)
{
    SqlMapper.AddTypeHandler(new MySqlGuidTypeHandler());
    SqlMapper.RemoveTypeMap(typeof(Guid));
    SqlMapper.RemoveTypeMap(typeof(Guid?));

    services.AddControllersWithViews();
    services.AddRazorPages();

    services.AddScoped<IDdsRepository, DdsRepository>();

    var dbConnectionSettings = new DdsDbConnectionConfiguration(Configuration.GetSection("DdsDbSettings"));
    services.AddSingleton(dbConnectionSettings);

    if (!Env.IsDevelopment())
    {
        services.AddHttpsRedirection(options =>
                                     {
                                         options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect;
                                         options.HttpsPort          = 443;
                                     });
    }
}

請求 Postman

郵遞員結果

我希望我已經提供了足夠的信息,以便能夠告訴我為什么這不起作用。

更新

好的。 問題是表單控件的綁定僅在我手動進行選擇更改時才起作用。 在渲染不工作時設置一個選定項。

<div class="form-group">
    <label for="objectType">Select object type</label>
    <select id="objectType" class="form-control" @bind="@_searchByNameObjectTypeUuid">
        @if (_objectTypes != null)
        {
            @foreach (var objectType in _objectTypes)
            {
                @if (objectType.TypeName == "Music")
                {
                    <option value="@objectType.Uuid.ToString("D")" selected="selected">@objectType.TypeName</option>
                }
                else
                {
                    <option value="@objectType.Uuid.ToString("D")">@objectType.TypeName</option>
                }
            }
        }
    </select>
</div>

這就是未設置_searchByNameObjectTypeUuid值的原因。 並使用該endpoints.MapFallbackToFile(" index.html ")

我在OnInitializedAsync()方法中設置了_searchByNameObjectTypeUuid的值,我還加載了_objectTypes

protected override async Task OnInitializedAsync()
{
    _objectTypes = await DdsApiClient.GetObjectTypes();
    _searchByNameObjectTypeUuid = _objectTypes.SingleOrDefault(x => x.TypeName == "Music")?.Uuid.ToString("D");
}

如果有人知道如何在渲染時使用 foreach 循環設置值,我將不勝感激。

感謝@Neil W 的幫助。

我沒有直接回答您的問題,但是當我第一次開始遇到來自 Blazor wasm 客戶端的 WebAPI 挑戰時,我創建了一個客戶端 API 基礎 class,因此:

public abstract class ClientAPI
{
    protected readonly HttpClient Http;
    private readonly string BaseRoute;

    protected ClientAPI(string baseRoute, HttpClient http)
    {
        BaseRoute = baseRoute;
        Http = http;
    }

    protected async Task<TReturn> GetAsync<TReturn>(string relativeUri)
        => await ProcessHttpResponse<TReturn>(await Http.GetAsync($"{BaseRoute}/{relativeUri}"));

    protected async Task<TReturn> PostAsync<TReturn, TRequest>(string relativeUri, TRequest request)
        => await ProcessHttpResponse<TReturn>(await Http.PostAsJsonAsync($"{BaseRoute}/{relativeUri}", request));

    private static async Task<TReturn> ProcessHttpResponse<TReturn>(HttpResponseMessage response)
    {
        if (response.IsSuccessStatusCode)
            return await response.Content.ReadFromJsonAsync<TReturn>();

        string msg = await response.Content.ReadAsStringAsync();
        Console.WriteLine(msg);
        throw new Exception(msg);
    }
}

然后我的派生客戶端 API class 將在基礎 class 上調用 GetAsync。 然后,這將解決 Json 響應,或者如果 HttpResponseMessage 有失敗狀態代碼,它將記錄錯誤。

從派生的 class 使用,如下所示:

public class BackOfficeClientAPI : ClientAPI
{
    public BackOfficeClientAPI(HttpClient http) : base("api/backoffice", http) { }

    public async Task<IEnumerable<Category>> GetCategoriesAsync(Guid organisationId)
        => await GetAsync<IEnumerable<Category>>($"getcategories?organisationId={organisationId}");

    public async Task<Category> AddCategoryAsync(AddCategoryRequest request)
        => await PostAsync<Category, AddCategoryRequest>("addcategory", request);

PS。 我使用的是查詢字符串而不是路由參數,但原理是一樣的。

我發現捕獲這種類型的異常是一個很好的模式。

暫無
暫無

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

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