HttpClient decoding my encoded URI causes request to fail - c#

HttpClient seems to not respect encoded strings.
var id = Uri.EscapeDataString("\"MyId\"");
var uri = new Uri($#"https://www.example.com/{id}").AbsoluteUri;
var req = new HttpRequestMessage(HttpMethod.Post, uri);
My uri object contains https://www.example.com/%22MyId%22 which is correct.
Inspecting req.RequestUri shows the correct value for every property except for LocalPath, everything else seems to have the correct %22 encoded quote.
I then send the request:
await httpClient.SendAsync(req)
and it makes a request to https://www.example.com/"MyId" (no longer encoded?) and the request fails.
uri.UserEscaped shows false after creating. I tried to escape the entire URI using Uri.EscapeUriString, but that function is deprecated and suggests using Uri.EscapeDataString for the query (which I'm already doing).
I followed the solution from the GitHub issue in this question, however it did not fix my issue (using AbsoluteUri).
This question is incorrect as it is not just the ToString() method, the actual request includes the decoded values.
This question is just a dead link.
How can I actually send my request with the encoded %22 quote values?

Don't do that Uri.EscapeDataString, just include the id as-is in the Uri.
var id = "\"MyId\"";
var uri = new Uri($#"https://www.example.com/{id}").AbsoluteUri;
var req = new HttpRequestMessage(HttpMethod.Post, uri);
Fiddler shows below request was made.
POST https://www.example.com/%22MyId%22

you can use them (namespace System.Web)
HttpUtility.UrlDecode("")
HttpUtility.UrlEncode("")

Related

httpClient.PostAsync() performs GET request anyway

I really am not sure what is happening.
I'm using an HttpClient to post XML content to a remote server using the PostAsync method like this:
using var content = new StringContent(payload, Encoding.UTF8, "application/xml");
using var response = await _httpClient.PostAsync(string.Empty, content);
... where payload is a string, and relative uri is empty because I just need to call base uri of httpclient.
I can perform same request in Postman and it works fine.
The issue is, for some reason httpclient actually performs a GET request instead of POST, and ignores content whatsoever:
I've checked in Postman, and it seems like it is a normal response from the server to GET request.
I've also tried
using var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, string.Empty){Content = content});
... and it gives the same result.
This looks like a very weird issue to me, as I've never seen http client behaving like this in the past. Could anyone please explain why is this happening? Thanks!
OK, so the issue was actually with server.
It redirected all the requests with URLs not ending with "/", like http://address.com/page to the same address but ending with "/" - http://address.com/page/, and lost the method and content in process.
As #Jimi mentioned, the RequestMessage field in HttpResponseMessage contains the info about the last request that reached the server, therefore initial request data was lost, and I mistook it for HttpClient making wrong requests.

Accessing Microsoft Cognitive Service through Restful Service

I'm receiving the following error while accessing the Microsoft Cognitive API:
I'm 100% sure that my subscription key is valid because I have tested it in DHC as well as the online tool of Project Oxford.
I'm using sample code provided by Microsoft. Here it is...
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString("safeee");
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{bce8988422e94fd3ac64xxxxxxxxxxxx}");
var uri = "https://api.projectoxford.ai/face/v1.0/persongroups/{personGroupId}?" + queryString;
HttpResponseMessage response;
byte[] byteData = Encoding.UTF8.GetBytes("{body}");
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PutAsync(uri, content);
MessageBox.Show(response.ToString());
}
For any code snippet in the Microsoft Cognitive Services site, including the page to which I believe you're referring, you need to substitute all curly-braced strings with appropriate values. In your case, you need to:
Drop the query string "safeee" from the queryString. There are no query parameters for this particular endpoint.
Drop the curly braces in the Ocp-Apim-Subscription-Key value (sounds like you tried that.)
Give an approriate personGroupId value. Per the documentation on the aforementioned page, "valid characters include numbers, English letters in lower case, '-' and '_'. The maximum length of the personGroupId is 64."
Provide a proper JSON value for body. In your case you might simply use "{\"name\":\"safeee\"}".

C# HttpWebRequest GET partially encoded url

When I send a get using HttpWebRequest is seems to turn it into a uri, run it through an encoder and send the encoded string. When I look at my address in the request after it is created I have the OriginalString which is correct and an AbsoluteUri which is encoded and incorrect. My code and example urls are below.
HttpWebRequest webRequest = System.Net.WebRequest.Create(url) as HttpWebRequest;
String responseData = WebResponseGet(webRequest);
OriginalString:"https://api.linkedin.com/v1/people/url=https%3A%2F%2Fwww.linkedin.com%2Fin%2Ffirstmlast"
AbsoluteUri:"https://api.linkedin.com/v1/people/url=https%3A//www.linkedin.com/in/firstmlast"
How can I force HttpWebRequest to send my original string that I passed it and not a uri? Also I cannot send the already encoded string as a query string, LinkedIn requires it to be apart of the url.
I found a HackedUri class here: http://blogs.msdn.com/b/xiangfan/archive/2012/01/16/10256915.aspx and created my request like this passing it a "Hacked Uri" instead of a string. This seems to be a security limitation problem with .Net.
HttpWebRequest webRequest = System.Net.WebRequest.Create(HackedUri.Create(url)) as HttpWebRequest;
Have you tried double-encoding the relevant part of the URL?
var request = WebRequest.CreateHttp("https://api.linkedin.com/v1/people/url=" + HttpUtility.UrlEncode("https%3A%2F%2Fwww.linkedin.com%2Fin%2Ffirstmlast"));

WebRequest: Query string data vs x-www-form-urlencoded content

I am trying to call Google's OAuth2 authentication service as per these instructions: https://developers.google.com/accounts/docs/OAuth2ForDevices
I put all of the required parameters into the query string and sent the request. This worked for the "Obtaining a user code" section but not for the "Obtaining Access and Refresh Tokens" section.
After much playing around and getting 400 Bad Request errors, I found that, instead of putting the data in the query string, you can create a request with a FormUrlEncodedContent and send the data through as content with application\x-www-form-urlencoded Content-Type.
Here is the code before:
var requestMessage = new HttpRequestMessage();
requestMessage.Method = "POST";
requestMessage.RequestUri = new Uri(fullUrl);
Where fullUrl is something like:
https://accounts.google.com/o/oauth2/device/code?client_id=812741506391-h38jh0j4fv0ce1krdkiq0hfvt6n5amrf.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile
And the new code is:
var requestMessage = new HttpRequestMessage();
requestMessage.Method = "POST";
requestMessage.RequestUri = new Uri(url);
requestMessage.Content = new FormUrlEncodedContent(CreateDictionary(queryStringNames, queryStringValues));
Where url is:
https://accounts.google.com/o/oauth2/device/code
and queryStringNames and queryStringValues are string arrays of the names and values of the required parameters.
What is the difference between these two methods? Is it safe to assume that all POST calls can use the URL Encoded Content requests instead of putting the data in the query string?
In general, POST requests do not need query string but it is still subjected to Server's logic implementation. In case of OAuth which is quite known standard and they do follow good practice, it is safe to use form encoded data unless mentioned explicitly in API to send Parameter as query string.
Query String & Post data are two different set of parameters. If server is expecting Query string then you must send query string only. It all depends on how server side logic is implemented. You can not use them alternatively. Most API documentation specify clearly what are they expecting.

HttpWebRequest not being received correctly in MVC ASP.NET

This is me publicly documenting my mistake so that if I or anyone does it again, they don't have to spend 3 hours tearing their hair out trying to fix such a simple thing.
Context
I was sending an HttpRequest from one C# MVC ASP.NET application to another.
The applications require an HTTPS connection, and we are using URLRewrite to redirect an HTTP request to an HTTPS url.
One application was sending a POST request with some JSON data in the body, pretty standard stuff. The other application was set up to receive this data with an MVC controller class (CollectionAction and Insert methods for GET and POST respectively).
Symptoms of the problem
The receiving application was running the GET method (CollectionAction) instead of the POST action (ItemAction). The reason for this was that the request coming in to the application was in fact a GET request, and to top it off the JSON data was missing too.
I sent the header "x-http-method" to override the request method from GET to POST (I was already setting the request httpmethod to POST but this was being ignored). This worked but still I had no data being sent.
So now I am stuck pulling my hair out, because I can see a POST request with content-length and data being sent out and I have a GET request with no data or content-length coming in (but the headers were preserved)
Turns out I was using UriBuilder to take a base URL and apply a resource path to it. For example I would have "google.com" in my web.config and then the UriBuilder would take a resource like Pages and construct the url "google.com/Pages". Unfortunately, I was not initializing the UriBuilder with the base URL, and instead was using a second UriBuilder to extract the host and add that to the path like so:
public Uri GetResourceUri(string resourceName)
{
var domain = new UriBuilder(GetBaseUrl());
var uribuilder = new UriBuilder()
{
Path = domain.Path.TrimEnd('/') + "/" + resourceName.TrimStart('/'),
Host = domain.Host
};
var resourceUri = uribuilder.Uri;
return resourceUri;
}
The problem with this code is that the scheme is ignored (HTTP:// vs HTTPS://) and it defaults to HTTP. So my client was sending out the request to an HTTP url instead of the required HTTPS url. This is the interesting part, URLRewrite was kicking in and saying that we needed to go to an HTTPS url instead so it redirected us there. But in doing so, it ignored the Http-Method and the POST data, which just got set to defaults GET and null. This is what the 2nd application could see at the receiving end.
So the function had to be rewritten to this which fixed the problem:
public Uri GetResourceUri(string resourceName)
{
var baseUrl = GetBaseUrl();
var domain = new UriBuilder(baseUrl);
var uribuilder = new UriBuilder(baseUrl)
{
Path = domain.Path.TrimEnd('/') + "/" + resourceName.TrimStart('/'),
};
var resourceUri = uribuilder.Uri;
return resourceUri;
}

Categories

Resources