The HttpClient custom Client Message Handlers insert themselves in the client-side pipeline when making web api calls. This info is from this article
The author says that these message handlers go into action as the request is being sent out and also when the response is received back from the server.
I understand these client message handler's role as request is being sent out from client to the Server because the sendAsync method provides the request object. One can add custom request headers, etc.
I do not understand the usefulness of these message handlers AFTER the response is received from the server because the sendAsync method does not provide access to the response object. So, I am not sure how these client side message handlers are useful on the return journey of the request.
I am obviously missing something here.
Actually you do get access to the response message inside the message handler. For example, in the following handler, I am logging outgoing request and incoming response. Now as long as I use this instance of HttpClient, all calls made through it write traces of requests and response.
HttpClient client = new HttpClient(new LoggingHandler(new HttpClientHandler()));
public class LoggingHandler : DelegatingHandler
{
public LoggingHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine("Request:");
Console.WriteLine(request.ToString());
if (request.Content != null)
{
Console.WriteLine(await request.Content.ReadAsStringAsync());
}
Console.WriteLine();
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
Console.WriteLine("Response:");
Console.WriteLine(response.ToString());
if (response.Content != null)
{
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
Console.WriteLine();
return response;
}
}
Related
I have a few web services running on different servers, and I want to have one web service running "in front" of the rest to decide which web service (server) the request should be forwarded to based on header values.
The idea is that a client will send a request, say:
http://api.mysite.com/cars
The API at mysite.com will inspect the request, extract information from the API key (which is supplied in the headers) and redirect to the appropriate server, e.g.
http://server4.mysite.com/api/cars
Is this going to work? I'm concerned about how I will return the response (w/data) from "server4" to the client. Will the response only be returned back to the first server or will the client achieve that response?
Just run into the same task and have to add some more lines in addition to Yazan Ati answer.
[HttpPost]
[HttpGet]
[Route("api/TestBot/{*remaining}")]
public Task<HttpResponseMessage> SendMessage()
{
const string host = "facebook.botframework.com";
string forwardUri = $"https://{host}/api/v1/bots/billycom{Request.RequestUri.Query}";
Request.Headers.Remove("Host");
Request.RequestUri = new Uri(forwardUri);
if (Request.Method == HttpMethod.Get)
{
Request.Content = null;
}
var client = new HttpClient();
return client.SendAsync(Request, HttpCompletionOption.ResponseHeadersRead);
}
All you need to do is build a Web API DelegatingHandler like this:
public class ProxyHandler : DelegatingHandler
{
protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
UriBuilder forwardUri = new UriBuilder(request.RequestUri);
//strip off the proxy port and replace with an Http port
forwardUri.Port = 80;
//send it on to the requested URL
request.RequestUri = forwardUri.Uri;
HttpClient client = new HttpClient();
var response = await client.SendAsync(request,HttpCompletionOption.ResponseHeadersRead);
return response;
}
}
I am facing a rather simple situation but I cannot wrap my head around it. Maybe the OkHttp gurus could light my path.
I am using Picasso, Retrofit and OkHttp for multiple purposes on my android app. yay!. As I read properly, the developer should make an effort in keeping on OkHttpClient (as read here ).
With the approach in mind, I want any of my HTTP calls (be it an API call, an Image loading, a resource download) to :
Send the request
If HTTP401 is received, then Send another HTTP Request that sends a token back
When that token is received, the call is re-emitted with that token included in the headers
Any subsequent call (be it an API, a resource or image call) should use that token until the next HTTP401 is received (invalid token).
Of course, I would reuse the same client for Retrofit and Picasso.
One route I am considering is to use a mix of Authenticator and an application Interceptor. Authenticator should catch HTTP401, but can I make it make another sync request in the meantime, store the token and activate the new interceptor ?
Looks like I found the solution myself to that problem so let's share the knowledge to everyone.
In order to this, OkHttp already gives all the necessary hooks.
Make sure to use Authenticator
Install an interceptor once the authenticator succeed
Return a request with the good token.
This also imply that the Authenticator handles an HTTP to set your token back (done in another android service).
okHttpClient.setAuthenticator(new Authenticator() {
#Override
public Request authenticate(Proxy proxy, Response response) {
AccountManager accountManager = AccountManager.get(context);
Account[] accounts = accountManager.getAccountsByType(Authenticator.ACCOUNT_TYPE);
// No account, do not even try to authenticate
if (accounts.length == 0) {
Log.i(TAG, "... But we dont have any account yet, so I will just back off for now.");
return null;
}
Account account = accounts[0];
try {
final String mCurrentToken = accountManager.blockingGetAuthToken(account, "", false);
// For now, we just re-install blindly an interceptor
okHttpClient.interceptors().clear();
Log.i(TAG, "... Installing interceptor after authentication");
okHttpClient.interceptors().add(new Interceptor() {
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request newReq = request.newBuilder()
.addHeader("Authorization", mCurrentToken)
.build();
Response response = chain.proceed(newReq);
return response;
}
});
Log.i(TAG, "Install temporary auth token in request");
return response.request().newBuilder()
.addHeader("Authorization", mCurrentToken)
.build();
} catch (OperationCanceledException e) {
Log.e(TAG, "Interrupted exception");
return null;
} catch (AuthenticatorException e) {
Log.e(TAG, "Authentication error");
return null;
} catch (IOException e) {
Log.e(TAG, "IO Error");
return null;
}
}
#Override
public Request authenticateProxy(Proxy proxy, Response response) {
return null; // Null indicates no attempt to authenticate.
}
})
With this, just use this OkClient in Picasso and Retrofit.
I am not able to set the keep-alive in the response header with the web Api. I am running in the IIS Express(Development server). I am using the message handler which sets the same
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
if (request.Headers.Authorization == null)
{
var NonAuthenicateresponse = new HttpResponseMessage(HttpStatusCode.Unauthorized);
NonAuthenicateresponse.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("NTLM"));
return NonAuthenicateresponse;
}
var response=await base.SendAsync(request,cancellationToken);
response.Headers.ConnectionClose = false;
return response;
}
Am i missing anything extra parameter for the same.
To add more details , I am trying to implement NTLM authentication for the application. Once the request is made from the browser we issue a 401 unauthorized and the negotiation happening but it is able to send the Authorization header back in the subsequent request.
We need to explicitly say to the browser about the Keep-alive the connection.
For auditing purposes, I would like to store the raw request (as displayed in Fiddler) as a file when a new request comes in before I processing it. Can this be done and how? Thanks!
Yes, you can do it. Following is an example where I use a message handler to log incoming requests. This handler can be used to log any kind of request(not only the multipartform requests).
//add this handler in your config
config.MessageHandlers.Add(new LoggingMessageHandler());
// Logging message handler
public class LoggingMessageHandler : DelegatingHandler
{
private StringBuilder messageBuilder = null;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
messageBuilder = new StringBuilder();
messageBuilder.AppendFormat("{0} {1}", request.Method.Method, request.RequestUri);
messageBuilder.AppendLine();
//get request headers information
GetHeaders(request.Headers);
//get request content's headers and body
if (request.Content != null)
{
GetHeaders(request.Content.Headers);
// NOTE 1:
// ReadAsStringAsync call buffers the entire request in memory.
// So, even though you could be consuming the request's stream here, since the entire request is buffered
// in memory, you can expect the rest of the call stack to work as expected.
// NOTE 2:
// Look for performance considerations when the request size is too huge.
string body = await request.Content.ReadAsStringAsync();
messageBuilder.AppendLine();
messageBuilder.Append(body);
}
//TODO: log the message here
//logger.Log(messageBuilder.ToString())
// call the rest of the stack as usual
return await base.SendAsync(request, cancellationToken);
}
private void GetHeaders(HttpHeaders headers)
{
foreach (KeyValuePair<string, IEnumerable<string>> header in headers)
{
messageBuilder.AppendLine(string.Format("{0}: {1}", header.Key, string.Join(",", header.Value)));
}
}
}
I need to run a validation routine looking for some header information on every request to the server. I would use OnActionExecuting in ASP.NET MVC, or an ActionInvoker, to run on every request, but I've been looking in Web API, and haven't found something specific.
If something could be implemented for both synchronous and asynchronous, that would be the best.
For Web API you should resort to MessageHandlers
Message handlers always run first, before anything else in the pipeline, and they are also able to run last (after Web API returns response, just prior to the response reaching the client).
More about message handlers can be found here - http://www.asp.net/web-api/overview/working-with-http/http-message-handlers.
And here is a simple example, validating an API key:
public class WebApiKeyHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
string apikey = HttpUtility.ParseQueryString(request.RequestUri.Query).Get("apikey");
if (apikey != "something")
{
HttpResponseMessage response = request.CreateErrorResponse(HttpStatusCode.Forbidden, "You can't use the API without the key.");
throw new HttpResponseException(response);
}
else
{
return base.SendAsync(request, cancellationToken);
}
}
}
In this example only request with the key "something": i.e./api/values/?apikey=something will be allowed, all other will be rejected.
In your case, you can simply access the request.Headers and validate whatever you need.