HttpClient failing in accessing simple website - c#

Here is my code
internal static void ValidateUrl(string url)
{
Uri validUri;
if(Uri.TryCreate(url,UriKind.Absolute,out validUri))
{
using (HttpClient client = new HttpClient())
{
try
{
HttpResponseMessage response = client.Get(url);
response.EnsureStatusIsSuccessful();
}
catch (Exception ex)
{
//exception handler goes here
}
}
}
}
This code when i run it produces this result.
ProxyAuthenticationRequired (407) is not one of the following:
OK (200), Created (201), Accepted (202), NonAuthoritativeInformation
(203), NoContent (204), ResetContent (205), PartialContent (206).
All i want to do is make this code validate whether a given website is up and running.
Any ideas?

This basically means exactly what it says: That you are trying to access the service via a proxy that you are not authenticated to use.
I guess that means your server was reached from the Web Service, but that it was not permitted to access the URL it tried to reach, since it tried to access it through a proxy it was not authenticated for.

It's what EnsureStatusIsSuccessful() does, it throws an exception if status code (returned from web server) is not one of that ones.
What you can do, to simply check without throwing an exception is to use IsSuccessStatusCode property. Like this:
HttpResponseMessage response = client.Get(url);
bool isValidAndAccessible = response.IsSuccessStatusCode;
Please note that it simply checks if StatusCode is within the success range.
In your case status code (407) means that you're accessing that web site through a proxy that requires authentication then request failed. You can do following:
Provide settings for Proxy (in case defaults one doesn't work) with WebProxy class.
Do not download page but just try to ping web server. You won't know if it's a web page or not but you'll be sure it's accessible and it's a valid URL. If applicable or not depends on context but it may be useful if HTTP requests fails.
Example from MSDN using WebProxy with WebRequest (base class for HttpWebRequest):
var request = WebRequest.Create("http://www.contoso.com");
request.Proxy = new WebProxy("http://proxyserver:80/",true);
var response = (HttpWebResponse)request.GetResponse();
int statusCode = (int)response.StatusCode;
bool isValidAndAccessible = statusCode >= 200 && statusCode <= 299;

You are invoking EnsureStatusIsSuccessful() which rightfully complains that the request was not successful because there's a proxy server between you and the host which requires authentication.
If you are on framework 4.5, I've included a slightly enhanced version below.
internal static async Task<bool> ValidateUrl(string url)
{
Uri validUri;
if(Uri.TryCreate(url,UriKind.Absolute,out validUri))
{
var client = new HttpClient();
var response = await client.GetAsync(validUri, HttpCompletionOption.ResponseHeadersRead);
return response.IsSuccessStatusCode;
}
return false;
}

Related

How to propagate HTTP responses cleanly to consumers of a typed HTTP client in ASP.NET Core

Is there a good way of propagating an object on a successful response or the status code and response body to consumers of a typed HTTP client in ASP.NET Core?
Given the following API service:
public class TestApiService
{
private readonly HttpClient _httpClient;
public TestApiService(HttpClient httpClient)
{
httpClient.BaseAddress = new Uri("https://localhost:5000");
_httpClient = httpClient;
}
public async Task<string> GetVersion()
{
var response = await _httpClient.GetAsync("/api/v1/version");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
return null;
}
}
which is registered with the DI container via:
services.AddHttpClient<TestApiService>();
I would like to return the string value from the TestApiService.GetVersion() method if the response was successful or if the response was not successful return the status code and the response body.
It doesn't appear to be possible to do something like:
public async Task<string> GetVersion()
{
var response = await _httpClient.GetAsync("/api/v1/version");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
and get the desired outcome because the HttpRequestException thrown from HttpResponseMessage.EnsureSuccessStatusCode() does not include the status code or the response body.
There is an open issue about this on GitHub but I'm not sure if it will get implemented anytime soon or not.
While ActionResult does exist it seems to really be for the controller layer so I'm not sure if using it here is an appropriate use of that class or not or if there is a better way of getting the desired outcome?
It should be possible to create my own exception class and throw that from the service but I would really like to avoid that if there is some built-in mechanism that is usable instead.
Remove response.EnsureSuccessStatusCode() this is basically checking the status and if its not a 200 throwing the exception. Consider using response.IsSuccessStatusCode or check the response status code manually. Either way will prevent the raising of the exception which you don't want.
if (HttpStatusCode.Ok == response.StatusCode)
{
// Read your result
}
else if ( // handle the specific failure case was it a 404 or a 401)
{
string value = await response.Content?.ReadAsStringAsync();
// Read your failed result
return $"{response.StatusCode} {value}".Trim()";
}
The next question is how you handle and communicate failure to the callee's of your service? Do you want your service to be opaque to your client application?
Since your code is only returning a string, have you considered either returning something else such as an encompassing object { Success = true|false, Error = "", ErrorCode = 1234, Data = "value"} or simply throwing an appropriate exception to communicate the nature of the failure. E.g. You might want to throw an appropriate exception, e.g. TestApiException where TestApiException might have the ErrorCode or whatever you need on it.

WebRequest Strange NotFound Error

I have 2 different ASP.NET Core websites: admin and public.
Both running on staging server and on local machine.
I send GET request to different pages to determine execution time of different pages and encountered problem with admin site: all urls on local instance and staging always returns 404 error:
An unhandled exception of type 'System.Net.WebException' occurred in
System.dll
Additional information: The remote server returned an error: (404) Not
Found.
Meanwhile, same requests in browser return html pages normally. Requests through HttpWebRequest to public site always also return 200 Status Code (OK).
Code for request I took here.
I tried to add all headers and cookies from browser request, but it didn't help. Also tried to debug local instance and found that no exceptions thrown while request executed.
Any ideas?
404 is way to generic. The code provided in answer in your link (https://stackoverflow.com/a/16642279/571203) does no error handling - this is brilliant example of how you can get to troubles when you blindly copy code from stackoverflow :)
Modified code with error handling should look like:
string urlAddress = "http://google.com/rrr";
var request = (HttpWebRequest)WebRequest.Create(urlAddress);
string data = null;
string errorData = null;
try
{
using (var response = (HttpWebResponse)request.GetResponse())
{
data = ReadResponse(response);
}
}
catch (WebException exception)
{
using (var response = (HttpWebResponse)exception.Response)
{
errorData = ReadResponse(response);
}
}
static string ReadResponse(HttpWebResponse response)
{
if (response.CharacterSet == null)
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
return reader.ReadToEnd();
}
}
using (var reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(response.CharacterSet)))
{
return reader.ReadToEnd();
}
}
So when there is an exception, you'll get not just the status code, but entire response from server in the errorData variable.
One thing to check is proxy - browser can use http proxy while your server client uses none.

C# HttpClient 303 SeeOther bug

HttpClient automatically handles 303 (SeeOther) as described in https://msdn.microsoft.com/en-us/library/system.net.httpstatuscode(v=vs.118).aspx
SeeOther automatically redirects the client to the URI specified in
the Location header as the result of a POST. The request to the
resource specified by the Location header will be made with a GET.
The same behavior seems to happen on GET requests, but the call fails as if my Authorization header was missing.
I set the header using
public void SetOauthToken(String key, String token)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(key, token);
}
I am able to fix the error by intercepting the 303 and calling the URI from Location header by hand.
For example I change
public async Task<T> GetXML<T>(String url)
{
using (HttpResponseMessage response = await client.GetAsync(url))
{
string result = await response.Content.ReadAsStringAsync();
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(new StringReader(result));
}
}
to
public async Task<T> GetXML<T>(String url)
{
using (HttpResponseMessage response = await client.GetAsync(url))
{
if (response.StatusCode == HttpStatusCode.SeeOther)
{
return await GetXML<T>(response.Headers.Location.ToString());
}
else
{
string result = await response.Content.ReadAsStringAsync();
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(new StringReader(result));
}
}
}
and it fixes the bug.
When examining the redirect request everything seems fine. The redirect call works from Postman.
Any idea why is this happening?
First, don't always believe the http status responses. Websites often change the values to make it harder for hackers to get access to the server. When a Http connection is made a negotiation takes place between client and server using the http headers. The negotiation attempts to get a common mode for the transfer. For example a server may be designed to work in English, French, and German. The client includes English in the header so the client will then connect to the English webpages. So you leaving out the AuthenticationHeaderValue is not a bug, but a line of code that is required. A IE webpage is more robust than the Net Http Client and sends more default headers. If you used a sniffer like wireshark or fiddler and compared the headers using an IE and you application you would see the differences.

C# Unit Testing using webClient.UploadString

I am using webClient.uploadString(uri,parameter) method.
It hits an url which returns a string. Whenever there is Bad request or other similar kind of errors, the server throws exception.
While Unit testing I need to compare the Response Status Code with some specific status codes.
The UploadString method only return string.
My query is:
How can I get the Response Object from WebClient so that I can compare the status Code?
You can only determine the status code if the request fails:
try
{
var client = new WebClient();
client.DownloadString("...");
}
catch (WebException ex)
{
var statusCode = ((HttpWebResponse)ex.Response).StatusCode;
...
}
If you want to determine the status code when the request is successful you'll need to monkey patch WebClient...
I actually reccomend that you use HttpClient instead of WebClient. It is has a much nicer interface:
var client = new HttpClient();
HttpResponseMessage response = client.GetAsync("...").Result;
HttpStatusCode status = response.StatusCode;
string result = response.Content.ReadAsStringAsync().Result;
Bare in mind that what you are describing is not a unit test but an integration test and it does not seem useful.

RestSharp + Server Down - How do you know if server is down?

I am wondering how can I check if the RestSharp request I made failed because the server is down vs something else.
When I shutdown my server I get a status code of "NotFound" but that could be a particular record was not found(which I do on my site if say they try to find a record that might be recently deleted).
How can I figure out the server is actually down?
Edit
here is my code
private readonly RestClient client = new RestClient(GlobalVariables.ApiUrl);
var request = new RestRequest("MyController", Method.POST);
request.AddParameter("UserId", "1");
request.AddParameter("Name", name.Trim());
var asyncHandle = client.ExecuteAsync(request, response =>
{
var status = response.StatusCode;
});
When the server is down, it should not return a "404 NotFound" error.
The most appropriate in this case is HTTP Error 503 - Service unavailable
The Web server (running the Web site) is currently unable to handle
the HTTP request due to a temporary overloading or maintenance of the
server. The implication is that this is a temporary condition which
will be alleviated after some delay. Some servers in this state may
also simply refuse the socket connection, in which case a different
error may be generated because the socket creation timed out.
That way checking that RestResponse.StatusCode is 503 it will tell you that the server is down.
I am having the same issue.
I decided to check for the ContentLength too. At least my webservice always returns ContentLength>0 (even for NotFound occurrences). This seems to work out.
if ( response.StatusCode == System.Net.HttpStatusCode.NotFound &&
response.ContentLength == -1 ){
==> client couldn't connect to webservice
}
In my scenario, I needed to check if anything happened to the connection, e.g. connection time out, can't resolve the host name, etc. So I also added following code:
try
{
var client = new RestClient(_configuration.ServerUrl);
var request = new RestRequest
{
Resource = _configuration.SomeUrl,
Method = Method.POST
};
var response = client.Execute(request);
if (response.ErrorException != null)
{
throw response.ErrorException;
}
}
catch (WebException ex)
{
// TODO: check ex.Status, if it matches one of needed conditions.
}
For more information about WebExceptionStatus Enumeration, please, see following link https://msdn.microsoft.com/en-us/library/system.net.webexceptionstatus(v=vs.110).aspx

Categories

Resources