I need to add a client certificate to my web requests and tried to achieve it in this way: Stackoverflow
At the end of this answer the "FlurlClient way" is presented. Using and configuring a FlurlClient instead of a global FlurlHttp configuration. I've tried this, but it didn't work.
I've created a new .NET Core Console application to show you the problem:
static void Main(string[] args)
{
/****** NOT WORKING *******/
try
{
IFlurlClient fc1 = new FlurlClient(url)
.ConfigureClient(c => c.HttpClientFactory = new X509HttpFactory(GetCert()));
fc1.WithHeader("User-Agent", userAgent)
.WithHeader("Accept-Language", locale);
dynamic ret1 = fc1.Url.AppendPathSegments(pathSegments).GetJsonAsync()
.GetAwaiter().GetResult();
}
catch
{
// --> Exception: 403 FORBIDDEN
}
/****** NOT WORKING *******/
try
{
IFlurlClient fc2 = new FlurlClient(url);
fc2.Settings.HttpClientFactory = new X509HttpFactory(GetCert());
fc2.WithHeader("User-Agent", userAgent)
.WithHeader("Accept-Language", locale);
dynamic ret2 = fc2.Url.AppendPathSegments(pathSegments).GetJsonAsync()
.GetAwaiter().GetResult();
}
catch
{
// --> Exception: 403 FORBIDDEN
}
/****** WORKING *******/
FlurlHttp.Configure(c =>
{
c.HttpClientFactory = new X509HttpFactory(GetCert());
});
dynamic ret = url.AppendPathSegments(pathSegments).GetJsonAsync()
.GetAwaiter().GetResult();
// --> OK
}
The X509HttpFactory
is copied from the linked StackOverflow answer (but using HttpClientHandler
instead of WebRequestHandler
):
public class X509HttpFactory : DefaultHttpClientFactory
{
private readonly X509Certificate2 _cert;
public X509HttpFactory(X509Certificate2 cert)
{
_cert = cert;
}
public override HttpMessageHandler CreateMessageHandler()
{
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(_cert);
return handler;
}
}
So using the global FlurlHttp configuration is working and configuring the FlurlClient is not working. Why?
This all comes down to the order you're calling things:
fc.Url
returns a Url
object, which is little more than a string-building thing. It doesn't hold a reference back to the FlurlClient
. (This allows Flurl to exist as a URL building library independent of Flurl.Http.)Url.AppendPathSegments
returns "this" Url
.Url.GetJsonAsync
is an extension method that first creates a FlurlClient
, then uses it with the current Url
to make the HTTP call.So as you can see, you've lost your reference to fc
in step 1 of that flow. 2 possible solutions:
1. Build the URL first, then fluently add in the HTTP bits:
url
.AppendPathSegments(...)
.ConfigureClient(...)
.WithHeaders(...)
.GetJsonAsync();
2. OR, if you want to reuse the FlurlClient, "attach" it to the URL using WithClient:
var fc = new FlurlClient()
.ConfigureClient(...)
.WithHeaders(...);
url
.AppendPathSegments(...)
.WithClient(fc)
.GetJsonAsync();