【发布时间】:2020-04-27 07:56:34
【问题描述】:
在我的 Blazor WASM 应用程序中,我编写了一个(客户端)服务类,其中包含一种对 Web API 进行 API 调用的方法。服务器将返回 IEnumerable<WeatherForecast> 的预期结果或 Microsoft.AspNetCore.Mvc.ProblemDetails 对象来解释问题所在。
调用该方法时,UI (FetchData.razor) 会传递一个Action<IEnumerable<WeatherForecast>> 和一个Action<ProblemDetails>。根据服务器返回的内容,应该只执行这些操作中的一个。这允许服务类根据 API 调用的反序列化 JSON 结果选择要执行的操作。
用法(在 FetchData.razor 中):
@page "/fetchdata"
@using BlazorApp1.Shared
@inject HttpClient Http
@inject WeatherForecastsService Service
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private IEnumerable<WeatherForecast> forecasts;
protected override async Task OnInitializedAsync()
{
await Service.GetAllAsync(
success => forecasts = success,
problem => Console.WriteLine("Handle this problem: " + problem.Detail));
}
}
我在下面的实施尝试不起作用。我确信 API 调用正在到达正确的 API 端点并返回 JSON,但是我的 razor 页面没有填充 WeatherForecast,它也没有将问题详细信息写入控制台。在 Blazor WASM 中调试(尽管有很大改进)仍然相当困难。
我几天来一直在摆弄这段代码,但失败了。谁能帮我看看我做错了什么?
public class WeatherForecastsService : ServiceBase
{
public WeatherForecastsService(
HttpClient client) : base(client)
{
}
public async Task GetAllAsync(
Action<IEnumerable<WeatherForecast>> actionOnSuccess,
Action<ProblemDetails> actionOnFailure,
CancellationToken cancellationToken = default)
{
await GetManyAsync("weatherforecast",
actionOnSuccess,
actionOnFailure,
cancellationToken);
}
}
public abstract class ServiceBase
{
public ServiceBase(HttpClient client)
{
Client = client;
}
protected HttpClient Client
{
get;
}
protected virtual async Task GetManyAsync<TExpected>(
string path,
Action<IEnumerable<TExpected>> actionOnSuccess,
Action<ProblemDetails> actionOnProblem,
CancellationToken cancellationToken = default)
where TExpected : class
{
string json = await GetJsonAsync(path, cancellationToken);
ProblemDetails? problem = Deserialize<ProblemDetails>(json);
if (problem is { })
{
var taskOnProblem = TaskFromAction(actionOnProblem, problem);
await taskOnProblem;
}
else
{
IEnumerable<TExpected>? expected = Deserialize<IEnumerable<TExpected>>(json);
expected = EnsureNotNull(expected);
var taskOnSuccess = TaskFromAction(actionOnSuccess, expected);
await taskOnSuccess;
}
}
private Task TaskFromAction<T>(Action<T> action, T state)
{
return new Task(ActionOfObjectFromActionOfT(action), state);
}
private Action<object> ActionOfObjectFromActionOfT<T>(Action<T> actionOfT)
{
return new Action<object>(o => actionOfT((T)o));
}
private IEnumerable<T> EnsureNotNull<T>(IEnumerable<T>? enumerable)
{
if (enumerable is null)
{
enumerable = new List<T>();
}
return enumerable;
}
private async Task<string> GetJsonAsync(string path, CancellationToken cancellationToken = default)
{
var response = await Client.GetAsync(path, cancellationToken);
return await response.Content.ReadAsStringAsync();
}
private T? Deserialize<T>(string json)
where T : class
{
try
{
return JsonSerializer.Deserialize<T>(json, null);
}
catch (JsonException)
{
return default;
}
}
}
可以在此处找到我在此问题上的失败尝试的最小可重现示例: https://github.com/BenjaminCharlton/AsyncBlazorRepro
谢谢!
【问题讨论】:
-
您好 Henk,感谢您的关注!到目前为止,我在浏览器中得到的最好结果是看到它显示正确数量的 WeatherForecast,但它们的字符串属性全部为空白,并且它们的日期属性全部显示
DateTime.MinValue。TaskFromAction是我试图完成这项工作的几个实验之一,因为我认为问题可能与同步性有关,所以我尝试异步运行这些操作。我还尝试通过Func<TExpected, Task>而不是Action<TExpected>,它的效果没有好坏之分。 -
知道了! :-D 我按照您的要求仔细查看了浏览器控制台,发现了一些我以前没有想到的东西:什么都没有。那里什么都没有。所以我开始思考在我无法控制的幕后发生的事情:反序列化。
Microsoft.AspNetCore.Components.HttpClientJsonExtensions中的方法都将JsonSerializerOptions传递给Deserialize方法,但在我的代码中,我只是传递了null,因为我认为这并不重要。由于区分大小写,JsonSerializer忽略了每个属性!
标签: c# asp.net-core async-await blazor blazor-client-side