HttpClient - 请求被取消 - 超时时间为100秒

8
我正在使用一个Blazor WebAssembly项目,紧跟着是一个ASP.NET Core Web API和一个共享项目。当运行我的两个项目并打开Postman执行GET请求https://localhost: 5011 / api / WebReport / GatherAllReports时,它会从Blazor WebAssembly中的ReporterRepository进行访问,当它到达第一行var response时停留在那里,然后最终在很长时间后,在Postman上显示以下错误消息...

System.Threading.Tasks.TaskCanceledException:由于配置的HttpClient.Timeout为100秒已过去而取消了请求。

---> System.TimeoutException:操作被取消。

---> System.Threading.Tasks.TaskCanceledException:操作已取消。

---> System.IO.IOException:无法从传输连接中读取数据:I / O操作因线程退出或应用程序请求而中止。

---> System.Net.Sockets.SocketException(995):I / O操作已中止,因为要么是线程退出,要么是应用程序请求。
--- 内部异常堆栈跟踪结尾 ---

at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)

at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token)

at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](TIOAdapter adapter, Memory`1 buffer)

at System.Net.Http.HttpConnection.FillAsync(Boolean async)

at System.Net.Http.HttpConnection.ReadNextResponseHeaderLineAsync(Boolean async, Boolean foldedHeadersAllowed)

at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)

--- 内部异常堆栈跟踪结尾 ---

at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)

at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)

at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)

at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)

at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

at System.Net.Http.HttpClient.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken)

--- 内部异常堆栈跟踪结尾 ---

还有很多其他信息在Postman中返回。

为什么每当我调用Web API控制器的路由端点时会发生这种情况?

Web API 控制器:

using BlazorReports.Services; // this contains the `ReporterRepository.cs` file

[Route("api/[controller]")]
[ApiController]
public class WebReportController : ControllerBase
{
    
    private readonly ReporterRepository_repo;

    public WebReportController (ReporterRepository repo)
    {
        _repo = repo;
    }

    [HttpGet]
    [Route("GatherAllReports")]
    public async Task<IActionResult> Get()
    {
        var reportValues = await _repo.GetAll();
        return Ok(reportValues);
    }

}

用于Web API的startup.cs

public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{

    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebAPI", Version = "v1" });
    });
    services.AddDbContext<ReportContext>(options =>
          options.UseSqlServer(Configuration.GetConnectionString("Default")));

    services.AddScoped<IReporterRepository, ReporterRepository>();

    services.AddHttpClient<IReporterRepository, ReporterRepository>(client =>
        {
            client.BaseAddress = new Uri("https://localhost:5011/");
        });

    services.AddHttpContextAccessor();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebAPI v1"));
    }

    app.UseHttpsRedirection();

    app.UseCors(opt => opt
          .AllowAnyMethod()
          .AllowAnyHeader()
          .SetIsOriginAllowed(origin => true) 
          .AllowCredentials()); 

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

在我的 Blazor WebAssembly 项目中:
IReporterRepository.cs:
public interface IReporterRepository
{
    Task<List<Reports>> GetAll();
}

ReporterRepository.cs:

public class ReporterRepository: IReporterRepository
{
   
    private readonly HttpClient _httpClient;
    private readonly JsonSerializerOptions _options;

    public ReporterRepository(HttpClient httpClient)
    {
        _httpClient = httpClient;
        _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
    }

    public async Task<List<Reports>> GetAll()
    {
        var response = await _httpClient.GetAsync("/ReportsPage/GatherAllReports");
        var content = await response.Content.ReadAsStringAsync();

        if (!response.IsSuccessStatusCode)
        {
            throw new ApplicationException(content);
        }

        var results = JsonSerializer.Deserialize<List<Reports>>(content, _options);
        return results;
    }

}

这听起来像是你的 IReportRepository.GetAll() 方法中存在性能问题,你的问题中没有包含该方法的内容。 - Andrew H
抱歉,我有点困惑你的意思。我确实包含了 IReportRepository.GetAll() 的内容,请检查 ReporterRepository 文件中的 GetAll() 方法。@AndrewH - MarkCo
是我自己的问题还是 _httpClient.GetAsync("/ReportsPage/GatherAllReports") 会创建一个无限循环? /GatherAllReports 的控制器操作调用了 ReporterRepository.GetAll() - Andrew H
是的,我也遇到了那个无限循环的问题,不知道为什么会出现。@AndrewH - MarkCo
2个回答

3
你的 GetAll() 方法在调用自身,导致无限循环。你的代码应该像这样:
public class ReporterAccessLayer
{
   
    private readonly HttpClient _httpClient;
    private readonly JsonSerializerOptions _options;

    public ReporterAccessLayer(HttpClient httpClient)
    {
        _httpClient = httpClient;
        _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
        _repository = repository;
    }

    public async Task<List<Reports>> GetAll()
    {
      try
      {
         return await httpClient.GetAsync<List<Reports>>("/ReportsPage/GatherAllReports");
      }
      catch
      {
         // do exception handling

      } 
    }

}

谢谢!对于这个文件,我应该在我的blazor web assembly项目中实现它,还是需要在我的web api项目中实现它?我意识到我在我的web api项目中做了'services.AddHttpClient <IReporterRepository,ReporterRepository>(client => {client.BaseAddress = new Uri(“https://localhost:5011/”);})',而这应该在我的blazor应用程序中。但我注意到blazor服务器项目只包含一个“startup.cs”文件。 我需要翻译并将此行代码放入我的blazor wa项目的“program.cs”中吗? - MarkCo
我没有 Blazor 服务器项目,我只想使用带有 API 项目的 Blazor WebAssembly。 - MarkCo
1
ReporterAccessLayer 应该在 wasm 中。另外两个应该在 WebAPI 中。 构造函数上不需要。你应该了解一下依赖注入的工作原理。 保持 get all 在 reporter repo 中。那里才是实际获取数据的地方。ReporterAccessLayer 只是用来调用控制器并管理 http 请求周围的后勤。 - Mayur Ekbote
我跟着你说的做了,将 builder.Services.AddHttpClient("localhost:5011", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>(); 添加到了 Blazor WebAssembly 的 program.cs 文件中,但是出现了以下错误:blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set. - MarkCo
然后我尝试了 builder.Services.AddHttpClient("https://localhost:5011/", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)); 但是我得到了相同的错误。我执行了 return await _httpClient.GetFromJsonAsync<Reports[]>("api/ReportsPage/GatherAllReports"); 如果我去 web api 项目并执行 get 请求,它可以工作,只是 blazor 返回了那个 url,但它是正确的 url。 - MarkCo
显示剩余4条评论

1
您的请求超时是因为您创建了一个无限循环。 WebReportController.Get() 调用 ReporterRepository.GetAll(),后者又向 WebReportController.Get() 发出 HTTP 请求,这样就回到了起点。对于您正在使用的模式,在其中控制器操作调用服务类执行业务逻辑(这是一个很好的模式),您将调用数据访问层,其中包含报告数据。在您提供的代码中实际上没有检索任何报告数据。
例如:
public async Task<List<Reports>> GetAll()
{
    var reportA = await _reports.GetReportA();
    var reportB = await _reports.GetReportA();

    // do any further business logic or error handling here

    return new List<Reports> { reportA, reportB };
}

谢谢!这就能解释为什么会发生这种情况了。我需要将这个数据访问层文件包含到我的 Blazor WebAssembly 项目中吗? - MarkCo
很可能是的,因为您的数据应该存储在Web Assembly可以访问的某个地方。 - Andrew H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接