Note
ãã®ããŒãžã«ã¢ã¯ã»ã¹ããã«ã¯ãæ¿èªãå¿ èŠã§ãã ãµã€ã³ã€ã³ãŸãã¯ãã£ã¬ã¯ããªã®å€æŽã詊ãããšãã§ããŸãã
ãã®ããŒãžã«ã¢ã¯ã»ã¹ããã«ã¯ãæ¿èªãå¿ èŠã§ãã ãã£ã¬ã¯ããªã®å€æŽã詊ãããšãã§ããŸãã
ãã®èšäºã§ã¯ãIHttpClientFactory ã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšããŠãäŸåé¢ä¿ã®æ¿å
¥ (DI)ããã°èšé²ãæ§æãªã©ã®.NETã®åºç€ãæã€HttpClientåãäœæããæ¹æ³ã«ã€ããŠèª¬æããŸãã
HttpClient 㯠HTTP èŠæ±ãè¡ãã Uriã«ãã£ãŠèå¥ããã Web ãªãœãŒã¹ããã® HTTP å¿çãåŠçããŸãã HTTP ãããã³ã«ã«ãã£ãŠããã¹ãŠã®ã€ã³ã¿ãŒããã ãã©ãã£ãã¯ã®å€§éšåãæ§æãããŸãã
IHttpClientFactoryã䜿çšããã«ã¯ãMicrosoft.Extensions.Http NuGet ããã±ãŒãžãåç
§ããŸãã ãã¡ã¯ããªã¯ãã«ã¹ã¿ã æ§æã§HttpClientã€ã³ã¹ã¿ã³ã¹ãäœæãã.NETã¯ãŒã¯ããŒãã§å埩æ§ã®ããäžæçãªé害åŠçãµãŒãããŒã㣠ããã«ãŠã§ã¢ã䜿çšã§ããããã«ããŸãã
èŠå
ã¢ããªã§ Cookie ãå¿ èŠãªå Žåã¯ã IHttpClientFactoryã䜿çšããªãããšããå§ãããŸãã HttpMessageHandler ã€ã³ã¹ã¿ã³ã¹ã®ããŒã«ã䜿çšãããšãCookieContainer ãªããžã§ã¯ããå ±æãããããã«ãªããŸãã äºæããªã CookieContainer å ±æã«ãããã¢ããªã±ãŒã·ã§ã³ã®é¢é£ã®ãªãéšåéã§ Cookie ãæŒæŽ©ããå¯èœæ§ããããŸãã ããã«ã HandlerLifetime ãæéåãã«ãªããšãã³ãã©ãŒããªãµã€ã¯ã«ãããŸããã€ãŸãããã® CookieContainer ã«æ ŒçŽãããŠãããã¹ãŠã® Cookie ã倱ãããŸãã ã¯ã©ã€ã¢ã³ãã管çããå¥ã®æ¹æ³ã«ã€ããŠã¯ããHTTP ã¯ã©ã€ã¢ã³ãã®äœ¿çšã«é¢ããã¬ã€ãã©ã€ã³ããåç §ããŠãã ããã
éèŠ
HttpClient ã«ãã£ãŠäœæããã IHttpClientFactory ã€ã³ã¹ã¿ã³ã¹ã®æå¹æéã®ç®¡çã¯ãæåã§äœæãããã€ã³ã¹ã¿ã³ã¹ãšã¯å®å
šã«ç°ãªããŸãã ãã®æ¹æ³ã¯ã ã«ãã£ãŠäœæãããIHttpClientFactoryã¯ã©ã€ã¢ã³ãã䜿ããã ãèšå®ãããPooledConnectionLifetimeã¯ã©ã€ã¢ã³ãã䜿ãããšã§ãã 詳现ã«ã€ããŠã¯ããHttpClient ã®æå¹æéã®ç®¡çãã»ã¯ã·ã§ã³ãšãHTTP ã¯ã©ã€ã¢ã³ãã®äœ¿çšã«é¢ããã¬ã€ãã©ã€ã³ããåç
§ããŠãã ããã
IHttpClientFactory å
ãã®èšäºã§æäŸããããã¹ãŠã®ãµã³ãã« ãœãŒã¹ ã³ãŒãã«ã¯ãnuGet ããã±ãŒãž Microsoft.Extensions.Http ãš Microsoft.Extensions.Hosting ãå¿
èŠã§ãã ããã«ãã³ãŒã ãµã³ãã«ã§ã¯ãç¡æã® GET API ãããŠãŒã¶ãŒ Todo ãªããžã§ã¯ããååŸããããã® HTTP èŠæ±ã®äœ¿ç𿹿³ã玹ä»ããŠããŸãã
AddHttpClient æ¡åŒµã¡ãœããã®ãããããåŒã³åºããšãIHttpClientFactory ãšé¢é£ãµãŒãã¹ã IServiceCollection ã«è¿œå ãããŸãã
IHttpClientFactory åã«ã¯æ¬¡ã®ãããªå©ç¹ããããŸãã
-
HttpClientã¯ã©ã¹ã DI 察å¿åãšããŠå ¬éããŸãã - è«ç
HttpClientã€ã³ã¹ã¿ã³ã¹ã®ååä»ããšæ§æãäžå åããŸãã -
HttpClientã§ã®ãã³ãã©ãŒã®ããªã²ãŒãã«ãããéä¿¡ããã«ãŠã§ã¢ã®æŠå¿µãäœç³»åãããŸãã -
HttpClientã§ã®ãã³ãã©ãŒã®ããªã²ãŒããå©çšããããã«ãPolly ããŒã¹ã®ããã«ãŠã§ã¢ã«å¯Ÿããæ¡åŒµã¡ãœãããæäŸãããŸãã - åºã«ãªã£ãŠãã HttpClientHandler ã€ã³ã¹ã¿ã³ã¹ã®ãã£ãã·ã¥ãšæå¹æéã管çãããŸãã èªå管çã«ããã
HttpClientã®æå¹æéãæåã§ç®¡çãããšãã®äžè¬çãªãã¡ã€ã³ ããŒã ã·ã¹ãã (DNS) ã®åé¡ãçºçããªããªããŸãã - ãã¡ã¯ããªã«ãã£ãŠäœæãããã¯ã©ã€ã¢ã³ãããéä¿¡ããããã¹ãŠã®èŠæ±ã«å¯Ÿãã(ILogger ã«ãã£ãŠ) æ§æå¯èœãªãã° ãšã¯ã¹ããªãšã³ã¹ã远å ããŸãã
å©çšãã¿ãŒã³
ã¢ããªã§ IHttpClientFactory ã䜿çšããã«ã¯è€æ°ã®æ¹æ³ããããŸãã
- åºæ¬çãªäœ¿ç𿹿³
- ååä»ãã¯ã©ã€ã¢ã³ã
- åæå®ãããã¯ã©ã€ã¢ã³ã
- çæãããã¯ã©ã€ã¢ã³ã
ã©ã®æ¹æ³ãæåãã¯ãã¢ããªã®èŠä»¶ã«ãã£ãŠç°ãªããŸãã
åºæ¬çãªäœ¿ç𿹿³
IHttpClientFactory ãç»é²ããã«ã¯ãAddHttpClient ãåŒã³åºããŸãã
using Shared;
using BasicHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHttpClient();
builder.Services.AddTransient<TodoService>();
using IHost host = builder.Build();
ãµãŒãã¹ãå©çšããå ŽåãIHttpClientFactory ã䜿çšããŠã³ã³ã¹ãã©ã¯ã¿ãŒ ãã©ã¡ãŒã¿ãŒãšã㊠ãèŠæ±ã§ããŸãã æ¬¡ã®ã³ãŒãã§ã¯ãIHttpClientFactory ã䜿çšã㊠HttpClient ã€ã³ã¹ã¿ã³ã¹ãäœæããŠããŸãã
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;
namespace BasicHttp.Example;
public sealed class TodoService(
IHttpClientFactory httpClientFactory,
ILogger<TodoService> logger)
{
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
// Create the client
HttpClient client = httpClientFactory.CreateClient();
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo types
Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
$"https://jsonplaceholder.typicode.com/todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
åã®äŸã®ããã« IHttpClientFactory ã䜿çšããã®ã¯ãæ¢åã®ã¢ããªããªãã¡ã¯ã¿ãªã³ã°ããã®ã«é©ããæ¹æ³ã§ãã
HttpClient ã®äœ¿ç𿹿³ã«å¯Ÿãã圱é¿ã¯ãããŸããã æ¢åã¢ããªã§äœæãããHttpClientã€ã³ã¹ã¿ã³ã¹ãããå ŽåããããCreateClientã®åŒã³åºãã«çœ®ãæããŸãã
ååä»ãã¯ã©ã€ã¢ã³ã
ååä»ãã¯ã©ã€ã¢ã³ãã¯ã次ã®å Žåã«é©ããŠããŸãã
- ãã®ã¢ããªã§ã¯ã
HttpClientã®ç°ãªãäœ¿ãæ¹ãå€ãå¿ èŠã§ãã - å€ãã®
HttpClientã€ã³ã¹ã¿ã³ã¹ã«ã¯ç°ãªãæ§æããããŸãã
ååä»ã HttpClient ã®æ§æã¯ãIServiceCollection ã§ã®ç»é²æã«æå®ã§ããŸã:
using Shared;
using NamedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
string? httpClientName = builder.Configuration["TodoHttpClientName"];
ArgumentException.ThrowIfNullOrEmpty(httpClientName);
builder.Services.AddHttpClient(
httpClientName,
client =>
{
// Set the base address of the named client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
äžèšã®ã³ãŒãã§ã¯ãã¯ã©ã€ã¢ã³ãã¯æ¬¡ã®ãã®ã§æ§æãããŸãã
-
"TodoHttpClientName"ã®äžã®æ§æãããã«ãããååã - ããŒã¹ ã¢ãã¬ã¹
https://jsonplaceholder.typicode.com/ã -
"User-Agent"ããããŒã
æ§æã䜿çšã㊠HTTP ã¯ã©ã€ã¢ã³ãåãæå®ããããšãã§ããŸããããã¯ãè¿œå æãäœææã«ã¯ã©ã€ã¢ã³ãã«èª€ã£ãååãä»ããªãããã«ããã®ã«åœ¹ç«ã¡ãŸãã ãã®äŸã§ã¯ãHTTP ã¯ã©ã€ã¢ã³ãåãæ§æããããã« appsettings.json ãã¡ã€ã«ã䜿çšãããŠããŸãã
{
"TodoHttpClientName": "JsonPlaceholderApi"
}
ãã®æ§æãæ¡åŒµããŠãHTTP ã¯ã©ã€ã¢ã³ããã©ã®ããã«æ©èœãããã«ã€ããŠã®è©³çŽ°ãæ ŒçŽããã®ã¯ç°¡åã§ãã 詳现ã«ã€ããŠã¯ãã.NET ã®Configurationããåç §ããŠãã ããã
泚
åå¥ã®ç»é²æžã¿ååä»ãã¯ã©ã€ã¢ã³ãã®æ°ã¯ããªãœãŒã¹ã®æ¯æžã«ã€ãªããå¯èœæ§ããããããç¡å¶éã«ããªãã§ãã ããã ããšãã°ãç¡å¶éã®å ¥åããã¯ã©ã€ã¢ã³ãåãæŽŸçããããããªãã§ãã ããã
ã¯ã©ã€ã¢ã³ãã®äœæ
CreateClient ãåŒã³åºããããã³ã«ã次ã®ããšãè¡ãããŸãã
-
HttpClientã®æ°ããã€ã³ã¹ã¿ã³ã¹ãäœæãããŸãã - æ§æã¢ã¯ã·ã§ã³ãåŒã³åºãããŸãã
ååä»ãã¯ã©ã€ã¢ã³ããäœæããã«ã¯ããã®ååã CreateClient ã«æž¡ããŸãã
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Shared;
namespace NamedHttp.Example;
public sealed class TodoService
{
private readonly IHttpClientFactory _httpClientFactory = null!;
private readonly IConfiguration _configuration = null!;
private readonly ILogger<TodoService> _logger = null!;
public TodoService(
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
ILogger<TodoService> logger) =>
(_httpClientFactory, _configuration, _logger) =
(httpClientFactory, configuration, logger);
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
// Create the client
string? httpClientName = _configuration["TodoHttpClientName"];
HttpClient client = _httpClientFactory.CreateClient(httpClientName ?? "");
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo type
Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
$"todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
_logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
äžèšã®ã³ãŒãã§ã¯ãHTTP èŠæ±ã§ãã¹ãåãæå®ããå¿ èŠã¯ãããŸããã ã¯ã©ã€ã¢ã³ãã«æ§æãããŠããããŒã¹ ã¢ãã¬ã¹ã䜿çšããããããã³ãŒãã§ã¯ãã¹ãæž¡ãã ãã§æžã¿ãŸãã
åæå®ãããã¯ã©ã€ã¢ã³ã
åæå®ãããã¯ã©ã€ã¢ã³ã:
- ããŒãšããŠæååã䜿çšããå¿ èŠãªãã«ãååä»ãã¯ã©ã€ã¢ã³ããšåãæ©èœãæäŸããŸãã
- ã¯ã©ã€ã¢ã³ãã䜿çšãããšãã«ãIntelliSense ãšã³ã³ãã€ã©ã®ãã«ããæäŸããŸãã
- ç¹å®ã®
HttpClientãæ§æããŠãããšå¯Ÿè©±ãã 1 ã€ã®å ŽæãæäŸããŸãã ããšãã°ãåäžã®åæå®ãããã¯ã©ã€ã¢ã³ãã¯ã次ã®ããã«äœ¿çšãããå ŽåããããŸãã- åäžã®ããã¯ãšã³ã ãšã³ããã€ã³ãçšã
- ãšã³ããã€ã³ããåŠçãããã¹ãŠã®ããžãã¯ãã«ãã»ã«åããããã
- DI ã«å¯Ÿå¿ããŠãããã¢ããªå ã®å¿ èŠãªå Žæã«æ¿å ¥ã§ããŸãã
åæå®ãããã¯ã©ã€ã¢ã³ãã¯ããã®ã³ã³ã¹ãã©ã¯ã¿ãŒã§ HttpClient ãã©ã¡ãŒã¿ãŒãåãåããŸãã
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;
namespace TypedHttp.Example;
public sealed class TodoService(
HttpClient httpClient,
ILogger<TodoService> logger)
{
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo type
Todo[]? todos = await httpClient.GetFromJsonAsync<Todo[]>(
$"todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
äžã®ã³ãŒãã§ã¯ä»¥äžã®æäœãè¡ãããŸãã
- ãã®æ§æã¯ãåæå®ãããã¯ã©ã€ã¢ã³ãããµãŒãã¹ ã³ã¬ã¯ã·ã§ã³ã«è¿œå ããããšãã«èšå®ãããŸãã
-
HttpClientã¯ãã¯ã©ã¹ã¹ã³ãŒãã®å€æ° (ãã£ãŒã«ã) ãšããŠå²ãåœãŠãããå ¬éããã API ãšå ±ã«äœ¿çšãããŸãã
HttpClient ã®æ©èœãå
¬éãã API åºæã®ã¡ãœãããäœæã§ããŸãã ããšãã°ãGetUserTodosAsync ã¡ãœããã§ã¯ããŠãŒã¶ãŒåºæã®Todo ãªããžã§ã¯ããååŸããããã®ã³ãŒããã«ãã»ã«åãããŸãã
次ã®ã³ãŒãã§ã¯ãAddHttpClient ãåŒã³åºããŠãåæå®ãããã¯ã©ã€ã¢ã³ã ã¯ã©ã¹ãç»é²ããŠããŸãã
using Shared;
using TypedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHttpClient<TodoService>(
client =>
{
// Set the base address of the typed client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
åæå®ãããã¯ã©ã€ã¢ã³ãã¯ãDI ã§äžæçãšããŠç»é²ãããŸãã äžèšã®ã³ãŒãã§ãAddHttpClient 㯠TodoService ãäžæçãªãµãŒãã¹ãšããŠç»é²ããŸãã ãã®ç»é²ã§ã¯ããã¡ã¯ã㪠ã¡ãœããã䜿çšããŠæ¬¡ã®ããšãè¡ããŸãã
-
HttpClientã®ã€ã³ã¹ã¿ã³ã¹ãäœæããŸãã -
TodoServiceã®ã€ã³ã¹ã¿ã³ã¹ãäœæããHttpClientã®ã€ã³ã¹ã¿ã³ã¹ããã®ã³ã³ã¹ãã©ã¯ã¿ãŒã«æž¡ããŸãã
éèŠ
ã·ã³ã°ã«ãã³ ãµãŒãã¹ã§åæå®ãããã¯ã©ã€ã¢ã³ãã䜿ããšãå±éºãªå ŽåããããŸãã 詳现ã«ã€ããŠã¯ãã·ã³ã°ã«ãã³ ãµãŒãã¹ã§ã®åæå®ãããã¯ã©ã€ã¢ã³ãã®åé¿ã«é¢ããã»ã¯ã·ã§ã³ãåç §ããŠãã ããã
泚
åæå®ãããã¯ã©ã€ã¢ã³ãã AddHttpClient<TClient> ã¡ãœããã䜿çšããŠç»é²ããå ŽåãTClient åã«ã¯ããã©ã¡ãŒã¿ãšã㊠HttpClient ãèš±å¯ããã³ã³ã¹ãã©ã¯ã¿ãå¿
èŠã§ãã ããã«ãTClient å㯠DI ã³ã³ããã䜿çšããŠåå¥ã«ç»é²ããªãã§ãã ãããç»é²ãããšãåç»é²ãåŸã®ç»é²ã«ãšãäžæžããããããã§ãã
çæãããã¯ã©ã€ã¢ã³ã
IHttpClientFactory ã¯ãRefit ãªã©ã®ãµãŒãããŒãã£è£œã©ã€ãã©ãªãšçµã¿åãããŠäœ¿çšã§ããŸãã åé©åã¯ã.NETçšã® REST ã©ã€ãã©ãªã§ãã ããã«ãããã€ã³ã¿ãŒãã§ã€ã¹ ã¡ãœããããšã³ããã€ã³ãã«ãããã³ã°ãã宣èšåã® REST API å®çŸ©ãå¯èœã«ãªããŸãã ã€ã³ã¿ãŒãã§ã€ã¹ã®å®è£
㯠RestService ã«ãã£ãŠåçã«çæãããHttpClient ã䜿çšããŠå€éš HTTP ã®åŒã³åºããè¡ããŸãã
次㮠record åããããšããŸã:
namespace Shared;
public record class Todo(
int UserId,
int Id,
string Title,
bool Completed);
次ã®äŸã¯ãRefit.HttpClientFactory NuGet ããã±ãŒãžã«äŸåããŠãããåçŽãªã€ã³ã¿ãŒãã§ã€ã¹ã§ãã
using Refit;
using Shared;
namespace GeneratedHttp.Example;
public interface ITodoService
{
[Get("/todos?userId={userId}")]
Task<Todo[]> GetUserTodosAsync(int userId);
}
äžèšã® C# ã€ã³ã¿ãŒãã§ã€ã¹ã§ã¯ã次ã®ããšãè¡ããŸãã
-
GetUserTodosAsyncã€ã³ã¹ã¿ã³ã¹ãè¿ãTask<Todo[]>ãšããååã®ã¡ãœãããå®çŸ©ããŸãã - å€éš API ã«å¯Ÿãããã¹ãšã¯ãšãªæååã䜿çšããŠã
Refit.GetAttribute屿§ã宣èšããŸãã
åæå®ãããã¯ã©ã€ã¢ã³ãã远å ããRefit ã䜿çšããŠå®è£ ãçæã§ããŸãã
using GeneratedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Refit;
using Shared;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddRefitClient<ITodoService>()
.ConfigureHttpClient(client =>
{
// Set the base address of the typed client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
å®çŸ©ãããã€ã³ã¿ãŒãã§ã€ã¹ã¯ãDI ããã³ Refit ã«ãã£ãŠæäŸãããå®è£ ã§ãå¿ èŠã«å¿ããŠäœ¿çšã§ããŸãã
POSTãPUTãDELETE ã®èŠæ±ãè¡ã
åã®äŸã§ã¯ããã¹ãŠã® HTTP èŠæ±ã§ GET HTTP åè©ã䜿çšãããŠããŸãã
HttpClient ã§ã¯ã次ã®ãããªä»ã® HTTP åè©ããµããŒããããŠããŸãã
POSTPUTDELETEPATCH
ãµããŒããããŠãã HTTP åè©ã®äžèЧã«ã€ããŠã¯ããHttpMethodããåç §ããŠãã ããã HTTP èŠæ±ã®äœæã®è©³çްã«ã€ããŠã¯ããHttpClient ã䜿çšããŠèŠæ±ãéä¿¡ããã ãåç §ããŠãã ããã
次ã®äŸã¯ãHTTP POST èŠæ±ã®æ¹æ³ã瀺ããŠããŸãã
public async Task CreateItemAsync(Item item)
{
using StringContent json = new(
JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
Encoding.UTF8,
MediaTypeNames.Application.Json);
using HttpResponseMessage httpResponse =
await httpClient.PostAsync("/api/items", json);
httpResponse.EnsureSuccessStatusCode();
}
äžèšã®ã³ãŒãã® CreateItemAsync ã¡ãœããã¯:
-
Itemã䜿çšããŠSystem.Text.Jsonãã©ã¡ãŒã¿ãŒã JSON ã«ã·ãªã¢ã«åããŸãã ããã¯ãJsonSerializerOptions ã®ã€ã³ã¹ã¿ã³ã¹ã䜿çšããŠãã·ãªã¢ã«åããã»ã¹ãæ§æããŸãã - HTTP èŠæ±ã®æ¬æã§éä¿¡ããããã«ã·ãªã¢ã«åããã JSON ãããã±ãŒãžåãã StringContent ã®ã€ã³ã¹ã¿ã³ã¹ãäœæããŸãã
- PostAsync ãåŒã³åºããŠãæå®ãã URL ã« JSON ã®å 容ãéä¿¡ããŸãã ãã㯠HttpClient.BaseAddress ã«è¿œå ãããçžå¯Ÿ URL ã§ãã
- å¿çç¶æ ã³ãŒããæåã瀺ããŠããªãå Žåã«ãEnsureSuccessStatusCode ãåŒã³åºããŠäŸå€ãã¹ããŒããŸãã
HttpClient ã¯ãä»ã®çš®é¡ã®ã³ã³ãã³ãããµããŒãããŠããŸãã ããšãã°ãMultipartContent ãšStreamContent ã§ãã ãµããŒããããŠããã³ã³ãã³ãã®äžèЧã«ã€ããŠã¯ããHttpContentããåç
§ããŠãã ããã
HTTP PUT èŠæ±ã®äŸã次ã«ç€ºããŸãã
public async Task UpdateItemAsync(Item item)
{
using StringContent json = new(
JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
Encoding.UTF8,
MediaTypeNames.Application.Json);
using HttpResponseMessage httpResponse =
await httpClient.PutAsync($"/api/items/{item.Id}", json);
httpResponse.EnsureSuccessStatusCode();
}
äžèšã®ã³ãŒãã¯ãPOST ã®äŸã«ãã䌌ãŠããŸãã
UpdateItemAsync ã¡ãœãã㯠PutAsync ã§ã¯ãªã PostAsync ãåŒã³åºããŸãã
HTTP DELETE èŠæ±ã®äŸã次ã«ç€ºããŸãã
public async Task DeleteItemAsync(Guid id)
{
using HttpResponseMessage httpResponse =
await httpClient.DeleteAsync($"/api/items/{id}");
httpResponse.EnsureSuccessStatusCode();
}
äžèšã®ã³ãŒãã® DeleteItemAsync ã¡ãœãã㯠DeleteAsync ãåŒã³åºããŸãã HTTP DELETE èŠæ±ã«ã¯éåžžãæ¬æãå«ãŸããŠããªãããã DeleteAsync ã¡ãœããã¯ã HttpContentã®ã€ã³ã¹ã¿ã³ã¹ãåãå
¥ãããªãŒããŒããŒããæäŸããŸããã
HttpClient ã§ã®ããŸããŸãª HTTP åè©ã®äœ¿çšã®è©³çްã«ã€ããŠã¯ããHttpClientããåç
§ããŠãã ããã
HttpClient æå¹æé管ç
HttpClient ã§ CreateClient ãåŒã³åºããã³ã«ãIHttpClientFactory ã®æ°ããã€ã³ã¹ã¿ã³ã¹ãè¿ãããŸãã ã¯ã©ã€ã¢ã³ãåããšã« 1 ã€ã® HttpClientHandler ã€ã³ã¹ã¿ã³ã¹ãäœæãããŸãã ãã¡ã¯ããªã«ãã£ãŠ HttpClientHandler ã€ã³ã¹ã¿ã³ã¹ã®æå¹æéã管çãããŸãã
IHttpClientFactory ã¯ããªãœãŒã¹ã®æ¶è²»éãæžããããããã¡ã¯ããªã«ãã£ãŠäœæããã HttpClientHandler ã®ã€ã³ã¹ã¿ã³ã¹ããã£ãã·ã¥ããŸãã æ°ãã HttpClientHandler ã€ã³ã¹ã¿ã³ã¹ãäœæãããšãã«ããã£ãã·ã¥ã® HttpClient ã€ã³ã¹ã¿ã³ã¹ã®æå¹æéãåããŠããªãå Žåã¯ãããåå©çšããå ŽåããããŸãã
éåžžãåãã³ãã©ãŒã§ã¯åºã«ãªã HTTP æ¥ç¶ããŒã«ãç¬èªã«ç®¡çãããããããã³ãã©ãŒã¯ãã£ãã·ã¥ããããšãæãŸããŸãã å¿ èŠä»¥äžã®ãã³ãã©ãŒãäœæãããšããœã±ããäžè¶³ãšæ¥ç¶ã®é å»¶ãçºçãããããããããŸãã ãŸããäžéšã®ãã³ãã©ãŒã¯æ¥ç¶ãç¡æéã«éããŠãããDNS ã®å€æŽã«ãã³ãã©ãŒã察å¿ã§ããªãããšããããŸãã
ãã³ãã©ãŒã®æ¢å®ã®æå¹æé㯠2 åã§ãã æ¢å®å€ããªãŒããŒã©ã€ãããã«ã¯ãSetHandlerLifetimeã«IHttpClientBuilderãç»é²ãããšãã«ãIHttpClientFactoryã§ã¯ã©ã€ã¢ã³ãããšã«ServiceCollectionãåŒã³åºããŸãã
services.AddHttpClient("Named.Client")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
éèŠ
HttpClient ã«ãã£ãŠäœæããã IHttpClientFactory ã€ã³ã¹ã¿ã³ã¹ã¯ãçãæå¹æéã§ããããšãæ³å®ãããŸãã
ãã³ãã©ãŒã DNS ã®å€æŽã«ç¢ºå®ã«å¯Ÿå¿ããããã«ã¯ãæå¹æéã®æå¹æéãåãããšãã®
HttpMessageHandlerã®ãªãµã€ã¯ã«ãšåäœæãIHttpClientFactoryã«äžå¯æ¬ ã§ããHttpClientã¯äœææã«ç¹å®ã®ãã³ãã©ãŒ ã€ã³ã¹ã¿ã³ã¹ã«é¢é£ä»ãããããããæ°ããHttpClientã€ã³ã¹ã¿ã³ã¹ãé©åãªã¿ã€ãã³ã°ã§èŠæ±ããã¯ã©ã€ã¢ã³ããæŽæ°ããããã³ãã©ãŒã確å®ã«ååŸã§ããããã«ããå¿ èŠããããŸããHttpClientãã®ãã㪠ã€ã³ã¹ã¿ã³ã¹ãç Žæ£ããŠããœã±ããã¯æ¯æžããŸãããç Žæ£ããŠã ã¯ç Žæ£HttpMessageHandlerããã§ããIHttpClientFactoryã¯HttpClientã€ã³ã¹ã¿ã³ã¹ã®äœæã«äœ¿çšããããªãœãŒã¹ (å ·äœçã«ã¯HttpMessageHandlerã€ã³ã¹ã¿ã³ã¹) ã远跡ããç Žæ£ããŸãããããã®æå¹æéã¯ããã«æéåãã«ãªãããããã䜿ãHttpClientããªããªãããã§ãã
1 ã€ã® HttpClient ã€ã³ã¹ã¿ã³ã¹ãé·æéåç¶ãããããšã¯ã ã®IHttpClientFactoryãšããŠäœ¿çšã§ããäžè¬çãªãã¿ãŒã³ã§ããããã®ãã¿ãŒã³ã«ã¯ PooledConnectionLifetime ã®ãããªè¿œå ã®ã»ããã¢ãããå¿
èŠã§ãã
ãæå®ããPooledConnectionLifetimeã¯ã©ã€ã¢ã³ãã䜿ããã ã«ãã£ãŠäœæãããIHttpClientFactoryã¯ã©ã€ã¢ã³ãã䜿ãããšãã§ããŸãã ã¢ããªã§äœ¿çšããæŠç¥ã®è©³çްã«ã€ããŠã¯ããHTTP ã¯ã©ã€ã¢ã³ãã®äœ¿çšã«é¢ããã¬ã€ãã©ã€ã³ããåç
§ããŠãã ããã
HttpMessageHandler ãæ§æãã
ã¯ã©ã€ã¢ã³ãã«ãã£ãŠäœ¿çšãããå éš HttpMessageHandler ã®æ§æãå¶åŸ¡ããããšãå¿ èŠãªå ŽåããããŸãã
ååä»ãã¯ã©ã€ã¢ã³ããŸãã¯åæå®ãããã¯ã©ã€ã¢ã³ãã远å ãããšãIHttpClientBuilder ãè¿ãããŸãã
ConfigurePrimaryHttpMessageHandleræ¡åŒµã¡ãœããã¯ãIHttpClientBuilderã§åŒã³åºããŠããªã²ãŒãã§æž¡ãããšãã§ããŸãã ããªã²ãŒãã¯ããã®ã¯ã©ã€ã¢ã³ãã«ãã£ãŠäœ¿çšããããã©ã€ã㪠HttpMessageHandler ã®äœæãšæ§æã«äœ¿çšãããŸãã
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
HttpClientHandler ãæ§æãããšããã³ãã©ãŒã®ããŸããŸãªä»ã®ããããã£ã®äžãã HttpClient ã€ã³ã¹ã¿ã³ã¹ã®ãããã·ãæå®ã§ããŸãã 詳现ã«ã€ããŠã¯ãã¯ã©ã€ã¢ã³ãããšã®ãããã·ã«é¢ããèšäºãåç
§ããŠãã ããã
远å ã®èšå®
IHttpClientBuilder ãå¶åŸ¡ããããã®è¿œå ã®æ§æãªãã·ã§ã³ãããã€ããããŸãã
| ã¡ãœãã | 説æ |
|---|---|
| AddHttpMessageHandler | ååä»ã HttpClient ã®ã¡ãã»ãŒãž ãã³ãã©ãŒãããã«è¿œå ããŸãã |
| AddTypedClient |
TClient ãšãHttpClient ã«é¢é£ä»ããããŠããååä»ã IHttpClientBuilder ãšã®éã®ãã€ã³ãã£ã³ã°ãæ§æããŸãã |
| ConfigureHttpClient | ååä»ã HttpClient ã®æ§æã«äœ¿çšãããããªã²ãŒãã远å ããŸãã |
| ConfigurePrimaryHttpMessageHandler | ååä»ã HttpMessageHandler ã«å¯ŸããŠãäŸåé¢ä¿æ¿å
¥ã³ã³ãããŒãããã©ã€ã㪠HttpClient ãæ§æããŸãã |
| RedactLoggedHeaders | ãã°èšé²ããåã«å€ãç·šéããå¿ èŠããã HTTP ããããŒåã®ã³ã¬ã¯ã·ã§ã³ãèšå®ããŸãã |
| SetHandlerLifetime |
HttpMessageHandler ãåå©çšã§ããæéãèšå®ããŸãã ååä»ãã®åã¯ã©ã€ã¢ã³ãã«ã¯ãç¬èªã®æ§ææžã¿ãã³ãã©ãŒã®æå¹æéã®å€ãèšå®ã§ããŸãã |
| UseSocketsHttpHandler | ååä»ã SocketsHttpHandler ã®ãã©ã€ã㪠ãã³ãã©ãšããŠäœ¿çšãããããã«äŸåæ§ã®æ³šå
¥ã³ã³ããããæ°ãããŸãã¯åã«è¿œå ãã HttpClient ã€ã³ã¹ã¿ã³ã¹ãæ§æããŸãã (.NET 5+ ã®ã¿) |
IHttpClientFactoryãšSocketsHttpHandlerãäžç·ã«äœ¿çšãã
SocketsHttpHandler
HttpMessageHandler ã®å®è£
ã .NET Core 2.1 ã«è¿œå ãããŸãããããã«ãããPooledConnectionLifetimeãæ§æã§ããŸãã ãã®èšå®ã¯ããã³ãã©ãŒã DNS ã®å€æŽã«å¯Ÿå¿ã§ããããã«ããããã«äœ¿çšãããŸãããã®ãããSocketsHttpHandler ã®äœ¿çšã¯ IHttpClientFactory ã®äœ¿çšã®ä»£æ¿ææ®µãšèŠãªãããŸãã 詳现ã«ã€ããŠã¯ããHttpClient ã®äœ¿çšã«é¢ããã¬ã€ãã©ã€ã³ããåç
§ããŠãã ããã
ãã ãã SocketsHttpHandler ãš IHttpClientFactory ãäžç·ã«äœ¿çšããŠãæ§ææ§ãåäžãããããšãã§ããŸãã ãããã® API ã®äž¡æ¹ã䜿ãããšã§ãäœã¬ãã« (ããšãã°ãåçãªèšŒææžã®éžæã« LocalCertificateSelectionCallback ã䜿ã) ãšé«ã¬ãã« (ããšãã°ãDI çµ±åãè€æ°ã®ã¯ã©ã€ã¢ã³ãæ§æãæŽ»çšãã) ã®äž¡æ¹ã§æ§æå¯èœæ§ã®å©ç¹ãåŸãããšãã§ããŸãã
äž¡æ¹ã® API ã䜿ãã«ã¯:
-
SocketsHttpHandlerãPrimaryHandlerçµç±ã§ ConfigurePrimaryHttpMessageHandler ãšããŠæå®ããããUseSocketsHttpHandler (.NET 5+ ã®ã¿). - DNS ã®æŽæ°ãäºæ³ãããéé (ããšãã°ãSocketsHttpHandler.PooledConnectionLifetimeæ¡åŒµã¡ãœããã§ä»¥åã«èšå®ããå€) ã«åºã¥ããŠã
SetHandlerLifetimeãèšå®ããŸãã - (ãªãã·ã§ã³)
SocketsHttpHandlerãæ¥ç¶ããŒã«ãšãªãµã€ã¯ã«ãåŠçãããããIHttpClientFactoryã¬ãã«ã®ãã³ãã© ãªãµã€ã¯ã«ã¯äžèŠã«ãªããŸãããHandlerLifetimeãTimeout.InfiniteTimeSpanã«èšå®ããŠãç¡å¹ã«ããããšãã§ããŸãã
services.AddHttpClient(name)
.UseSocketsHttpHandler((handler, _) =>
handler.PooledConnectionLifetime = TimeSpan.FromMinutes(2)) // Recreate connection every 2 minutes
.SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime
äžã®äŸã§ã¯ãæ¢å®ã® HandlerLifetime å€ã«åãããŠãå³è§£ã®ç®çã§ 2 åãä»»æã«éžæããŸããã DNS ãŸãã¯ãã®ä»ã®ãããã¯ãŒã¯å€æŽã®äºæ³ãããé »åºŠã«åºã¥ããŠå€ãéžæããå¿
èŠããããŸãã 詳现ã«ã€ããŠã¯ã ã¬ã€ãã©ã€ã³ã®HttpClientã»ã¯ã·ã§ã³ãš PooledConnectionLifetime API ããã¥ã¡ã³ãã®ãåèãã»ã¯ã·ã§ã³ãåç
§ããŠãã ããã
ã·ã³ã°ã«ãã³ ãµãŒãã¹ã§åæå®ãããã¯ã©ã€ã¢ã³ãã䜿ã
"ååä»ãã¯ã©ã€ã¢ã³ã" ã®æ¹æ³ã䜿çšããå Žåã ã¯ãµãŒãã¹ã«æ¿å
¥ãããIHttpClientFactory ã€ã³ã¹ã¿ã³ã¹ã¯ HttpClient ãå¿
èŠã«ãªããã³ã« CreateClient ãåŒã³åºãããšã«ãã£ãŠäœæãããŸããHttpClient
ãã ãã"åæå®ãããã¯ã©ã€ã¢ã³ã" ã®æ¹æ³ã§ã¯ãåæå®ãããã¯ã©ã€ã¢ã³ãã¯ãéåžžã¯ãµãŒãã¹ã«æ¿å ¥ãããäžæçãªãªããžã§ã¯ãã«ãªããŸãã ããã«ããåé¡ãçºçãããããããããŸããåæå®ãããã¯ã©ã€ã¢ã³ããã·ã³ã°ã«ãã³ ãµãŒãã¹ã«æ¿å ¥ã§ããããã§ãã
éèŠ
åæå®ãããã¯ã©ã€ã¢ã³ãã¯ã ã«ãã£ãŠäœæããã HttpClient ã€ã³ã¹ã¿ã³ã¹ãšåãæå³ã§ãIHttpClientFactoryã§ããããšãæ³å®ãããŸã (詳现ã«ã€ããŠã¯ããHttpClient ã®æå¹æéã®ç®¡çããåç
§)ã åæå®ãããã¯ã©ã€ã¢ã³ã ã€ã³ã¹ã¿ã³ã¹ãäœæããããšããã«ãIHttpClientFactory ã§ã¯ãããå¶åŸ¡ã§ããªããªããŸãã åæå®ãããã¯ã©ã€ã¢ã³ã ã€ã³ã¹ã¿ã³ã¹ãã·ã³ã°ã«ãã³ã§ååŸãããå ŽåãDNS ã®å€æŽã«å¯Ÿå¿ã§ããªããªããIHttpClientFactory ã®ç®çã® 1 ã€ã倱ããããããããããŸãã
ã·ã³ã°ã«ãã³ ãµãŒãã¹ã§ HttpClient ã€ã³ã¹ã¿ã³ã¹ã䜿ãå¿
èŠãããå Žåã¯ã次ã®ãªãã·ã§ã³ãæ€èšããŠãã ããã
- 代ããã« "ååä»ãã¯ã©ã€ã¢ã³ã" ã®æ¹æ³ã䜿ããã·ã³ã°ã«ãã³ ãµãŒãã¹ã« ãæ¿å
¥ããå¿
èŠã«å¿ããŠ
IHttpClientFactoryã€ã³ã¹ã¿ã³ã¹ãåäœæããŸããHttpClient - "åæå®ãããã¯ã©ã€ã¢ã³ã" ã®æ¹æ³ãå¿
èŠãªå Žåã¯ãæ§ææžã¿ã® ãšå
±ã«ãã©ã€ã㪠ãã³ãã©ãŒãšããŠ
SocketsHttpHandlerã䜿ããŸããPooledConnectionLifetimeSocketsHttpHandlerãšIHttpClientFactoryãäœ¿ãæ¹æ³ã«ã€ããŠè©³ããã¯ãã»ã¯ã·ã§ã³ãSocketsHttpHandler ãšå ±ã« IHttpClientFactory ã䜿çšããã ãåç §ããŠãã ããã
ã®ã¡ãã»ãŒãž ãã³ãã©ãŒ ã¹ã³ãŒã IHttpClientFactory
IHttpClientFactory ã¯ãå HttpMessageHandler ã€ã³ã¹ã¿ã³ã¹ããšã«åå¥ã® DI ã¹ã³ãŒããäœæããŸãã ãããã® DI ã¹ã³ãŒãã¯ãã¢ããªã±ãŒã·ã§ã³ DI ã¹ã³ãŒãïŒäŸïŒASP.NET ã®åä¿¡èŠæ±ã¹ã³ãŒããããŠãŒã¶ãŒãäœæããæå DI ã¹ã³ãŒãïŒãšã¯å¥ã§ãããããã¹ã³ãŒãä»ããµãŒãã¹ ã€ã³ã¹ã¿ã³ã¹ã¯å
±æãããŸããã ã¡ãã»ãŒãž ãã³ãã©ãŒ ã¹ã³ãŒãã¯ãã³ãã©ãŒã®æå¹æéã«é¢é£ä»ããããŠãããã¢ããªã±ãŒã·ã§ã³ ã¹ã³ãŒããäžåãå¯èœæ§ããããŸããããã¯ãããšãã°ãè€æ°ã®åä¿¡èŠæ±éã§åãã¹ã³ãŒãã®äŸåé¢ä¿ãæ¿å
¥ãããåã HttpMessageHandler ã€ã³ã¹ã¿ã³ã¹ãåå©çšããå¯èœæ§ããããŸãã
ã€ã³ã¹ã¿ã³ã¹å
ã§ (HttpContext ã®ããŒã¿ãªã©ã®) HttpMessageHandlerããšãšãæ©å¯æ
å ±ãæŒæŽ©ããªãããã«æ³šæããŠã¹ã³ãŒãä»ãäŸåé¢ä¿ã䜿ãããšã匷ããå§ãããŸãã
ã¡ãã»ãŒãž ãã³ãã©ãŒããã¢ã㪠DI ã¹ã³ãŒãã«ã¢ã¯ã»ã¹ããå¿
èŠãããå Žåã¯ãèªèšŒã®äŸãšããŠãã¹ã³ãŒã察å¿ããžãã¯ãå¥ã®äžæç㪠DelegatingHandler ã«ã«ãã»ã«åããHttpMessageHandler ãã£ãã·ã¥ãã IHttpClientFactory ã€ã³ã¹ã¿ã³ã¹ã®åšå²ã«ã©ããããŸãã ç»é²æžã¿ã® IHttpMessageHandlerFactory.CreateHandler ã®ãã³ãã©ãŒåŒã³åºã ã«ã¢ã¯ã»ã¹ããŸãã ãã®å Žåã¯ãæ§ç¯ããããã³ãã©ãŒã䜿çšããŠèªåã§ HttpClient ã€ã³ã¹ã¿ã³ã¹ãäœæããŸãã
次ã®äŸã¯ãã¹ã³ãŒã察å¿ã® HttpClient ã䜿çšã㊠DelegatingHandler ãäœæããæ¹æ³ã瀺ããŠããŸã:
if (scopeAwareHandlerType != null)
{
if (!typeof(DelegatingHandler).IsAssignableFrom(scopeAwareHandlerType))
{
throw new ArgumentException($"""
Scope aware HttpHandler {scopeAwareHandlerType.Name} should
be assignable to DelegatingHandler
""");
}
// Create top-most delegating handler with scoped dependencies
scopeAwareHandler = (DelegatingHandler)_scopeServiceProvider.GetRequiredService(scopeAwareHandlerType); // should be transient
if (scopeAwareHandler.InnerHandler != null)
{
throw new ArgumentException($"""
Inner handler of a delegating handler {scopeAwareHandlerType.Name} should be null.
Scope aware HttpHandler should be registered as Transient.
""");
}
}
// Get or create HttpMessageHandler from HttpClientFactory
HttpMessageHandler handler = _httpMessageHandlerFactory.CreateHandler(name);
if (scopeAwareHandler != null)
{
scopeAwareHandler.InnerHandler = handler;
handler = scopeAwareHandler;
}
HttpClient client = new(handler);
ããã«åé¿çãšããŠãã¹ã³ãŒãå¯Ÿå¿ DelegatingHandler ãç»é²ããçŸåšã®ã¢ã㪠ã¹ã³ãŒãã«ã¢ã¯ã»ã¹ã§ããäžæçãªãµãŒãã¹ã«ããæ¢å® IHttpClientFactory ã®ç»é²ããªãŒããŒã©ã€ãããããã®æ¡åŒµã¡ãœããã䜿çšã§ããŸã:
public static IHttpClientBuilder AddScopeAwareHttpHandler<THandler>(
this IHttpClientBuilder builder) where THandler : DelegatingHandler
{
builder.Services.TryAddTransient<THandler>();
if (!builder.Services.Any(sd => sd.ImplementationType == typeof(ScopeAwareHttpClientFactory)))
{
// Override default IHttpClientFactory registration
builder.Services.AddTransient<IHttpClientFactory, ScopeAwareHttpClientFactory>();
}
builder.Services.Configure<ScopeAwareHttpClientFactoryOptions>(
builder.Name, options => options.HttpHandlerType = typeof(THandler));
return builder;
}
詳现ã«ã€ããŠã¯ãå®å šãªäŸãåç §ããŠãã ããã
"åºè·æã®æ¢å®" ãã©ã€ã㪠ãã³ãã©ãŒã«äŸåããªãããã«ãã
ãã®ã»ã¯ã·ã§ã³ã§ã¯ã "factory-default" ãã©ã€ã㪠ãã³ãã©ãŒ ãšããçšèªã¯ãæ¢å®ã® IHttpClientFactory å®è£
(ã€ãŸããæ¢å®ã® HttpMessageHandlerBuilder å®è£
) ãå²ãåœãŠããã©ã€ã㪠ãã³ãã©ãŒãæããŸã (äœ ããã®æ¹æ³ã§æ§æãããŠããªã å Žå)ã
泚
"åºè·æã®æ¢å®" ãã©ã€ã㪠ãã³ãã©ãŒã¯å®è£
㮠詳现 ã§ããã倿Žãããå¯èœæ§ããããŸãã
â "å·¥å Žåºè·æã®æ¢å®å€" ãšããŠäœ¿çšãããŠããç¹å®ã®å®è£
(ããšãã°ã HttpClientHandler) ã«å¿ããŠåé¿ããŸãã
ç¹ã«ã¯ã©ã¹ ã©ã€ãã©ãªã§äœæ¥ããŠããå Žåã¯ããã©ã€ã㪠ãã³ãã©ãŒã®ç¹å®ã®çš®é¡ãç¥ãå¿
èŠãããå ŽåããããŸãã ãšã³ã ãŠãŒã¶ãŒã®æ§æãä¿æããªãããããšãã°ãHttpClientHandlerãClientCertificatesãUseCookiesãªã©ã®UseProxyåºæã®ããããã£ãæŽæ°ã§ããŸãã ãã©ã€ã㪠ãã³ãã©ãŒã HttpClientHandler ã«ãã£ã¹ãããããšãèããããŸãããããã¯ã ã "å·¥å Žåºè·æã®æ¢å®" ã®ãã©ã€ã㪠ãã³ãã©ãŒãšããŠäœ¿çšãããŠããå Žåã«ã®ã¿HttpClientHandleråäœããæ¹æ³ã§ãã ããããå®è£
ã®è©³çްã«å¿ããã³ãŒãã®ããã«ããã®ãããªåé¿ç㯠è匱 ã§ãããå£ããå¯èœæ§ããããŸãã
"åºè·æã®æ¢å®" ãã©ã€ã㪠ãã³ãã©ãŒã«äŸåãã代ããã«ã ConfigureHttpClientDefaults ã䜿çšã㊠"ã¢ã㪠ã¬ãã«" ã®æ¢å®ã®ãã©ã€ã㪠ãã³ãã©ãŒ ã€ã³ã¹ã¿ã³ã¹ãèšå®ã§ããŸãã
// Contract with the end-user: Only HttpClientHandler is supported.
// --- "Pre-configure" stage ---
// The default is fixed as HttpClientHandler to avoid depending on the "factory-default"
// Primary Handler.
services.ConfigureHttpClientDefaults(b =>
b.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { UseCookies = false }));
// --- "End-user" stage ---
// IHttpClientBuilder builder = services.AddHttpClient("test", /* ... */);
// ...
// --- "Post-configure" stage ---
// The code can rely on the contract, and cast to HttpClientHandler only.
builder.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
{
if (handler is not HttpClientHandler h)
{
throw new InvalidOperationException("Only HttpClientHandler is supported");
}
h.ClientCertificates.Add(GetClientCert(provider, builder.Name));
//X509Certificate2 GetClientCert(IServiceProvider p, string name) { ... }
});
ãŸãã¯ããã©ã€ã㪠ãã³ãã©ãŒã®çš®é¡ã確èªããæ¢ç¥ã®ãµããŒãã®çš®é¡ (å€ãã®å Žåã HttpClientHandler ãš SocketsHttpHandler) ã§ã®ã¿ã¯ã©ã€ã¢ã³ãèšŒææžãªã©ã®è©³çŽ°ãæ§æããããšãã§ããŸãã
// --- "End-user" stage ---
// IHttpClientBuilder builder = services.AddHttpClient("test", /* ... */);
// ...
// --- "Post-configure" stage ---
// No contract is in place. Trying to configure main handler types supporting client
// certs, logging and skipping otherwise.
builder.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
{
if (handler is HttpClientHandler h)
{
h.ClientCertificates.Add(GetClientCert(provider, builder.Name));
}
else if (handler is SocketsHttpHandler s)
{
s.SslOptions ??= new System.Net.Security.SslClientAuthenticationOptions();
s.SslOptions.ClientCertificates ??= new X509CertificateCollection();
s.SslOptions.ClientCertificates!.Add(GetClientCert(provider, builder.Name));
}
else
{
// Log warning
}
//X509Certificate2 GetClientCert(IServiceProvider p, string name) { ... }
});