Csi*_*ert 14 c# asp.net-web-api dotnet-httpclient asp.net-core-webapi httpclientfactory
在我的 .net core Web api 项目中,我想使用外部 API,以便获得预期的响应。
我注册和使用 HttpClient 的方式如下。在启动中,我添加了以下代码,称为命名类型 httpclient 方式。
services.AddHttpClient<IRecipeService, RecipeService>(c => {
c.BaseAddress = new Uri("https://sooome-api-endpoint.com");
c.DefaultRequestHeaders.Add("x-raay-key", "123567890754545645gggg");
c.DefaultRequestHeaders.Add("x-raay-host", "sooome-api-endpoint.com");
});
Run Code Online (Sandbox Code Playgroud)
除此之外,我还有 1 个服务,我在其中注入了 HttpClient。
public class RecipeService : IRecipeService
{
private readonly HttpClient _httpClient;
public RecipeService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<List<Core.Dtos.Recipes>> GetReBasedOnIngAsync(string endpoint)
{
using (var response = await _httpClient.GetAsync(recipeEndpoint))
{
// ...
}
}
}
Run Code Online (Sandbox Code Playgroud)
创建 httpClient 时,如果我将鼠标悬停在对象本身上,则基本 URI/标头会丢失,并且我不明白为什么会发生这种情况。如果有人能透露一些信息,我将不胜感激:)
第一次更新
该服务正在如下所示的控制器之一中使用。该服务由 DI 注入,然后将相对路径解析为该服务(我假设我已经将基本 URL 存储在客户端中)也许我做错了?
namespace ABC.Controllers
{
[ApiController]
[Route("[controller]")]
public class FridgeIngredientController : ControllerBase
{
private readonly IRecipeService _recipeService;
private readonly IMapper _mapper;
public FridgeIngredientController(IRecipeService recipeService, IMapper mapper)
{
_recipeService = recipeService;
_mapper = mapper;
}
[HttpPost("myingredients")]
public async Task<ActionResult> PostIngredients(IngredientsDto ingredientsDto)
{
var readyUrIngredientStr = string.Join("%2", ingredientsDto.ingredients);
var urlEndpoint = $"recipes/findByIngredients?ingredients={readyUrIngredientStr}";
var recipesResponse = await _recipeService.GetRecipeBasedOnIngredientsAsync(urlEndpoint);
InMyFridgeRecipesDto recipesFoundList = new InMyFridgeRecipesDto
{
FoundRecipes = recipesResponse
};
return Ok(recipesFoundList);
}
}
}
Run Code Online (Sandbox Code Playgroud)
有什么建议么?
Jac*_*ack 26
发生这种情况的一个简单但令人沮丧的原因是服务收集语句的顺序。
在 HTTPClient之后分配依赖服务将不起作用,它必须位于之前:
// NOT WORKING - BaseAddress is null
services.AddTransient<Controller1>();
services.AddTransient<Controller2>();
services.AddHttpClient<HttpService>(client =>
{
client.BaseAddress = new Uri(baseAdress);
});
services.AddTransient<HttpService>();
// WORKING - BaseAddress is not null
services.AddTransient<Controller1>();
services.AddTransient<Controller2>();
services.AddTransient<HttpService>();
services.AddHttpClient<HttpService>(client =>
{
client.BaseAddress = new Uri(baseAdress);
});
Run Code Online (Sandbox Code Playgroud)
编辑
正如LIFEfreedom在他们的回答中正确指出的那样:虽然陈述的顺序在这里有影响,但它并不是行为的原因。
以下两条语句都会为该类创建一个临时服务HttpService
:
services.AddTransient<HttpService>();
services.AddHttpClient<HttpService>();
Run Code Online (Sandbox Code Playgroud)
但是,当添加这两个语句时,仅使用最新的语句,覆盖其之前的所有语句。AddHttpClient
在我的示例中,只有当带有基地址配置的语句位于最后时,我才得到预期结果。
Ser*_*rge 11
您将客户端配置为类型化客户端而不是命名客户端。不需要工厂。
您应该在构造函数中显式注入 http 客户端,而不是 http 客户端工厂。
将您的代码更改为:
private readonly HttpClient _httpClient;
public ReService(HttpClient httpClient) {
_httpClient = httpClient;
}
public async Task<List<Core.Dtos.Re>> GetReBasedOnIngAsync(string endpoint)
{
///Remove this from your code
var client = _httpClientFactory.CreateClient(); <--- HERE the base URL/Headers are missing
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(endpoint)
};
//////
using (var response = await _httpClient.GetAsync(endpoint))
{
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
根据最新的 MS 文档,在这种情况下仅需要键入客户端注册。将您的启动修复为:
// services.AddScoped<IReService, ReService>(); //<-- REMOVE. NOT NEEDED
services.AddHttpClient<IReService, ReService>(c => ...
Run Code Online (Sandbox Code Playgroud)
但您仍然可以尝试添加基地址,请添加尾部斜杠(并让我们知道它是否仍然有效):
services.AddHttpClient<IReService, ReService>(c => {
c.BaseAddress = new Uri("https://sooome-api-endpoint.com/");
});
Run Code Online (Sandbox Code Playgroud)
如果问题仍然存在,我建议您尝试命名 http 客户端。
好吧,我会回答我的帖子,因为使用建议的 TYPED 方法会导致 httpClient 内未设置值的问题,EG BaseAddress 始终为空。
在启动时我试图使用类型化的 httpclient 例如
services.AddHttpClient<IReService, ReService>(c => ...
Run Code Online (Sandbox Code Playgroud)
但我没有这样做,而是选择与指定客户端一起使用。这意味着在启动时我们需要像这样注册httpclient
services.AddHttpClient("recipeService", c => {
....
Run Code Online (Sandbox Code Playgroud)
然后在服务本身中,我使用了 HttpClientFactory,如下所示。
private readonly IHttpClientFactory _httpClientFactory;
public RecipeService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<List<Core.Dtos.Recipes>> GetRecipeBasedOnIngredientsAsync(string recipeEndpoint)
{
var client = _httpClientFactory.CreateClient("recipeService");
using (var response = await client.GetAsync(client.BaseAddress + recipeEndpoint))
{
response.EnsureSuccessStatusCode();
var responseRecipies = await response.Content.ReadAsStringAsync();
var recipeObj = ConvertResponseToObjectList<Core.Dtos.Recipes>(responseRecipies);
return recipeObj ?? null;
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
15472 次 |
最近记录: |