C# Unit Testing using webClient.UploadString - c#

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.

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.

How do I know if my HTTP port requests worked in c#?

So I manage to create a post requests but I have no idea on how to know if it worked?
How can I get the html code of the website after the requests?
here is my code:
var client = new WebClient();
var method = "POST"; // If your endpoint expects a GET then do it.
var parameters = new NameValueCollection();
parameters.Add("utf8", "✓");
parameters.Add("style", data);
parameters.Add("size", size);
parameters.Add("commit", "add to basket");
var response_data = client.UploadValues(url_add_to_cart, method, parameters);
Moreover, after this post requests I need to do another post requests with the same session I used before. Is it possible by doing everything in the same webclient??
Thank you very much for your answers!
If you need more info I'll add it
Simply add a try catch block in this line :
try
{
var response_data = client.UploadValues(url_add_to_cart, method, parameters);
}
catch(Exception e)
{
Console.WriteLine(e);
}
If you get an error on your HTTP request, program should execute the catch block.
Try debbuging your code and see what is inside the response_data var.
HTTP error are metioned here btw :
https://www.npmjs.com/package/http-errors
WebClient will throw WebException if the server returns a non-success (200) code.
Examine WebException.Status for more details.
https://msdn.microsoft.com/en-us/library/system.net.webexception.status.aspx

Differences between using C# HttpClient API and the postman testing? Client call works on postman, but not C# httpClient getAsync

I am testing a REST API post, and it works well when I try it on Postman. However, in some scenario (related to the posting XML data) if I post with HttpClient API, I would receive the following error:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
But the same XML content works fine on Postman with status OK and proper response.
What is the differences between using the C# HttpClient API and the postman testing? How can I configure my API call to match with the behavior on postman?
Here I attached the source code, and the Postman screenshot
public void createLoan()
{
string baseCreateLoanUrl = #"https://serverhost/create?key=";
var strUCDExport = XDocument.Load(#"C:\CreateLoan_testcase.xml");
using (var client = new HttpClient())
{
var content = new StringContent(strUCDExport.ToString(), Encoding.UTF8, Mediatype);
string createLoanApi = string.Concat(baseCreateLoanUrl, APIKey);
try
{
var response = client.PostAsync(createLoanApi, content).Result;
}
catch (Exception ex)
{
MessageBox.Show("Error Happened here...");
throw;
}
if (response.IsSuccessStatusCode)
{
// Access variables from the returned JSON object
string responseString = response.Content.ReadAsStringAsync().Result;
JObject jObj = JObject.Parse(responseString);
if (jObj.SelectToken("failure") == null)
{
// First get the authToken
string LoanID = jObj["loanStatus"]["id"].ToString();
MessageBox.Show("Loan ID: " + LoanID);
}
else
{
string getTokenErrorMsg = string.Empty;
JArray errorOjbs = (JArray) jObj["failure"]["errors"];
foreach (var errorObj in errorOjbs)
{
getTokenErrorMsg += errorObj["message"].ToString() + Environment.NewLine;
}
getTokenErrorMsg.Dump();
}
}
}
Thanks for Nard's comment, after comparing the header, I found the issue my client header has this:
Expect: 100-continue
While postman doesn't has.
Once I removed this by using the ServicePointManager:
ServicePointManager.Expect100Continue = false;
Everything seems fine now. Thanks all the input!
My gut tells me it's something simple. First, we know the API works, so I'm thinking it's down to how you are using the HttpClient.
First things first, try as suggested by this SO answer, creating it as a singleton and drop the using statement altogether since the consensus is that HttpClient doesn't need to be disposed:
private static readonly HttpClient HttpClient = new HttpClient();
I would think it would be either there or an issue with your content encoding line that is causing issues with the API. Is there something you are missing that it doesn't like, I bet there is a difference in the requests in Postman vs here. Maybe try sending it as JSON ala:
var json = JsonConvert.SerializeObject(strUCDExport.ToString());
var content = new StringContent(json, Encoding.UTF8, Mediatype);
Maybe the header from Postman vs yours will show something missing, I think the real answer will be there. Have fiddler running in the background, send it via Postman, check it, then run your code and recheck. Pay close attention to all the attribute tags on the header from Postman, the API works so something is missing. Fiddler will tell you.
I was struggling with this for 2 days when I stumbled over Fiddler which lets you record the traffic to the service. After comparing the calls I saw that I had missed a header in my code.

HttpClient failing in accessing simple website

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;
}

Consuming Twitters 1.1 API using OAuth, getting no response and it happens instantly

I am trying to swap my website over to consuming the new Twitter 1.1 API with uses OAuth 1.0a. I am able to get the correct response using a REST client and I am now trying to duplicate that on my website using c#.
I have constructed my headers the appropriate way and I have verified that they are in the correct format for what Twitter is looking for.
The issue I am having is that I do not think I am actually sending the request. I say this because my application returns almost instantly. The request should take a second or so to send at least, and my response has totally empty, with no 401 or 400 status code.
Below I have the code that actually sends the request. I am actually sending the request and if so why am I not getting any status code or anything.
Thanks in advance for the help.
//string url = "https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=MYSCREENNAME&count=2";
string url = "https://api.twitter.com/1.1/statuses/user_timeline.json";
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "GET";
webRequest.Headers.Add("Authorization", authorizationHeaderParams);
try {
var response = webRequest.GetResponse() as HttpWebResponse;
if (response != null && response.StatusCode != HttpStatusCode.OK) {
lblresponse.InnerText = "The request did not complete and returned status code: {0} " + response.StatusCode;
}
if (response != null) {
var reader = new StreamReader(response.GetResponseStream());
reader.ReadToEnd();
lblresponse.InnerText += "success";
}
} catch {
lblresponse.InnerText += "fail";
}
So yeah this code goes straight to the catch block. My thoughts are I am not actually sending the request, since it takes no time to happen. I know there are some libraries designed to make this easier but I would much rather learn how to do it myself (with the help of you guys).
Thanks.
The request is going to throw an exception in the case of a 400 or 401. So catch System.Web.Exception in the catch block to see if there's a 400 or 401.
catch(System.Web.Exception ex) {
var errorReponse = (HttpWebResponse)ex.Response;
var statusCode = errorReponse.StatusCode;
lblresponse.InnerText += "fail";
}

Categories

Resources