I am making a lot of web api calls in my c# code. I don't know how to catch errors. Suppose, internet connection isn't working, then my code shows runtime error. How to I properly put them into try catch block, What's the general rule. All the articles I found were on how to throw back the error and error message.
example API calls:
WebResponse webResponse = webRequest.GetResponse();
string res = webResponse.ToString();
Also
using (var client = new HttpClient())
client.BaseAddress = new Uri(CairoBaseUrl);
var getStringTask = client.GetStringAsync(requestUrl);
response = await getStringTask;
And,
HttpResponseMessage response = await client.PostAsync(
url,requestContent);
if you are calling asp.net web api i would suggest you to use HttpClient which is done for that purposes
try
{
HttpResponseMessage response = await client.GetAsync("http://www.contoso.com/");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
// Above three lines can be replaced with new helper method below
// string responseBody = await client.GetStringAsync(uri);
Console.WriteLine(responseBody);
}
catch(HttpRequestException e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ",e.Message);
}
This is example from MSDN how to deal with exceptions using http client
in you example you have
using (var client = new HttpClient())
client.BaseAddress = new Uri(CairoBaseUrl);
var getStringTask = client.GetStringAsync(requestUrl);
response = await getStringTask;
But it wont work since await operator can be used only with methods marked async so it should be
var getStringTask = await client.GetStringAsync(requestUrl);
Related
I'm successfully getting a token back from my GetAccessToken() and GetAccessTokenAsync methods, but the token isn't retrieved until after the main method of GetCourses, which won't work because that's the method that collects the data I need to show on my cshtml page. I've tried pulling apart this controller and creating a Globals class that will house just the URIs, apiKey, and token, but then I read that's bad practice for MVC so I ditched that effort. It was getting called after the GetCourses method anyway, so it was dead end too.
I'm newer to MVC and come from a WebForms background where I was used to being able to throw this kind of code in my PageInit, but am struggling to figure out how to pull this off in MVC. Can someone help me figure out what I am doing wrong or if I need to go about this a different way?
public ActionResult GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = GetAccessToken().ToString();
IEnumerable<Course> courses = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri((string)TempData["EthosURI"]);
client.DefaultRequestHeaders.Add("Authorization", "Bearer {" + (string)TempData["Token"] + "}");
client.DefaultRequestHeaders.Add("Accept", "application/json");
//HTTP GET
var responseTask = client.GetAsync("courses");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
}
else //web api sent error response
{
//log response status here..
courses = Enumerable.Empty<Course>();
ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
}
}
return View(courses);
}
public static async Task<string> GetAccessToken()
{
var token = await GetAccessTokenAsync("redactedUrl", "redactedAPIKey");
return token;
}
public static async Task<string> GetAccessTokenAsync(string ethosURI, string apiKey)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ethosURI);
client.DefaultRequestHeaders.Accept.Clear();
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(ethosURI)
};
request.Headers.Clear();
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("Accept", "application/json");
request.Headers.CacheControl = new CacheControlHeaderValue() { NoCache = true };
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
The (non-blocking) way in C# to wait for a task to complete is to use the await keyword. And for a method to use the await keyword, it has to be marked async. By using await, you not only wait for the task to complete, but also the current thread is not blocked. Wrapping an asynchronous operation in another method would not make it synchronous. In other words, the asynchronous nature propagates up the call hierarchy and the caller has to await. So, the GetAccessToken() still has to be awaited. A controller action can be marked asynchronous as well, so you probably want:
public async Task<ActionResult> GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = (await GetAccessToken()).ToString(); // note the additional parentheses
....
Note the additional parantheses above before calling ToString(). However, since GetAccessToken() already returns a string, you don't need the redundant ToString() call:
TempData["Token"] = await GetAccessToken();
Now, you can also change this:
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
to just:
courses = await result.Content.ReadAsAsync<IList<Course>>();
Microsoft has quite good documentation on asynchronous programming and I would recommend checking it out.
That's not how async works in C#. You need either to make GetCourses() async AND await for GetAccessToken(), or use dirty hack GetAccessToken().GetAwaiter().GetResult() but it may become not safe in certain circumstances.
After 2-4 downloading of videos data from API using HttpClient suddenly prompt error.
Here's my code:
public async Task<byte[]> GetMedia(string id)
{
var api = $"/api/v1/download/{id}";
var Uri = $"{MccBaseURL}{api}";
byte[] responseBody;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("No");
try
{
HttpResponseMessage response = await httpClient.GetAsync(Uri);
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsByteArrayAsync();
if (response.IsSuccessStatusCode)
{
return responseBody;
}
}
catch (Exception ex)
{
Debug.Print(ex.Message);
throw;
}
}
Then below is the error faced:
Additional error info:
Error
Please help me?
First, you should dispose your HttpResponseMessage, as you have in your answer, but not in the original question.
The most likely issue, though, is your use of DefaultRequestHeaders. You should only use this for headers that apply to every request that the HttpClient instance will send, and then you should set them only once, when you create the client, as the documentation implies ("Headers set on this property don't need to be set on request messages again").
While HttpClient is essentially thread-safe, the DefaultRequestHeaders (and BaseAddress) properties are not. You're changing these values while the client instance is potentially busy using them elsewhere. It's not clear whether you're using the singleton HttpClient elsewhere as well, possibly changing the default headers there too, but if so that would significantly increase the chances of issues arising.
Some additional references about the non-thread-safety of these properties:
https://github.com/dotnet/dotnet-api-docs/issues/1085
http://www.michaeltaylorp3.net/httpclient-is-it-really-thread-safe/
https://github.com/MicrosoftDocs/architecture-center/issues/935
I found an answer which is:
public async Task<bool> GetMedia(string saveDir, string id)
{
var api = $"/api/v1/download/{id}";
var Uri = $"{MccBaseURL}{api}";
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(Uri, HttpCompletionOption.ResponseHeadersRead))
using (System.IO.Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
{
string fileToWriteTo = System.IO.Path.GetTempFileName();
using (System.IO.FileStream streamToWriteTo = new System.IO.FileStream(saveDir, System.IO.FileMode.Create))
{
await streamToReadFrom.CopyToAsync(streamToWriteTo);
return true;
}
}
}
}
It was really memory something problem which continuously using same HttpClient over and over again. So I created a new instance. I'm a super noob! Sorry!
There is a POST rest api which used to work from code before. But recently it is broken and is not returning any response. However if I try to call the api from the Postman, then it works fine.
In what way can I debug this to find the root cause of the issue ?
Following is the C# code which I am using to call this post rest api
public async Task SaveToServerAsync()
{
string filePath = #"<filePath>";
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
// tried this line of code from another SO answer, but this didn't work either
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://<server name>/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "d2ebf9aefbaa416adcd0");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("Accept", "*/*");
client.DefaultRequestHeaders.Add("Connection", "keep-alive");
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
var content = new MultipartFormDataContent();
content.Add(new StreamContent(fileStream), "file", filePath);
content.Add(new StringContent("e8d002f9-f381-44c2-bce0-13416929f14d"), "Id");
try
{
var response = await client.PostAsync("<rest api end point>", content).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
Debug.Write("Response received");
}
}
catch (Exception ex)
{
Debug.Write("Exception occured");
Debug.Write(ex.Message);
}
finally
{
}
}
}
}
It always goes to the exception block with exception as "The task was cancelled"
Not sure how can I debug it when it anyway works from the Postman.
So the problem was related to ExpectContinue header which goes as true by default. Somehow server was not handling it properly and client was waiting for continue (100) message for indefinite time.
For the time being manually setting this header to be false worked for us:
httpClient.DefaultRequestHeaders.ExpectContinue = false;
There is another question about this, but it doesn't have a functioning solution at the end, and the only good answer, for some reason, doesn't work, not for the guy who ask it, not for me either.
This such question is here:
How to post data using HttpClient?
Given the corresponding aclarations, this is the code I have so far:
The methods to call the method who connects with the web server:
private void Button_Click(object sender, System.EventArgs e)
{
//. . . DO SOMETHING . . .
PopulateListView();
//. . . DO SOMETHING ELSE . . .
}
private void PopulateListView()
{
//. . . DO SOMETHING . . .
list = await "http://web.server.url".GetRequest<List<User>>();
//. . . DO SOMETHING ELSE . . .
}
The method than connects with the web server:
public async static Task<T> SendGetRequest<T>(this string url)
{
try
{
var uri = new Uri(url);
HttpClient client = new HttpClient();
//Preparing to have something to read
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("OperationType", "eaf7d94356e7fd39935547f6f15e1c4c234245e4")
});
HttpResponseMessage response = await client.PostAsync(uri, formContent);
#region - - Envio anterior (NO FUNCIONO, SIN USO) - -
//var stringContent = new StringContent("markString");
//var sending = await client.PostAsync(url, stringContent);
//MainActivity.ConsoleData = await client.PostAsync(url, stringContent);
#endregion
//Reading data
//var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
MainActivity.ConsoleData = json.ToString();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
}
catch(Exception ex)
{
Console.WriteLine("Error: "+ex.ToString());
return default(T);
}
}
You maybe guessed it, but I'm trying to make a method that send some data (through POST) called "markString" to a web-server than receive it and, depending of the "markString" it returns certain json Objects.
This web server is already working properly (I tested it out with some plug-in, it work like it should)
This method is supposed to send the "markString" and receive the data back so then i can use it in the app.
I'm making a Xamarin Android application.
Also have in mind than I don't have any connection problem at all, in fact the app is sending data in an excellent matter when I try to do it using web client, but I want it to send it using HttpClient.
The problem
The code is not returning anything. Any request for information, clarification, question, constructive comments or anything than can lead to an answer would be greatly appreciated too.
Thanks in advance.
Most deadlock scenarios with asynchronous code are due to blocking further up the call stack.
By default await captures a "context" (in this case, a UI context), and resumes executing in that context. So, if you call an async method and the block on the task (e.g., GetAwaiter().GetResult(), Wait(), or Result), then the UI thread is blocked, which prevents the async method from resuming and completing.
void Main()
{
var test = SendGetRequest("http://www.google.com");
test.Dump();
}
public async static Task<string> SendGetRequest(string url)
{
try
{
var uri = new Uri(url);
HttpClient client = new HttpClient();
//Preparing to have something to read
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("OperationType", "eaf7d94356e7fd39935547f6f15e1c4c234245e4")
});
HttpResponseMessage response = await client.PostAsync(uri, formContent);
#region - - Envio anterior (NO FUNCIONO, SIN USO) - -
//var stringContent = new StringContent("markString");
//var sending = await client.PostAsync(url, stringContent);
//MainActivity.ConsoleData = await client.PostAsync(url, stringContent);
#endregion
//Reading data
//var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
return json;
}
catch (System.Exception ex)
{
Console.WriteLine("Error: " + ex.ToString());
return string.Empty;
}
}
I have the following, wher I try to download a string from the server:
HttpClient client = new HttpClient();
var getResponsestring = await client.GetStringAsync("url");
But how do I go about and the if the server does not return the string I want, but error 401 for instance? Or any web error for that mather
You have two choices. Either break down the request into two steps.
HttpClient client = new HttpClient();
var response = await client.GetAsync("url");
if (response.IsSuccessStatusCode) {
var getResponsestring = await response.Content.ReadAsStringAsync();
}
or you insert a new MessageHandler that will return a stock response on errors.
var errorMessageHandler = new ErrorMessageHandler(new HttpClientHandler());
HttpClient client = new HttpClient(errorMessageHandler);
var getResponsestring = await client.GetStringAsync("url");
You will have to implement the ErrorMessageHandler yourself by deriving from a DelegatingHandler and overriding SendAsync.
HttpClient.GetStringAsync Method returns Task which has Exception property.
Update
When we use async/await we can just wrap the awaitable call in try/catch block to handle exceptions.