First off, the error message I am receiving is as follows: The inner handler has not been set
I'm writing a custom message handler to handle authentication cookie timeouts to my API. For example, if my code base makes a call to the API and in turn receives a 401 then it should retry the login url to get an updated cookie. I planned to accomplish this as follows:
public class CkApiMessageHandler : DelegatingHandler
{
private readonly string _email;
private readonly string _password;
private const string _loginPath = "Account/Login";
public CkApiMessageHandler(string email, string password)
{
_email = email;
_password = password;
//InnerHandler = new HttpControllerDispatcher(null);
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
if(response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
Logging.Logger.LogInformation("Authentication cookie timed out");
var baseAddress = request.RequestUri.Host;
var loginPath = baseAddress + _loginPath;
var originalRequest = request;
var loginRequest = new HttpRequestMessage(new HttpMethod("POST"), loginPath);
var dict = new Dictionary<string, string>();
dict.Add("Email", _email);
dict.Add("Password", _password);
var form = new FormUrlEncodedContent(dict);
loginRequest.Content = form;
loginRequest.Headers.Clear();
foreach(var header in originalRequest.Headers)
{
loginRequest.Headers.Add(header.Key, header.Value);
}
var loginResponse = await base.SendAsync(loginRequest, cancellationToken);
if (loginResponse.IsSuccessStatusCode)
{
Logging.Logger.LogInformation("Successfully logged back in");
return await base.SendAsync(originalRequest, cancellationToken);
}
else
{
Logging.Logger.LogException(new Exception("Failed to log back in"), "Could not log back in");
}
}
return response;
}
}
I am converting an old service that used to access the database directly, to a service that accesses a web api and I'm trying to do it without having to change any of the consumers of this class.. Hence the weird handler. Here is an example method from the service class.
public void UpdateClientInstallDatabaseAndServiceData(string dbAcronym, string clientVersion, string clientEnginePort, Guid installationId, string sqlserver)
{
var dict = new Dictionary<string, string>();
dict.Add("dbAcronym", dbAcronym);
dict.Add("clientVersion", clientVersion);
dict.Add("clientEnginePort", clientEnginePort);
dict.Add("desktopClientId", installationId.ToString());
dict.Add("sqlServerIp", sqlserver);
var form = new FormUrlEncodedContent(dict);
_client.PostAsync(_apiPath + "/UpdateClientInstallDatabaseAndServiceData", form);
}
So if the above code fails with a 401, the service will auto log back in and retry the original code without the consumer of the service having to check requests and relog back in. The consumer should not know that it is dealing with a web api.
My problem is that my message handler is expecting an InnerHandler
to be set which requires an instance of HttpConfiguration
class. When I take a look at the specs here, it appears to be some type of class used to setup a web api service. This is fine, except that this code is not being executed in an api.. It is being executed on a windows forms app. The delegating handler is being used within an HttpClient
class like so...
_client = new HttpClient(new CKEnterprise.Common.CkApiMessageHandler(email, password));
So my question is : How can I get this DelegatingHandler to do it's job outside of the context of a web api project?
Making an update. Looks like I may be able to just use HttpClientHandler class https://blogs.msdn.microsoft.com/henrikn/2012/08/07/httpclient-httpclienthandler-and-webrequesthandler-explained/
Adding Message Handlers to the Client PipelineHttpClient client = HttpClientFactory. Create(new Handler1(), new Handler2(), new Handler3()); Message handlers are called in the order that you pass them into the Create method. Because handlers are nested, the response message travels in the other direction.
Typically, a series of message handlers are chained together. The first handler receives an HTTP request, does some processing, and gives the request to the next handler. At some point, the response is created and goes back up the chain. This pattern is called a delegating handler.
ConfigurePrimaryHttpMessageHandler(IHttpClientBuilder, Func<IServiceProvider,HttpMessageHandler>) Adds a delegate that will be used to configure the primary HttpMessageHandler for a named HttpClient.
The HttpClient class uses a message handler to process the requests on the client side. The default handler provided by the dot net framework is HttpClientHandler. This HTTP Client Message Handler sends the request over the network and also gets the response from the server.
I should have realized this sooner but it makes sense to perhaps set the inner handler to the default handler that HttpClient
uses. So inside your child class of DelegatingHandler
you should set your inner handler to the default handler used by HttpClient
like so:
public CkApiMessageHandler(string email, string password, Guid moduleId)
{
_email = email;
_password = password;
_moduleId = moduleId;
InnerHandler = new HttpClientHandler();
}
Adrian answer is correct. You have to set an inner handler for your custom handler, but there is a better way to set the inner handler.
public MyDelegatingHandler( ) : base( new HttpClientHandler( ) )
Or you can use the HttpClientFactory
_client = HttpClientFactory.Create(new CKEnterprise.Common.CkApiMessageHandler(email, password))
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/httpclient-message-handlers#adding-message-handlers-to-the-client-pipeline
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With