HttpClient is ignoring the `www.` part of the Request URI - c#

I'm trying to send a GET request with HttpClient:
public static HttpClient Client { get
{
var handler = new HttpClientHandler() { AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip };
var http = new HttpClient(handler) { BaseAddress = new Uri("https://www.example.com/") };
http.DefaultRequestHeaders.Accept.Clear();
http.DefaultRequestHeaders.Accept.TryParseAdd("*/*");
http.DefaultRequestHeaders.AcceptLanguage.TryParseAdd("en-US;q=0.6,en;q=0.4");
http.DefaultRequestHeaders.AcceptEncoding.TryParseAdd("gzip, deflate, br");
http.DefaultRequestHeaders.Host = "example.com";
http.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest");
http.DefaultRequestHeaders.Add("Connection", "keep-alive");
http.DefaultRequestHeaders.Add("Keep-Alive", "600");
http.DefaultRequestHeaders.UserAgent.TryParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36");
ServicePointManager
.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
return http;
}
...
using (var client = HttpHelper.Client)
using (var res = await client.GetAsync("api/stuff/filter?order=" + order + "&page=" + page))
if (res.IsSuccessStatusCode)
{
var json = await res.Content.ReadAsStringAsync();
var response = JsonConvert.DeserializeObject<AnimeResponse>(json);
return response;
}
Unfortunately this sends a request without the www. part and results in 301 status code as well as many, many failed redirects to the same address (without the www. part).
How can I fix this?
Edit: When I re-send the request with Fiddler it also returns 301, but the n it redirects correctly to wwww.. The HttpClient doesn't, and just redirects to the same www-less URI.

Related

C# HttpClient Host In Header not work with response 301/302

After using other DNS clients to resolve the IP and manually set the HOST in HEADER, it works fine, but when the server responds with 301/302 to other websites, the HOST in HEADER does not automatically change to the new HOST
public static HttpClient HttpClient { get; } =
new(new HttpClientHandler() {
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
AllowAutoRedirect = true,
MaxAutomaticRedirections = 6,
CheckCertificateRevocationList = false,
ServerCertificateCustomValidationCallback = (_, _, _, _) => true });
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36");
HttpClient.DefaultRequestHeaders.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
HttpClient.DefaultRequestHeaders.AcceptEncoding.ParseAdd("gzip, deflate");
HttpClient.Timeout = TimeSpan.FromMilliseconds(App.Config.HttpClientWait ? App.Config.HttpClientAsyncTimeout : App.Config.HttpClientTimeout);
var url = this.DomainTask.Domain!.Trim().StartsWith("http://") || this.DomainTask.Domain.Trim().StartsWith("https://") ? this.DomainTask.Domain : $"{(this.UseHttps ? "https" : "http")}://{this.DomainTask.Domain}/";
var uri = new Uri(url);
var protocol = url.StartsWith("https://") ? "https" : "http";
var ipUrl = $"{protocol}://{this.DomainTask.Result!.Ip}:{uri.Port}/";
var msg = new HttpRequestMessage(HttpMethod.Get, IPAddress.TryParse(this.DomainTask.Result!.Ip, out _) ? ipUrl : url);
msg.Headers.Host = this.DomainTask.Domain;
if (App.Config.Debug) App.Logger.Debug($"{this.DomainTask.Domain} - ipUrl: {msg.RequestUri}");
var result = HttpClient!.SendAsync(msg).ContinueWith(x => {......});

C# Xamarin file upload to API works using RestSharp but does not work using HttpClient

I'm attempting to upload an image file from the phone camera to a BuddyPress API from my Xamarin app (the API call documentation can be found here - https://developer.buddypress.org/bp-rest-api/reference/attachments/member-avatar/)
I can do this successfully using RestSharp as follows;
public string PostMediaFile(MediaFile data, string path, bool https = false, string authorisationToken = "")
{
var requestMethod = "http://";
if (https)
{
requestMethod = "https://";
}
var serverString = requestMethod + path;
var client = new RestClient(serverString)
{
Timeout = Convert.ToInt32(timeOut)
};
client.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer " + authorisationToken);
request.AddHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36");
request.AddFile("file", data.Path);
request.AddParameter("action", "bp_avatar_upload");
IRestResponse response = client.Execute(request);
return response.Content;
}
However, all my other requests in the application are performed using HttpClient and I'd like to keep it consistent, so I came up with the follow function to replace this;
public async Task<string> PostMediaFile(MediaFile data, string path, bool https = false, string authorisationToken = "")
{
var memoryStream = new MemoryStream();
data.GetStream().CopyTo(memoryStream);
byte[] fileAsBytes = memoryStream.ToArray();
var fileContent = new ByteArrayContent(fileAsBytes);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
Name = "file",
FileName = Path.GetFileName(data.Path),
};
fileContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
var content = new MultipartFormDataContent
{
{ fileContent, "file", Path.GetFileName(data.Path) },
{ new StringContent("action"), "bp_avatar_upload" }
};
var requestMethod = "http://";
if (https)
{
requestMethod = "https://";
}
var clientHandler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; }
};
httpClient = new HttpClient(clientHandler);
if (!string.IsNullOrWhiteSpace(authorisationToken))
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authorisationToken);
httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36");
}
httpClient.Timeout = TimeSpan.FromMilliseconds(timeOut);
var serverString = requestMethod + path;
HttpResponseMessage response = await httpClient.PostAsync(serverString, content);
HttpContent Content = response.Content;
var json = await Content.ReadAsStringAsync();
response.Dispose();
return json;
}
The problem is, obviously it doesn't work, and I don't know why. I just get the following response;
{"code":"bp_rest_attachments_user_avatar_upload_error","message":"Upload failed! Error was: Invalid form submission..","data":{"status":500,"reason":"upload_error"}}
I feel like I'm really close, but not sure where my mistake is.
Ah! it was so simple! I had part of the form data the wrong way around;
{ new StringContent("action"), "bp_avatar_upload" }
should be
{ new StringContent("bp_avatar_upload"), "action" }

How to update the cookies in Httpclient instance which created via HttpClientFactory -C#

By using Httpclient straightly in .Net Console application, I can manage the cookies via below code:
HttpClientHandler handler = new HttpClientHandler();
CookieContainer cookieContainer = handler.CookieContainer;
using (HttpClient client = new HttpClient(handler) { Timeout = TimeSpan.FromMinutes(30) })
{
var urlLogin="http://xxxxxx";
var strContent="key=value"
await client.PostAsync(new Uri(url), new StringContent(strContent, Encoding.UTF8, "application/x-www-form-urlencoded"))
var cookie = cookieContainer.GetCookies(new Uri(url)).Cast<Cookie>().ToList();//get cookies
cookie[0].Path = "/";
cookieContainer.Add(cookie[0]); //update the first cookie path to avoid missing cookies in redirect request (302)
//other action
}
But in .Net core 3.1, I'm using HttpClientFactory to create HttpClient instance, initially I can add cookies with similar code as below when build HttpClient, but the handler is not accessible in the client instance, therefore I don't know how to update the cookie in the instance.
services.AddHttpClient("test1", c =>
{
c.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
c.Timeout = TimeSpan.FromHours(1);
}).ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler
{
CookieContainer = new CookieContainer(),
UseDefaultCredentials = true
}) ;
You can switch to HttpClient.SendAsync(HttpRequestMessage). In that case you create HttpRequestMessage instance for each request separately and then set cookie through HttpRequestMessage.Headers. Also you need to keep cookie value itself in some singleton class.
var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = new StringContent("content here", Encoding.UTF8, "application/x-www-form-urlencoded")
};
request.Headers.Add("Cookie", "your_value");
await _httpClientFactory.CreateClient().SendAsync(request);
So you set cookie per request and don't need to update it in clients.
Finally it was resolved:
Create a static variable
public class ScopedVars
{
public static HttpClientHandler staticHandler { get; set; } = new
HttpClientHandler();
}
In Setup, register it
services.AddScoped<ScopedVars>();
Add httpclient
services.AddHttpClient("TEST", c => {
c.DefaultRequestHeaders.Add("Accept", "/");
c.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT
10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"); c.Timeout =
TimeSpan.FromMinutes(5); }).ConfigurePrimaryHttpMessageHandler(_
=>ScopedVars.staticHandler);
In the service layer
private HttpClientHandler _handler; public
TestService(IHttpClientFactory httpClientFactory, ScopedVars
scopedHandler) {
_httpClientFactory = httpClientFactory; _handler = ScopedVars.staticHandler; }
In a specific task
CookieContainer cookieContainer = _handler.CookieContainer; using
(HttpClient client = _httpClientFactory.CreateClient("TEST")) {
//XXXX; }
Since I just run it as QuartzNet task, no need to worry about the multiple request.

HttpClient high data usage when getting remote file size

I'm getting the file size of remote urls and I just noticed the difference between HttpClient and httpWebRequest.
I compared and I noticed that httpclient is taking too much data.
this is a big issue for me because, in the Philippines we are only have limited data
Could you please tell me what's wrong with my httpclient class? I can't figure out what is causing the high data usage
HttpClient
HttpClientHandler handler = new HttpClientHandler()
{
Proxy = null,
UseProxy = false,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("Method", "GET");
client.DefaultRequestHeaders.Add("Referer", uriPath.AbsoluteUri);
client.DefaultRequestHeaders.Add("Origin", "https://www.samplesite.com");
client.DefaultRequestHeaders.ConnectionClose = true;
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
using (HttpResponseMessage response = await client.GetAsync(uriPath, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
var resultTask = response.Content.ReadAsStringAsync();
var timeoutTask = Task.Delay(3000);
var completed = await Task.WhenAny(resultTask, timeoutTask).ConfigureAwait(false);
if (completed == timeoutTask)
return null;
return await resultTask;
}
HttpWebRequest
var webRequest = (HttpWebRequest)WebRequest.Create(uriPath);
webRequest.Method = "HEAD";
webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36";
using (var webResponse = await webRequest.GetResponseAsync())
{
return await Task.Run(() => webResponse.Headers.Get("Content-Length"), token);
}
You are using different HTTP methods GET in case of HttpClient & HEAD in case of WebRequest. To get file size you will enough HEAD method in both cases
The HTTP GET method requests a representation of the specified resource. Requests using GET should only retrieve data.
The HTTP HEAD method requests the headers that are returned if the specified resource would be requested with an HTTP GET method. Such a request can be done before deciding to download a large resource to save bandwidth, for example.
You need to change this code line
client.DefaultRequestHeaders.Add("Method", "GET");
it MUST BE
client.DefaultRequestHeaders.Add("Method", "HEAD");
A response to a HEAD method does not have a body in contradistinction to GET
UPD: use SendAsync method (not GetAsync)
HttpClientHandler handler = new HttpClientHandler();
using var client = new HttpClient(handler);
string requestUri = "enter request uri";
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Head, requestUri);
using var response = await client.SendAsync(message);
Finally it's solved Big thanks to #Dmitry
Here's the HttpClient updated code
public static async Task<string> GetTotalBytes(Uri uriPath)
{
HttpClientHandler handler = new HttpClientHandler();
handler.Proxy = null;
using (var client = new HttpClient(handler))
{
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Head, uriPath);
using (var response = await client.SendAsync(message))
{
response.EnsureSuccessStatusCode();
var lenght = response.Content.Headers.ContentLength;
return lenght.ToString();
}
}
}
RESULT (HttpClient):
Total URL: 355
Filsize: 1.14 GB
Elapsed Time: 0h 0m 2s 51.3ms
and here's HttpWebRequest (Maybe someone will need)
var webRequest = (HttpWebRequest)WebRequest.Create(uriPath);
webRequest.Method = "HEAD";
webRequest.Proxy = null;
using (var webResponse = await webRequest.GetResponseAsync())
{
return await Task.Run(() => webResponse.Headers.Get("Content-Length"), token);
}

API error 401 Unauthorized laravel C Sharp

I am developing an application using C # that calls a RESTApi de laravel. Through Postman it works correctly, but instead of C# no, it returns error 401.
If I remove the following if in the Laravel controller it works fine:
if ($ request-> isJson ()) {
The header Content-Type is set to application/json
Laravel Code
function getResult(Request $request, $id)
{
if ($request->isJson()) {
// Eloquent
$times = Result::selectRaw('THE SELECT')
->where('ID', $Id)
->get();
$result = [];
foreach($times as $key => $time)
{
...........
}
sort($result);
return response()->json(['results'=>$result], 200);
}
return response()->json(['error' => 'Unauthorized'], 401, []);
}
C#
public static async Task<dynamic> GETTimes(int eventID, int stageID)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(baseUrl);
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
client.DefaultRequestHeaders.TryAddWithoutValidation("cache-control", "no-cache");
client.DefaultRequestHeaders.Add("User-Agent", #"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36");
// Add the Authorization header with the AccessToken.
//client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// create the URL string.
string url = string.Format("api/v1/events/{0}/results/{1}", eventID, stageID);
// make the request
HttpResponseMessage response = await client.GetAsync(url);
// parse the response and return the data.
string jsonString = await response.Content.ReadAsStringAsync();
object responseData = JsonConvert.DeserializeObject(jsonString);
return (dynamic)responseData;
}
}

Categories