Download a JSON String in C# - c#

I'm trying to download a JSON string in my Windows Store App which should look like this:
{
"status": "okay",
"result": {"id":"1",
"type":"monument",
"description":"The Spire",
"latitude":"53.34978",
"longitude":"-6.260316",
"private": "{\"tag\":\"david\"}"}
}
but I'm getting what looks like info about the server. The output I'm getting is as follows:
Response: StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
MS-Author-Via: DAV
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Date: Thu, 22 Nov 2012 15:13:53 GMT
Server: Apache/2.2.22
Server: (Unix)
Server: DAV/2
Server: PHP/5.3.15
Server: with
Server: Suhosin-Patch
Server: mod_ssl/2.2.22
Server: OpenSSL/0.9.8r
X-Powered-By: PHP/5.3.15
Content-Length: 159
Content-Type: text/json
}
I've been looking around and see that WebClient was used before Windows 8, and is now replaced with HttpClient. So instead of using DownloadString(), I've been using Content.ReadAsString(). Here's the bit of code I have so far:
public async Task<string> GetjsonStream()
{
HttpClient client = new HttpClient();
string url = "http://(urlHere)";
HttpResponseMessage response = await client.GetAsync(url);
Debug.WriteLine("Response: " + response);
return await response.Content.ReadAsStringAsync();
}
Anyone know where I'm going wrong?
Thanks in advance!

You are outputting the server response. The server response contains a StreamContent (see documentation here) but this StreamContent doesn't define a ToString, so the class name is output instead of the content.
ReadAsStringAsync (documentation here) is the right method to get the content sent back by the server. You should print out the return value of this call instead:
public async Task<string> GetjsonStream()
{
HttpClient client = new HttpClient();
string url = "http://(urlHere)";
HttpResponseMessage response = await client.GetAsync(url);
string content = await response.Content.ReadAsStringAsync();
Debug.WriteLine("Content: " + content);
return content;
}

If you are inside await block you might need to get the Result ReadAsStringAsync().Result.
Example:
public async Task<HttpResponseMessage> Listen()
{
await Request.Content.ReadAsMultipartAsync<MultipartMemoryStreamProvider>(
new MultipartMemoryStreamProvider()).ContinueWith(task =>
{
MultipartMemoryStreamProvider multipartMemoryStreamProvider = task.Result;
var imageContent = multipartMemoryStreamProvider.Contents.First();
string name = imageContent.Headers.ContentDisposition.Name;
string fileName = imageContent.Headers.ContentDisposition.FileName;
data = imageContent.ReadAsByteArrayAsync().Result;
string content = multipartMemoryStreamProvider.Contents.Last().ReadAsStringAsync().Result;
model = multipartMemoryStreamProvider.Contents.Last().ReadAsAsync<RecordingModel>().Result;
}
);
}

Related

HTTP 500 response is received when using C# HttpClient

I have developed a C# application that calls a REST service existing in some PC in the network.
This is the code to make a request:
public async Task<bool> OpenDoorAsync(string name, int delay)
{
var data = await CallApiAsync("api/door/remoteOpenByName", new Dictionary<string, string> { { "doorName", name }, { "interval", delay.ToString() } });
return data.IsSuccess;
}
private async Task<ResponseData> CallApiAsync(string endPoint, Dictionary<string, string> parameters)
{
try
{
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Connection.ParseAdd("keep-alive");
var content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
string fullUri = "http://192.168.0.122:8088/api/door/remoteOpenByName?doorName=10.185.85.237-1&interval=5&access_token=1234";
HttpResponseMessage response = await client.PostAsync(fullUri, content);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<ResponseData>(responseBody);
}
}
catch (Exception ex)
{
OnError("Existió un error al realizar la llamada.", ex);
return new ResponseData()
{
message = "failed"
};
}
}
Entry point is OpenDoorAsync, called this way, from a Winform form:
await _device.OpenDoorAsync(TxtNombrePuerta.Text.Trim(), IntInterval.Value);
Well, after the execution of PostAsync method, a HTTP 500 error is returned:
{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: close
Date: Thu, 28 Jan 2021 21:06:35 GMT
Set-Cookie: JSESSIONID=4062B932CDB44B4CA3FCCC275937AC15; Path=/; HttpOnly
Server: Apache-Coyote/1.1
Content-Length: 2580
Content-Language: en
Content-Type: text/html; charset=utf-8
}}
However, if I make the same request using Google Chrome RESTED extension, it works perfectly:
Just in case, I analyzed Google Chrome developer tools after the RESTED call and I have not found anything weird. I thought maybe I missed to send something else in the headers.
Does anybody know what is happening with the call from the C# application? Clearly, I am not doing something that RESTED is.
I don't really know why it does not work when using HttpClient class, however, I solved the problem installling an using RestSharp NuGet package.
Finally, the code was reduced to this:
private ResponseData CallApi(string endPoint, Dictionary<string, string> parameters)
{
try
{
string fullUri = $"http://{GetServerIp()}:{((MainWindow)MainWindow).ServerPort}/{endPoint}?{GetQueryParameters(parameters)}";
var client = new RestClient(fullUri);
var request = new RestRequest(Method.POST);
var response = client.Execute(request);
return JsonConvert.DeserializeObject<ResponseData>(response.Content);
}
catch (Exception ex)
{
OnError("Existió un error al realizar la llamada.", ex);
return new ResponseData()
{
message = "failed"
};
}
}
Your working example is passing cookies, which may be required for the API you're calling.

JSON POST to Sharepoint Using Graph Status Code 400

OK. So this is really starting to baffle me now. I can get this to work on one UWP app, but not this one.
I have this piece of code to post:
public async Task<string> SubmitDataWithTokenAsync(string url, string token)
{
var httpClient = new HttpClient();
HttpResponseMessage response;
try
{
var root = new
{
fields = new Dictionary<string, string>
{
//General Parameters...
//Inspection Parameters...
//Startup Parameters...
//Mechanical Parameters...
//Electrical Parameters...
//SCR Parameters...
//Shutdown Parameters...
}
};
var s = new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat };
var content = JsonConvert.SerializeObject(root, s);
var request = new HttpRequestMessage(HttpMethod.Post, url);
//Add the token in Authorization header
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent(content, Encoding.UTF8, "application/json");
response = await httpClient.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
catch (Exception ex)
{
return ex.ToString();
}
}
}
the content comes back just fine, so it seems....
"{\"fields\":{\"Date\":\"8/16/2018 2:18:48 PM -04:00\",\"Maximo\":null,\"IBX\":\"DC4\",\"Generator\":\"Generator D\",\"AirQuality\":\"Red / Unhealthy\",\"Engineer\":\"Kassim Ganiyou\",\"MT1Level\":null,\"MT2Level\":null,\"StartDTLevel\":null,\"BC1V\":null,\"BC1A\":null,\"BC2V\":null,\"BC2A\":null,\"StartCoolantTEmp\":null,\"StartHours\":null,\"Reason\":null,\"InspectionNotes\":null,\"StartTime\":null,\"CrankV1\":null,\"CrankV2\":null,\"Emissions\":null,\"SCRStartTime\":null,\"OilPressure\":null,\"CoolantTemp\":null,\"BatteryVolt\":null,\"LeftExhTemp\":null,\"RightExhTemp\":null,\"ABVolts\":null,\"BCVolts\":null,\"CAVolts\":null,\"AAmps\":null,\"BAmps\":null,\"CAmps\":null,\"KW\":null,\"Frequency\":null,\"SCROutletTemp\":null,\"NOx\":null,\"UReaFLow\":null,\"Alarms\":null,\"SCRSTopTime\":null,\"StopTime\":null,\"StopHours\":null}}"
The request comes back:
{Method: POST, RequestUri: 'https://graph.microsoft.com/v1.0/sites/root/lists/A07CEC93-XXXX-XXXX-XXXX-0F756D2EF63A/items', Version: 2.0, Content: System.Net.Http.StringContent, Headers:
{
Authorization: Bearer eyJ0eX...PUQ
Content-Type: application/json; charset=utf-8
Content-Length: 603
}}
But then the response is:
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
client-request-id: bb26e6fe-4fae-41ae-921d-aeb39063bd8e
Strict-Transport-Security: max-age=31536000
request-id: bb26e6fe-4fae-41ae-921d-aeb39063bd8e
Transfer-Encoding: chunked
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"East US","Slice":"SliceC","Ring":"5","ScaleUnit":"001","Host":"AGSFE_IN_1","ADSiteName":"EUS"}}
Duration: 319.5961
Cache-Control: private
Date: Thu, 16 Aug 2018 20:03:10 GMT
Content-Type: application/json
}}
I have another UWP app going to the same sharepoint site and I get a Status 201 no problem. I am just not seeing where my issue is in this case.
This is why it is good to take a break from a project and come back to it later.
I found out that I had mistyped ONE letter on the SharePoint list. So today when I reviewed the response code it pointed it our to me.
The code WAS good.
Thanks all.

Azure graph API / c# patch URL

I am attempting to write a password reset application c# and the graph API. I have set permissions for the application within Azure, receive a valid token, and can request information.
I am receiving a 400 Bad Request response when attempting to perform the reset. I believe I am forming the URL incorrectly. Here is the response I receive followed by my code.
Thanks in advance!
Response: StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Transfer-Encoding: chunked
request-id: omitted
client-request-id: omitted
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"North Central US","Slice":"SliceA","Ring":"3","ScaleUnit":"002","Host":"AGSFE_IN_29","ADSiteName":"CHI"}}
Duration: 43.0949
Strict-Transport-Security: max-age=31536000
Cache-Control: private
Date: Mon, 02 Apr 2018 18:06:06 GMT
Content-Type: application/json
}
private static async Task ResetPasswordAsync(HttpClient client, string UPN)
{
var payload = new
{
accountEnabled = true,
passwordProfile = new
{
forceChangePasswordNextSignIn = true,
password = "Password!"
}
};
var payloadJSON = JsonConvert.SerializeObject(payload);
Console.WriteLine(payloadJSON);
HttpMethod method = new HttpMethod("PATCH");
string requestUrl = $"https://graph.microsoft.com/v1.0/users/{UPN}?api-version=1.6";
var request = new HttpRequestMessage(method, requestUrl)
{
Content = new StringContent($"{payloadJSON}", Encoding.UTF8, "application/json")
};
var response = await client.SendAsync(request);
Console.WriteLine("Response: " + response);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException(response.ReasonPhrase);
}
}
According to 400 Bad Request, we could know that there is something wrong with http request. In your case, you could use Fiddler to catch the htt prequest, we could get Query parameter api-version not allowed. So you could remove the api version from the requesturl.
We could get more information about update use from Graph Update user API
PATCH /users/{id | userPrincipalName}
We also need to add the Authorization in the request header.
string requestUrl = $"https://graph.microsoft.com/v1.0/users/{UPN}";
var token ="Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFCSGg0...."
...
request.Headers.Add("Authorization", token);
var response = await client.SendAsync(request);

HttpClient.PutAsync - "Entity only allows writes with a JSON Content-Type header"

I have a POST method that can post data perfectly.
Looking at docs it seems a PATCH (or PUT) should look the exact same, just use PutAsync instead of PostAsync.
Well doing just that I get the following error:
+ postResponse {StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.NoWriteNoSeekStreamContent, Headers:
{
Cache-Control: private
Date: Mon, 09 Oct 2017 12:19:28 GMT
Transfer-Encoding: chunked
request-id: 60370069-f7c4-421d-842e-b1ee8573c2c2
client-request-id: 60370069-f7c4-421d-842e-b1ee8573c2c2
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"North Europe","Slice":"SliceB","ScaleUnit":"002","Host":"AGSFE_IN_7","ADSiteName":"DUB"}}
Duration: 3.2626
Content-Type: application/json
}} System.Net.Http.HttpResponseMessage
And response:
Entity only allows writes with a JSON Content-Type header
And sure enough in the Error I can also see this:
ContentType {text/plain; charset=utf-8} System.Net.Http.Headers.MediaTypeHeaderValue
So the error makes sense, however, I do tell it to use JSON, and it work as intended in my POST-method with the same code:
public async Task UpdateToGraph(object UnSerializedContent, string relativeUrl)
{
string accessToken = await _tokenManager.GetAccessTokenAsync();
HttpContent content = new StringContent(JsonConvert.SerializeObject(UnSerializedContent));
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
Client.DefaultRequestHeaders.Add("ContentType", "application/json");
string endpoint = "https://graph.microsoft.com/v1.0" + relativeUrl;
var postResponse = Client.PutAsync(endpoint, content).Result;
string serverResponse = postResponse.Content.ReadAsStringAsync().Result;
}
You can use the .{Verb}AsJsonAsync HttpClientExtensions methods.
public async Task UpdateToGraph(object UnSerializedContent, string relativeUrl) {
var accessToken = await _tokenManager.GetAccessTokenAsync();
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
Client.DefaultRequestHeaders.Add("ContentType", "application/json");
var endpoint = "https://graph.microsoft.com/v1.0" + relativeUrl;
var postResponse = await Client.PutAsJsonAsync(endpoint, UnSerializedContent);
var serverResponse = await postResponse.Content.ReadAsStringAsync();
}
Also note the proper use of async/await by not mixing blocking calls like .Result with async methods as this can lead to deadlocks.
Reference Async/Await - Best Practices in Asynchronous Programming
Set the content type using the StringContent constructor:
HttpContent content = new StringContent(JsonConvert.SerializeObject(UnSerializedContent), System.Text.Encoding.UTF8, "application/json");
As far as I'm aware, you're not meant to set the content header on the request object when using the HttpClient.

Error 500 Sending JSON POST Request to API

I'm developing an app in Xamarin.Forms (Shared Project Library) that sends a list of custom types (just a document number and a file type enumeration) to a locally hosted API.
If I capture the JSON string and send it from Postman, everything works fine, but as soon as I run the httpClient.PostAsync from the app, I receive the following error:
{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1,
Content: System.Net.Http.StreamContent, Headers: { Cache-Control:
no-cache Pragma: no-cache Server: Microsoft-IIS/7.5 X-AspNet-Version:
4.0.30319 X-Powered-By: ASP.NET Date: Thu, 25 May 2017 16:00:44 GMT Content-Type: application/json; charset=utf-8 Expires: -1
Content-Length: 36 }}
I'm at a loss a to what I'm doing wrong. Can anyone help, please?
Type:
class EmailFiles
{
public string docNumber { get; set; }
public EmailFileTypes.EmailFileType type { get; set; }
}
Request:
List<EmailFiles> files = new List<EmailFiles>();
if (chkShipping.IsToggled)
{
EmailFiles file = new EmailFiles();
file.type = EmailFileTypes.EmailFileType.CustomerFile;
file.docNumber = Helpers.GlobalVariables.gSOLookup.PackList;
files.Add(file);
}
if (chkClosed.IsToggled)
{
EmailFiles file = new EmailFiles();
file.type = EmailFileTypes.EmailFileType.ClosedFile;
file.docNumber = Helpers.GlobalVariables.gSOLookup.Invoice;
files.Add(file);
}
if (chkInvoice.IsToggled)
{
EmailFiles file = new EmailFiles();
file.type = EmailFileTypes.EmailFileType.Invoice;
file.docNumber = Helpers.GlobalVariables.gSOLookup.Invoice;
files.Add(file);
}
string url = SetUrls.urlMtApi + "/api/EmailFile/?emailAddresses=" + strEmails;
string strJson = JsonConvert.SerializeObject(files);
//Results in: "[{\"docNumber\":\"234273\",\"type\":1},{\"docNumber\":\"633007\",\"type\":2}]" - which works in Postman!!
StringContent content = new StringContent(strJson, Encoding.UTF8, "application/json");
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.PostAsync(url, content);
Web Service:
[System.Web.Http.HttpPost]
public string EmailFile([FromBody]string jsonfiles, [FromUri] string emailAddresses)
{
List<EmailFiles> files = JsonConvert.DeserializeObject<List<EmailFiles>>(jsonfiles);
...
}
No need to manually deserialize the json body, just let the model binder do it for you by using correct parameters:
[System.Web.Http.HttpPost]
public MyModelReturnType EmailFile([FromBody] List<EmailFiles> files, [FromUri] string emailAddresses)
{
// the framework has already deserialized the json request for you
}
If you use string instead of the true model for your parameter the server will not be able to bind your request body to it, because it will expect a JSON string (surrounded by double quotes "), and this could cause a model binding exception that will lead to a 500 error in your client.
The same is for the return type, just use whatever class you want your client to receive as return type and do not use string if you want to send it Json.

Categories