繁体   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