文章目錄
讓 .NET Core 的 HttpClientFactory 不驗證 Https 憑證
Https 幾乎已成為了現在網站的基本配備,從過去只有敏感交易網站才需要,到現在瀏覽器還會把非 Https 網站標記為 不安全
,而 .NET Core 程式在預設專案範本下也會啟用 Https,不過因為開發環境的 SSL 憑證只是開發測試用,沒有實際效力並不被信任,因此透過 HttpClient call 時就會出現憑證無效的錯誤,但在正式環境上就會綁定正確憑證,所以得要在 Development 環境避免驗證憑證有效性,而在正式機上就需要啟用驗證功能,來看看如何設定吧
基本環境說明
- .NET Core 2.2.101
原始程式碼
services.AddHttpClient("yowkoblog", c => { c.BaseAddress = new Uri("/"); });
.NET Core 使用 HttpClientFactory 完整設定方式請參考 在 .NET Core 與 .NET Framework 上使用 HttpClientFactory
錯誤訊息
訊息內容
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
錯誤截圖
修改方式 (擇一即可)
直接回傳 true
services.AddHttpClient("yowkoblog", c => { c.BaseAddress = new Uri("/"); }).ConfigurePrimaryHttpMessageHandler(h => { var handler = new HttpClientHandler(); if (_env_.IsDevelopment()) { handler.ServerCertificateCustomValidationCallback = delegate { return true; }; } return handler; });
使用 HttpClientHandler 屬性
services.AddHttpClient("yowkoblog", c => { c.BaseAddress = new Uri("/"); }).ConfigurePrimaryHttpMessageHandler(h => { var handler = new HttpClientHandler(); if (_env_.IsDevelopment()) { handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; } return handler; });
實際使用
[ApiController]
[Route("[controller]")]
public class AccountsController : ControllerBase
{
private readonly IHttpClientFactory _blogClientFactory;
public BlogController(IHttpClientFactory blogClientFactory)
{
_blogClientFactory = blogClientFactory;
}
[HttpPost("GetPosts")]
public async Task<ActionResult<string>> GetPosts()
{
//名稱與 services.AddHttpClient 註冊時相同
var client = _platformAuthFactory.CreateClient("yowkoblog");
var response = await (await client.PostAsync(authPath, null)).Content.ReadAsStringAsync();
return response;
}
}
完整程式碼
Startup.cs
public class Startup { private IHostingEnvironment currentEnvironment { get; set; } 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.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddHttpClient("yowkoblog", c => { c.BaseAddress = new Uri("https://localhost:5000/"); }) .ConfigurePrimaryHttpMessageHandler(h => { var handler = new HttpClientHandler(); if (currentEnvironment.IsDevelopment()) { //handler.ServerCertificateCustomValidationCallback = delegate { return true; }; handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; } return handler; }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { currentEnvironment = env; if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/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.UseStaticFiles(); app.UseCookiePolicy(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } }
Controller
[ApiController] [Route("[controller]")] public class AccountsController : ControllerBase { private readonly IHttpClientFactory _blogClientFactory; public BlogController(IHttpClientFactory blogClientFactory) { _blogClientFactory = blogClientFactory; } [HttpPost("GetPosts")] public async Task<ActionResult<string>> GetPosts() { //名稱與 services.AddHttpClient 註冊時相同 var client = _platformAuthFactory.CreateClient("yowkoblog"); var response = await (await client.PostAsync(authPath, null)).Content.ReadAsStringAsync(); return response; } }
心得
這個問題好像曾經遇過,但當時似乎是覺得這個小小設定實在不需要紀錄,想不到再次相見又花了我二十分鐘 XD
但過去我只知道在 certificate callback 永遠回傳 true 來處理,這次重新 review 發現透過使用 HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
語意更清楚,.NET Core 真是令人驚豔呀
參考資訊
文章作者 Yowko Tsai
上次更新 2021-11-03
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。