What should `ReadAsAsync<string>` and `ReadAsStringAsync` be used for? - c#

What should HttpContentExtensions.ReadAsAsync<string> and HttpContent.ReadAsStringAsync be used for?
They would seem to do similiar things but work in curious ways. A couple of tests and their outputs are below. In some cases JsonReaderException are thrown, in some cases, the JSON is output but with additional escape characters.
I've ended up using both functions across my code base, but was hoping to align on one if I could understand how they were supposed to work.
//Create data and serialise to JSON
var data = new
{
message = "hello world"
};
string dataAsJson = JsonConvert.SerializeObject(data);
//Create request with data
HttpConfiguration config = new HttpConfiguration();
HttpRequestMessage request = new HttpRequestMessage();
request.SetConfiguration(config);
request.Method = HttpMethod.Post;
request.Content = new StringContent(dataAsJson, Encoding.UTF8, "application/json");
string requestContentT = request.Content.ReadAsAsync<string>().Result; // -> JsonReaderException: Error reading string.Unexpected token: StartObject.Path '', line 1, position 1.
string requestContentS = request.Content.ReadAsStringAsync().Result; // -> "{\"message\":\"hello world\"}"
//Create response from request with same data
HttpResponseMessage responseFromRequest = request.CreateResponse(HttpStatusCode.OK, dataAsJson, "application/json");
string responseFromRequestContentT = responseFromRequest.Content.ReadAsAsync<string>().Result; // -> "{\"message\":\"hello world\"}"
string responseFromRequestContentS = responseFromRequest.Content.ReadAsStringAsync().Result; // -> "\"{\\\"message\\\":\\\"hello world\\\"}\""
//Create a standalone new response
HttpResponseMessage responseNew = new HttpResponseMessage();
responseNew.Content = new StringContent(dataAsJson, Encoding.UTF8, "application/json");
string responseNewContentT = responseNew.Content.ReadAsAsync<string>().Result; // -> JsonReaderException: Error reading string.Unexpected token: StartObject.Path '', line 1, position 1.
string responseNewContentS = responseNew.Content.ReadAsStringAsync().Result; // -> "{\"message\":\"hello world\"}"

ReadAsStringAsync: This is a basic "get me the content as a string" method. It will work on anything you throw at it because it's just strings.
ReadAsAsync<T>: This is meant to be used to deserialise a JSON response into an object. The reason it fails is because the JSON in the return is not a valid JSON representation of a single string. For example, if you serialise a string:
var result = JsonConvert.SerializeObject("hello world");
Console.WriteLine(result);
Output is:
"hello world"
Note how it is surrounded by double quotes. If you try to deserialise any arbitrary JSON directly into a string that isn't in the format "....." it will throw the exception you see because it is expecting the JSON to start with a ".

Related

HttpReponseMessage returns 404 when making call to api in C#

I am trying to follow these guidelines to make a call to their API and get a prefilled QR code in return.
https://developer.swish.nu/api/qr-codes/v1#pre-filled-qr-code
But I get error 404 not found back, this is my code:
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://mpc.getswish.net/qrg-swish");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var json = new {
payee = new {value = "1234", editable = false},
amount = new {value = 100, editable = false},
message = new {value = "test message", editable = false},
format = "png"
};
HttpResponseMessage response = await client.PostAsJsonAsync(
"/api/v1/prefilled", json);
Does anybody know what I might be doing wrong?
client.BaseAddress = new Uri("https://mpc.getswish.net/qrg-swish");
HttpResponseMessage response = await client.PostAsJsonAsync("/api/v1/prefilled", json);
The problem is the leading / in the path passed to PostAsJsonAsync, and the lack of trailing / in the BaseAddress. Change that to:
client.BaseAddress = new Uri("https://mpc.getswish.net/qrg-swish/");
HttpResponseMessage response = await client.PostAsJsonAsync("api/v1/prefilled", json);
HttpClient combines the path passed to PostAsJsonAsync with the value of HttpClient.BaseAddress by doing new Uri(BaseAddress, path), see here.
If we try this out:
var baseUri = new Uri("https://mpc.getswish.net/qrg-swish");
var result = new Uri(baseUri, "/api/v1/prefilled");
Console.WriteLine(result);
We get the result:
https://mpc.getswish.net/api/v1/prefilled
Looking at the Remarks for new Uri(Uri baseUri, string relativeUri), we see:
If the baseUri has relative parts (like /api), then the relative part must be terminated with a slash, (like /api/), if the relative part of baseUri is to be preserved in the constructed Uri.
Additionally, if the relativeUri begins with a slash, then it will replace any relative part of the baseUri
So in order to preserve the path at the end of BaseAddress, it needs to end in a /, and the path passed to PostAsJsonAsync needs to not start with a /.
With that in place, we get a 400 Bad Request rather than a 404 Not Found. Let's take a look at the response body, and see whether the server is telling us anything useful:
HttpResponseMessage response = await client.PostAsJsonAsync(
"api/v1/prefilled", json);
Console.WriteLine(response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
We get:
BadRequest
{"timestamp":"2022-03-09T10:14:45.292+0000","status":400,"error":"Bad Request","message":"Invalid phone number length","path":"/qrg-swish/api/v1/prefilled"}
So, our phone number isn't valid: it's too short. Let's fix that:
payee = new {value = "01234567890", editable = false},
Now we get:
BadRequest
{"timestamp":"2022-03-09T10:15:30.675+0000","status":400,"error":"Bad Request","message":"The size parameter is required when using format PNG","path":"/qrg-swish/api/v1/prefilled"}
Right, we need to specify a size. In fairness, the docs do say:
Size of the QR code. The code is a square, so width and height are the same. Not required if the format is svg.
So let's do that:
size = 300,
And there we have it, success!

RestSharp content response returning bad json format

I am using RestSharp library to make requests to an WebApi.
This is how i am doing it:
var client = new RestClient(url);
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", autentication);
RestSharp.IRestResponse response = client.Execute(request);
This works good until here. The issue that i have is that the content of the response is returning like this:
string jsonObject2 = "\"{\\\"status\\\":\\\"success\\\",\\\"entities\\\":[{\\\"bank_code\\\":2,\\\"name\\\":\\\"BANK 02\\\"},{\\\"bank_code\\\":3,\\\"name\\\":\\\"BANCK 03\\\"},{\\\"bank_code\\\":4,\\\"name\\\":\\\"BANCK 04\\\"}]}\"";
The response.content is adding 2 more \ and when i tried to deserialize and it throws an exception that it cannot convert a string to my model.
How can i resolve that the content returns in this format?
string jsonObject = "{\"status\":\"success\",\"entities\":[{\"bank_code\":2,\"name\":\"BANK 02\"},{\"bank_code\":3,\"name\":\"BANK 03\"},{\"bank_code\":4,\"name\":\"BANK 04\"}]}";
This format i can deserialize because of the correct string json format.
It works when i deserialize to string the content that returns the API and then deserialize again the string into the model type that i want.
string jsonData = JsonConvert.DeserializeObject<string>(restResponse.Content);
EntidadResponse data = JsonConvert.DeserializeObject<EntidadResponse>(jsonData);
string result = string.Empty;
using (var reader = new StreamReader(await responseContent.ReadAsStreamAsync()))
{
result = await reader.ReadToEndAsync();
}
return Content(result, "application/json");

Parsing & Filtering JSON data from API into C# application

My goal is to retrieve JSON data from "The virus tracker" API and parse it into a label. This is for my IB personal project so I just started learning c#. Fount this old code and tried fixing it, it worked with basic GET API's but it doesn't work with the one I'm using. (English not my main language)
POSTMAN RESPONSE
{
"results": [
{
"total_cases": 5954607,
"total_recovered": 2622507,
"total_unresolved": 2255875,
"total_deaths": 363208,
"total_new_cases_today": 53700,
"total_new_deaths_today": 1659,
"total_active_cases": 45257,
"total_serious_cases": 2698001,
"total_affected_countries": 213,
"source": {
"url": "https://thevirustracker.com/"
}
}
],
"stat": "ok"
}
C# CODE
//Creating Client connection
RestClient restClient = new RestClient("https://thevirustracker.com/free-api?global=stats");
//Creating request to get data from server
RestRequest restRequest = new RestRequest("total_cases", Method.GET);
// Executing request to server and checking server response to the it
IRestResponse restResponse = restClient.Execute(restRequest);
// Extracting output data from received response
string response = restResponse.Content;
// Parsing JSON content into element-node JObject
var jObject = JObject.Parse(restResponse.Content);
//Extracting Node element using Getvalue method
string cases = jObject.GetValue("total_cases").ToString();
label1.Text = (cases);
ERROR
An unhandled exception of type 'Newtonsoft.Json.JsonReaderException' occurred in Newtonsoft.Json.dll
Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
My final goal with this code is that label1 says "5954607"
Remember that I'm really new to this so if you can explain the changes you made to the code ill really appreciate it.
You're not using RestRequest correctly or initialized the RestClient wrongly.
If that url you used in the RestClient is the whole url then you don't need a "resource" in the RestRequest.
Due to that mistake you got an HTML response indicated by the error
Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
The value of restResponse.Content probably starts with <html and that is not valid JSON.
The Json payload is a bit more complex then you coded for. The results object holds an array with an object that has your stat property.
Putting these fixes together gives you this code.
RestClient restClient = new RestClient("https://thevirustracker.com/free-api?global=stats");
//Creating request to get data from server
RestRequest restRequest = new RestRequest(null, DataFormat.Json);
// Executing request to server and checking server response to the it
IRestResponse restResponse = restClient.Execute(restRequest);
// Extracting output data from received response
string response = restResponse.Content;
// Parsing JSON content into element-node JObject
var jObject = JObject.Parse(restResponse.Content);
//Extracting
// the object has a results property
// that is an array with one item (zero-based)
// on index 0 there is an object
// that has a property total_cases
string cases = (string) jObject["results"][0]["total_cases"];
label1.Text = (cases);

Error while Deserializing JSON output

I am getting the following error at dynamic jsonText = JsonConvert.DeserializeObject(json);
ERROR
Unexpected character encountered while parsing value: <. Path '', line
0, position 0.
CODE
string api = "https://api.linkedin.com/v1/people/~:(id,first-name,formatted-name,email-address)";
using (var webClient = new WebClient())
{
webClient.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token);
var json = webClient.DownloadString(api );
dynamic jsonText = JsonConvert.DeserializeObject(json);
}
I think it is necessary to specify that you want the result in json, otherwise some web services returns the data in xml
webClient.Headers.Add(System.Net.HttpRequestHeader.Accept, "application/json");
//also the encoding if need
webClient.Headers.Add(System.Net.HttpRequestHeader.AcceptEncoding, "utf-8");
but in linkedin you must use
webClient.Headers.Add("x-li-format", "json");
More info here
https://developer.linkedin.com/docs/rest-api

Moving from standard .NET rest to RestSharp

For the most part, I have managed quite quickly to move my code from standard .NET code to using RestSharp. This has been simple enough for GET processes, but I'm stumped for POST processes
Consider the following
var request = System.Net.WebRequest.Create("https://mytestserver.com/api/usr") as System.Net.HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/json;version=1";
request.Headers.Add("Content-Type", "application/json;version=1");
request.Headers.Add("Accepts", "application/json;version=1");
request.Headers.Add("Authorize", "key {key}");
using (var writer = new System.IO.StreamWriter(request.GetRequestStream())) {
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes("{\n \"firstName\": \"Dan\",\n \"lastName\": \"Eccles\",\n \"preferredNumber\": 1,\n \"email\" : \"testuser#example.com\",\n \"password\": \"you cant get the wood\"\n}");
request.ContentLength = byteArray.Length;
writer.Write(byteArray);
writer.Close();
}
string responseContent;
using (var response = request.GetResponse() as System.Net.HttpWebResponse) {
using (var reader = new System.IO.StreamReader(response.GetResponseStream())) {
responseContent = reader.ReadToEnd();
}
This is fairly straight forward to move across, except for the serialisation code. Is there a particular way this has to be done for RestSharp? I've tried creating an object and using
var json = JsonConvert.SerializeObject(user);
restRequest.RequestFormat = DataFormat.Json;
restRequest.AddBody(json);
but the server still comes back with an error.
I'm also currently using JSON.NET for deserialization to an error object when the user passes in bad data. Is there a way I can deserialize to error object based on a single string using RestSharp?
You're close, but you don't need to worry about serialization with RestSharp.
var request = new RestRequest(...);
request.RequestFormat = DataFormat.Json;
request.AddBody(user); // user is of type User (NOT string)
By telling it that the format is JSON, then passing your already-serialized-as-JSON string, RestSharp is actually encoding it again as a string.
So you pass the string: {"firstName":"foo"} and it actually gets sent to the server as a JSON string object: "{\"firstName\":\"foo\"}" (note how your JSON is escaped as a string literal), which is why it's failing.
Note you can also use an anonymous object for the request:
var request = new RestRequest(...);
request.RequestFormat = DataFormat.Json;
request.AddBody(new{
firstName = "Dan",
lastName = "Eccles",
preferredNumber = 1,
// etc..
});
You use the same typed objects with the response (eg, RestSharp deserializes for you):
var response = client.Execute<UserResponse>(request);
// if successful, response.Data is of type UserResponse

Categories