[英]Blazor wasm Web API response is an HTML string
好的,我有一个调用服务器端 web api 端点的 wasm 应用程序。 问题是我从端点的 /wwwroot 目录中获取 index.html 页面作为答案。 但是当我使用 Postman 寻址端点时,我得到了 json 的预期答案。 好的,我将展示如何使用我的代码执行此操作。
在这里,当在表单字段中输入搜索文本时,我调用 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);
}
}
}
有了这个,我调用了不同的 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
}
}
当我使用 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
}
}
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;
});
}
}
我希望我已经提供了足够的信息,以便能够告诉我为什么这不起作用。
好的。 问题是表单控件的绑定仅在我手动进行选择更改时才起作用。 在渲染不工作时设置一个选定项。
<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.