Since Microsoft recommends that the HttpClient be created once and reused throughout the life of a program, I wondering how to update DefaultRequestHeaders when for example, the token has expired and need to be refresh.
DefaultRequestHeaders is more over not thread safe (for what I know) and the list of headers defined there, shared by all potentially pending requests. Clear() the list and Add() the header with a new token, seems not the wise thing to do.
Update
To be more precise, I don't want/need to change request headers for every request. Only when I've got a HTTP 401 status code.
Wire up a message handler with your HttpClient when you register the IHttpClient in the DI container registry phase or use another pattern such as a factory or singleton to return an instance of the IHttpClient with a custom message handler. Inspect the outbound call and add the necessary headers.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/httpclient-message-handlers
Sample header message handler
class MessageHandler1 : DelegatingHandler
{
private int _count = 0;
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
System.Threading.Interlocked.Increment(ref _count);
request.Headers.Add("X-Custom-Header", _count.ToString());
return base.SendAsync(request, cancellationToken);
}
}
Sample logger message handler:
class LoggingHandler : DelegatingHandler
{
StreamWriter _writer;
public LoggingHandler(Stream stream)
{
_writer = new StreamWriter(stream);
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
if (!response.IsSuccessStatusCode)
{
_writer.WriteLine("{0}\t{1}\t{2}", request.RequestUri,
(int)response.StatusCode, response.Headers.Date);
}
return response;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_writer.Dispose();
}
base.Dispose(disposing);
}
}
Add it to the pipeline
HttpClient client = HttpClientFactory.Create(new Handler1(), new Handler2(), new Handler3());
Threading Concerns
Regarding threading concerns or concurrency, the HttpRequestMessage parameter on the SendAsync method will be per request. If you add the header to the request.Headers collection, you will updating headers for that instance of the request only (i.e., not globally )
Or use the Authorization property on the request.Headers instance:
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", bearerToken);
Please see MSDN link below
https://msdn.microsoft.com/en-us/library/system.net.http.httprequestmessage
If you use DefaultRequestHeaders on a static, shared, singleton, Lifestyle.Singleton, etc, instance of the HttpClient then you will have threading concerns and need proper synchronization to update the DefaultRequestHeaders collection.
Related
I have implemented some logging in WebAPI 2 via a MessageHandler.
It seems to me that the response is returned only AFTER the logging occurs. It would be nice for the user to receive their response, but for the logging to be an after thought. Is this possible?
public abstract class MessageHandler : DelegatingHandler
{
protected HttpRequestMessage requestMessage;
protected HttpResponseMessage responseMessage;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage, CancellationToken token)
{
var requestTime = DateTime.Now;
this.requestMessage = requestMessage;
responseMessage = await base.SendAsync(requestMessage, token);
var responseTime = DateTime.Now;
await LogEntryAsync(requestTime, responseTime);
return responseMessage;
}
protected abstract Task LogEntryAsync(DateTime requestTime, DateTime responseTime);
}
Yes, of course, it's possible.
The usual way to handle this is to place the message in a concurrent queue consumed by a thread that deals with logging from all sources.
The thread and the queue would be encapsulated inside a Logger class with an interface similar to:
public class ILog
{
void Debug(....);
void Info(....);
void Warning(....);
}
and your code would look like:
ILog Log {get; set;}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage, CancellationToken token)
{
var requestTime = DateTime.Now;
this.requestMessage = requestMessage;
responseMessage = await base.SendAsync(requestMessage, token);
var responseTime = DateTime.Now;
log.Info(requestTime, responseTime); // No async needed
return responseMessage;
}
Blocking (for say database or file logs) would then occur on the consumer thread instead of the main thread that handles the user HTTP session.
The real question is why would you want to implement this yourself when several widely available libraries already do it for you and better than you would hope to achieve without spending a prohibitive amount of time on a comprehensive log solution (see NLog, Log4Net, CommonLogging).
I have a web project (C#, MVC5 but no WebAPI) and a simple HTTP REST client that is calling an external REST service and acquires an accessToken etcing.
I want to check the response from all my Get/PostAsync calls for statusCode 401 but I see that I can only override the SendAsync method when implementing the DelegatingHandler.
class CustomDelegatingHandler : DelegatingHandler
{
async protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
Is there something else I can do so as not to change the implementation of all my async calls to use SendAsync?
(What I really really want to do is refresh the accessToken.)
I think you're on the right track. You wouldn't need to change your calls to use SendAsync instead of GetAsync, PostAsync, etc though. Rather, you'd need to change how you instantiate HttpClient:
var client = new HttpClient(new CustomDelegatingHandlerTokenRefresher());
// GetAsync, PostAsync, etc will hit your handler
Use the Decorator or Interceptor pattern.
Example concrete decorator:
public class CustomDelegatingHandlerTokenRefresher : DelegatingHandler
{
public CustomDelegatingHandlerTokenRefresher(DelegatingHandler handler)
{
InnerHandler = handler;
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
RefreshToken();
return await InnerHandler.SendAsync(request, cancellationToken);
}
}
For logging purposes, I am trying to monitor the requests being made through a WebAPI. I have created and I am looking for a way to get back the body sent through in a request after the request has been fulfilled and responded to. I am trying to do this through using a ActionFilter but thus far have failed in reading the body from the request.
Can anybody give some advice how I may access this information?
For context I am trying to do this within this code:
public class LoggingActionFilter : ActionFilterAttribute
{
public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
var test = actionExecutedContext.Request.Content.ReadAsStringAsync().Result;
return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken);
}
}
I have tried reading back the Content on the actionExecutedContext variable in order to get back the body but have found this to return just blank so far.
you're just dealing with request body so don't need to use OnActionExecutedAsync method, you can just override OnActionExecuting like this,
public override void OnActionExecuting(HttpActionContext actionContext)
{
var test = (actionContext.Request.Content as ObjectContent).Value.ToString();
// your logging code here
}
Another option available in WebAPI is DelegatingHandler. if you want to log just request body then override SendAsync method,
public class ApiLogHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var requestBody = request.Content.ReadAsStringAsync().Result;
// your logging code here
return base.SendAsync(request, cancellationToken);
}
}
If you decided to choose DelegatingHandler then you need to register that handler to Global message handlers.
I'm trying to log the HTTP Response Headers of my Web API project.
The project is developed by VS2012, .NET 4.5 and ASP.NET MVC 4.
I've wrote a DelegatingHandler subclass like this:
public class LoggingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Execute the request
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
return response;
});
}
}
However, the problem is, I can't get the header values from the response. response.Headers is an empty collection, response.Content.Headers contains nothing but a key named Content-Type, and HttpContext.Current is null.
I've seen the code of WebAPIContrib which use the same logic to log the headers, but their code does not seem to work either.
So how should I trace the HTTP Response Headers in Web API project?
Message handlers are called in the same order that they appear in
MessageHandlers collection. Because they are nested, the response
message travels in the other direction. That is, the last handler is
the first to get the response message.
Make sure that the logging handler is registered early in the pipeline. Preferably first.
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.MessageHandlers.Add(new LoggingHandler(...));
//...add other handlers
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());
// Other code not shown...
}
}
That way any other handlers would have their chance to populate the response and have that info logged.
You can also simplify the class using async/await syntax to make accessing the response cleaner.
public class LoggingHandler : DelegatingHandler {
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
//...Extract and log request
LogRequest(request);
// Send the request on to the inner handler(s) and get the response
var response = await base.SendAsync(request, cancellationToken);
//...Extract details from response for logging
LogResponse(response);
return response;
}
private void LogRequest(HttpRequestMessage request) {
//... code removed for brevity
}
private void LogResponse(HttpResponseMessage response) {
//... code removed for brevity
}
}
Should be able to access the necessary details from the response before returning it.
Reference : HTTP Message Handlers in ASP.NET Web API
Try out the below code.
return base.SendAsync(request, cancellationToken).ContinueWith(
task =>
{
var headers = task.Result.ToString();
var body = task.Result.Content.ReadAsStringAsync().Result;
// RETURN THE ORIGINAL RESULT
var response = task.Result;
return response;
}
);
You must look at the right place:
request.Content.Headers.ContentType
Assuming there's a Content-Type: application/json request header, then the aforementioned will return "application/json".
So basically some headers are associated with the Content and that's where you should be reading them from.
Same is true for the response headers. Depending on their type you might need to extract them from the response content (for requests that return body)
response.Content.Headers.ContentType
I have to write unit test on ASP.NET MVC Web API Controller with Rhino.Mock
I have a handler named AHandler.cs with inherts from System.Net.Http.HttpClientHandler class.
The singnature SendAsync method of AHandler.cs is like followings :
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
.....
var response = base.SendAsync(request, cancellationToken).Result;
if (response.IsSuccessStatusCode)
{
.....
}
}
the base keyword above means HttpClientHandler and its SendAsync() method is "protected"!!!
Now I try to mock the object "base.SendAsync(request, cancellationToken).Result" and got the hand-made response result I wanted.
But it seems that Rhino mocks can't see the "base" keyword when I wrote the followings code :
var mockbase = MockRepository.GenerateMock<AHandler>;
mockbase.Stub(x => x.base <=== can't see base keyword
^^^^^
So I change another way and try to mock the HttpClientHandler class
var mockbase = MockRepository.GenerateMockHttpClientHandler>;
mockbase.Stub(x => x. <== I can't see SendAsync() method, becase it is protected !!
Now I really suffer in it !!
Can anybody give me some advice that how to made a custom response in MVC handler ?!
very thanks !!
Why do you want to mock a handler in first place ? You can inject an specific dummy implementation for your tests. That handler will return a new HttpResponse message expected by your tests.
public class MyDummyHttpHandler : DelegatingHandler
{
HttpResponseMessage response;
public MyDummyHttpHandler(HttpResponseMessage response)
{
this.response = response;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
.....
TaskCompletionSource<HttpResponseMessage> tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(this.response);
return tsc.Task;
}
}