How to determine if an HttpResponseMessage was fulfilled from cache using HttpClient - c#

What is the equivalent to WebResponse.IsFromCache when using HttpClient and HttpResponseMessage?
Is there some HTTP header in the response that I can look at?

FYI: The Windows.Web.Http HttpClient (a similar API targetted at Windows 8.1 app development) does include an HttpResponseMessage.Source field that specifies where the result came from (common values are "cache" and "network").
The Windows.Web.Http classes are usable from C# and other .NET languages, from C++, and from JavaScript (when running as a WwaHost app like from the Windows app store).

Can I ask what you're trying to achieve? Are trying to avoid caching?
The reason for asking is I've looked at the source code for HttpClient (specifically HttpClientHandler) and the source for HttpWebResponse and I dont believe you can get this information from the headers.
HttpClient/HttpClientHandler does use HttpWebResponse internally however it does not expose all properties from HttpWebResponse :
private HttpResponseMessage CreateResponseMessage(HttpWebResponse webResponse, HttpRequestMessage request)
{
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(webResponse.StatusCode);
httpResponseMessage.ReasonPhrase = webResponse.StatusDescription;
httpResponseMessage.Version = webResponse.ProtocolVersion;
httpResponseMessage.RequestMessage = request;
httpResponseMessage.Content = (HttpContent) new StreamContent((Stream) new HttpClientHandler.WebExceptionWrapperStream(webResponse.GetResponseStream()));
//this line doesnt exist, would be nice
httpResponseMessage.IsFromCache = webResponse.IsFromCache;// <-- MISSING!
...
}
So your options the way I see it are:
a) Look at the source code for HttpWebRequest to determine the logic for IsFromCache and retrofit this somehow into HttpClient (this may not even be possible, depends on what the logic actually does/needs)
b)ask the ASP.NET team for this property to be included with HttpResponseMessage. either directly as a property or perhaps they could 'keep' the HttpWebResponse
Neither of these options are that great sorry, hence my original question, what are you trying to acheive?

I've been struggling with this scenario recently as well.
What I needed was an integration test to verify that:
Responses for a newly created resource had the correct headers set by the server.
Subsequent requests for that resource were fulfilled from the client-cache.
Responses for an existing resource had the correct headers set by the server as well.
What I ended up doing was a twofold check:
A non-caching HttpClient to check the initial response:
new WebRequestHandler
{
AllowAutoRedirect = true,
UseCookies = true,
CookieContainer = new CookieContainer(),
CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Refresh)
};
var client = new HttpClient(handler)
and a second HTTP client to check the client-side cache:
new WebRequestHandler
{
AllowAutoRedirect = true,
UseCookies = true,
CookieContainer = new CookieContainer(),
CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Default)
};
var client = new HttpClient(handler)
To verify the source of response messages I compare the HttpResponseMessage.Headers.Date values from steps 1 and 2 (which will be the same if the response came from the client cache). For my third step I can just re-use the client from the first step and append an arbitrary string to the URL.
Disclaimer: this applies to .NET Framework 4.7 and ignores best practices concerning HttpClient usage but is seems to do the trick for me in my test suite. An explicit property like the one mentioned above would be preferable but does not seem to be available. Since the last reply here is already a few years old there might be better ways to handle this, but I couldn't think of one.

Related

C# http request add json object in the content

I'm making an http request post to an external api. I am constructing a json object to add to the request body. How can I check if the added body/content is correct before it is sent.
public async void TestAuthentication()
{
var client = new HttpClient();
var request = new HttpRequestMessage()
{
RequestUri = new Uri("http://test"),
Method = HttpMethod.Post
};
var jsonObj = new
{
data = "eneAZDnJP/5B6r/X6RyAlP3J",
};
request.Content = new StringContent(JsonConvert.SerializeObject(jsonObj), Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
}
If you are not sure whether the serialization works as intended, you could give it a shot in LINQpad or dotnetfiddle.net. See my example that returns the JSON on the console. These tools are great for quick prototyping a method or a snippet, if you are not sure if a piece of code works as intended.
You could also check in Wireshark, but that could be a bit of an overkill and works best if your connection if not encrypted (no HTTPS).
I personally tend to test code that calls some API the following way:
Make the called URL parameterizable (via the classes constructor)
If there is any variable data this data should be passed as the methods parameter(s)
For your test start an HTTP server from your test fixture (read on testing with xUnit or NUnit if you don't know what this means)
I use PeanutButter.SimpleHTTPServer for that
Pass the local IP to the class that accesses the API
Check whether the HTTP server received the expected data
Whether or not this kind of code shall be tested (this way) may be debatable, but I found this way to work kind of good. I used to abstract the HttpClient class away, but IMHO I would not recommend this anymore, because if the class accesses the API (and does not do anything else, which is important), the HTTP access is the crucial part that shall be tested and not mocked.

Default proxy in .net core 2.0

I saw couple of questions asked about core 2.0 on how to make HttpClient to use default proxy configured on the system. But no where found right answer. Posting this question hoping someone who might have encountered this issue might have found the solution by now.
In .net framework versions I've used the following configuration in my web.config and it worked for me.
<system.net>
<defaultProxy useDefaultCredentials="true"></defaultProxy>
</system.net>
But in .net core 2.0 where I've make a web request to external api from my company's intranet my code is failing with 407, proxy authentication required.
After little bit of research I am of the opinion that it is not possible to make your HttpClient to use default proxy settings configured via WPAD in IE. Can someone correct my understanding here?
On this page of https://github.com/dotnet/corefx/issues/7037
It is said as follows :
"The default for HttpClientHandler.UseProxy property is true. And the default value of HttpClientHandler.Proxy is NULL which means to use the default proxy."
But I don't observe this behavior.
Update:
I am finally able to call external web api by specifying the proxy server address and then making the HttpClient call. Still wondering how to use default proxy setup in IE.
using (var handler = new HttpClientHandler {
Credentials = new System.Net.NetworkCredential(user, password, domain),
UseProxy = true,
Proxy = new System.Net.WebProxy(new Uri("http://xxxxxxx:8080"), true)
})
{
handler.Proxy.Credentials = new NetworkCredential("xxxx", "yyyyy", "cccccc");
using (var httpClient = new HttpClient(handler))
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri(destinationUrl),
Method = HttpMethod.Post
};
request.Content = new StringContent(requestXml, Encoding.UTF8, "text/xml");
HttpResponseMessage response = await httpClient.SendAsync(request);
Task<Stream> streamTask = response.Content.ReadAsStreamAsync();
}
}
If any one interested in finding out how I was able to find out the proxy server was, I wrote the following code in .net 4.0 and found out the proxy used.
var proxy = WebRequest.GetSystemWebProxy();
var url = proxy.GetProxy(new Uri("http://google.com"));
Thanks
I hope this is the answer you're looking for: Default Proxy issues #28780
If you simply want to use the default system proxy and need to pass default credentials to that proxy (because the proxy is an authenticated proxy) during HTTP requests, then do this:
var handler = new HttpClientHandler();
handler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
var client = new HttpClient(handler);

UWP HttpClient disable "If" headers

I found one interesting feature in UWP and HttpClient (it also works with WebRequest) :
Any Http request sends "If-*" headers. I did experiment with UWP and WPF apps. I sent request to Azure file storage which doesn't support "If-" headers and will return Error 400 if headers will be sended. So here is my code:
HttpClient client = new HttpClient();
var response = await client.GetAsync("LINK_TO_AZURE_FILE_STORAGE_IMAGE");
Very simply, similar for two apps. Result - WPF app doesn't send "If-*" headers, UWP does. So It means that I'm not able to use File Storage in UWP apps, I just have Error 400.
My question is - can I disable this st...d caching ? Thanks for your attention
Yeah, while using HttpClient in UWP apps, it will automatically use the local HTTP cache by default. For the first time, you code should work. Then you will get 400 Error as in the first response, it contains cache data and all subsequent requests will use this cache by default like following:
To fix this issue, we can use Windows.Web.Http.HttpClient class with HttpBaseProtocolFilter class and HttpCacheControl class to disable the cache like following:
var filter = new Windows.Web.Http.Filters.HttpBaseProtocolFilter();
filter.CacheControl.ReadBehavior = Windows.Web.Http.Filters.HttpCacheReadBehavior.MostRecent;
filter.CacheControl.WriteBehavior = Windows.Web.Http.Filters.HttpCacheWriteBehavior.NoCache;
var httpClient = new Windows.Web.Http.HttpClient(filter);
var response = await httpClient.GetAsync(new Uri("LINK_TO_AZURE_FILE_STORAGE_IMAGE"));
To make this method work, we need make sure there is no local HTTP cache. As HttpCacheReadBehavior.MostRecent represents it will still use the local HTTP cache if possible. So we'd better uninstall the app first and do not use HttpClient client = new HttpClient(); in the app.
Update:
Starting from Windows Anniversary Update SDK, there is a new enum value NoCache added to HttpCacheReadBehavior enumeration. With a combination of ReadBehavior and WriteBehavior, we can implement a variety of cache-related behaviors. When we don't want to use local cache, we can just set the ReadBehavior to HttpCacheReadBehavior.NoCache like:
var filter = new Windows.Web.Http.Filters.HttpBaseProtocolFilter();
filter.CacheControl.ReadBehavior = Windows.Web.Http.Filters.HttpCacheReadBehavior.NoCache;
var httpClient = new Windows.Web.Http.HttpClient(filter);
Have you tried using the Remove method of the DefaultRequestHeaders property of the HttpClient class?

MVC to WebApi : Can a header be compressed?

I have started to get some issues where I am over the limit with my headers.
I have a MVC controller, calling a WebApi Controller/Service.
I know the trigger, it is a saml-token (xml) that I've converted to base64.
No, I don't have control of the SecurityToken service...so JWT is not an option at this time. Trust me, I've raised my concerns several times.
We use the saml to secure the WebApi Controller(s) using a custom delegating handler that reads the custom-header and transforms it to a ClaimPrincipal...
I have seen gzip code examples for dealing with the Response, but after hours of googling, I haven't found if there is a way to compress my custom header (or all of them if that's the only way)...for the ~Request.
Ideally I would be able to compress the
"X-My-Custom-Header"
and deal with uncompressing it on the webapi side....
So I'm at a loss to know if this is even possible. This is the first time I've ever had to deal with a way too big header issue.
Sample MVC code below. As an FYI, the windows-credentials are sent over, but that contains the Identity that runs the AppPool that runs the MVC.
My custom header is the saml that is associated with the specific logged in User. Thus why I need to send it over and consider it separately from the windows-identity.
using (var client = new HttpClient(HttpClientHandlerFactory.GetWindowsAuthenticationHttpClientHandler()))
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string base64SerializedToken = "SuperDuperLongBase64String_IMeanSuperDuper";
client.DefaultRequestHeaders.Add("X-My-Custom-Header", base64SerializedToken);
Uri baseUri = new Uri("http:www.mywebapiservice.com");
Uri destinationUri = new Uri(baseUri, "doSomething");
HttpResponseMessage response = client.PostAsJsonAsync(new Uri(new Uri(this._baseUri), destinationUri.ToString()).ToString(), accountName).Result;
if (response.IsSuccessStatusCode)
{
returnItem = response.Content.ReadAsAsync<MyCustomReturnObject>().Result;
}
else
{
string errorMessage = response.Content.ReadAsStringAsync().Result;
throw new InvalidOperationException(errorMessage);
}
}
public static class HttpClientHandlerFactory
{
public static HttpClientHandler GetWindowsAuthenticationHttpClientHandler()
{
HttpClientHandler returnHandler = new HttpClientHandler()
{
UseDefaultCredentials = true,
PreAuthenticate = true
};
return returnHandler;
}
}
Considering that a SAML token can be several kilobytes in size, depending on the number of claims, sending it as a header is probably a bad idea. Even if you can get it to work now there's no guarantee that it will continue to work if the claim count grows.
Since there is no standard for header compression you will have to modify both ends of the conversation to do something about it. That being the case, why not simply add the SAML token as part of the request body in your API?
If that's really not going to fly (I get that project managers are often painful when it comes to things like this) then you'll have to look into using something like GZipStream to pack the XML, but at some point you're still going to run into problems. This is a bandaid, not a solution.
No, there's no standard for header compression in HTTP. This might have something to do with the fact that you'd need to read the headers to know if (and how) the headers are compressed.
If you don't have a way to decompress whatever manual compression you figure out on the other side, you're out of luck.

AuthenticationHeaderValue Vs NetworkCredential

I'm trying to write a client for either HTTP Post or HTTP Get using HttpClient. When Googling around I come across these methods that set these authentication within the HttpClient object. One uses NetworkCredential while the other uses AuthenticationHeaderValue
HttpClient sClient;
HttpClientHandler sHandler = new HttpClientHandler();
sHandler.Credentials = new NetworkCredential("UserName", "Password");
sClient = new HttpClient(sHandler);
OR
HttpClient sClient new HttpClient();
sClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic",Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes("UserName:Password")));
Reading on MSDN does not give me a distinct answer about the differences between them. Is this a case where both will do the same thing except how its authentication information is stored? such as AuthenticationHeaderValue puts it in the header while the other doesn't? Is one better than the other in term of my use case or best practices ?
The 2nd approach is more flexible in such way that you can specify a type of authentication (e.g., anonymous, basic, window, certificate, etc) to use.
If your first approach doesn't work, try to specify the 3rd param on NetworkCredential, which is the domain name.

Categories