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);
}
}
Related
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.
I am working on a DelegatingHandler to intercept all the calls from one service to another. And I am totally new to DelegatingHandlers.
This is the issue.
We currently have an API that takes calls and measure the concurrent calls and it has some mechanisms to throttle it if needed. it is in this form. (Since actual API is heavy for debugging I am actually using following stub methods to test this)
private Task<HttpResponseMessage> ExecuteAsync1(Func<Task<HttpResponseMessage>> action)
{
return ExecuteAsync2(action);
}
private async Task<HttpResponseMessage> ExecuteAsync2(Func<Task<HttpResponseMessage>> action)
{
return await action().ConfigureAwait(false);
}
Now within this Delegating handler I have to use this API like this.
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (this.ThrottlingFactory == null || !this.ThrottlingEnabled)
{
return await base.SendAsync(request, cancellationToken);
}
else
{
var response = await this.ExecuteAsync1(() =>
{
return base.SendAsync(request, cancellationToken);
});
// Analyse response for certain thing here.
return response;
}
}
My problem is, after executing ExecuteAsync1 (it actually completes the ExecuteAsync2) the code never return back. I tried numerous variation of this like adding async/await to all the methods in the chain including the anon method etc. non worked. So can somebody please point me to things I am doing wrong here?
This is what I had to do. I had to ConfigureAwait(false) to the call.
var response = await this.ExecuteAsync1(() =>
{
return base.SendAsync(request, cancellationToken);
}).ConfigureAwait(false);
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;
}
}