badrequest when sending json data - c#

I'm trying to send json data to an api and it doesn't come trough.
First of all the data is a list of objects. the very first time it works, and that is because the array is empty.
however the second time it tries to send I directly get a 400 without my debugger even coming in the controller.
I suspect that my json data cannot be deserialized form some reason, this is also my second suspect again since my reponse is of type application/problem+json.
However everything might be possible tough.
I have tried to use ['frombody'], I have tried build in json serializer aswell as newtonsoft. I have tried to use formatting.Intended but all witouth luck.
There is one paramater in my object a string that could cause problems as it contains lot of special characters-> this paramater hold a fysical path to a directory so it will contains '/' and '' and space and or other special characters from the directory name.
client:
using (HttpClient client = new HttpClient())
{
var message = new HttpRequestMessage();
message.Content = JsonContent.Create(files, typeof(List<WatchedFile>));
message.RequestUri = new Uri("http://localhost:5245/api/fileagent");
message.Method = HttpMethod.Post;
var response = await client.SendAsync(message);
if (!response.IsSuccessStatusCode)
{
logger.LogError($"Sending to api was not successful {(int)response.StatusCode}");
}
}
This still needs to be refactored to inject the hhtpclient rather thatn the using statement
controller:
[HttpPost]
public async Task<ActionResult> AddMessages([FromBody]List<WatchedFile> messages)
{
messages.ForEach(x => x.Ipaddress = HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString());
//send to repository
await context.WatchedFiles.AddRangeAsync(messages);
await context.SaveChangesAsync();
return Ok();
}

I would make sure the JSON is actually a valid JSON.
Then you try to send it with Postman to your endpoint to see if you get the intended result.
That would at least help you eliminate some of the places where it could go wrong.

Related

Unable to deserialize JSON in C# using the same class at both ends

I'm looking for someone to point out the obvious blunder here.
A .NET Core in C# application makes an HTTP call to another such application. Some processing is performed, and a response is sent thus:
Response response = new Response(input)
{
stuff = processedStuff;
};
responseMessage = JsonConvert.SerializeObject(response);
return new OkObjectResult(responseMessage);
This all looks good and responseMessage contains valid JSON (according to an online JSON checker I found).
At the other end, this is received thus:
Response returned = new Response();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
returned = JsonConvert.DeserializeObject<Response>(json);
}
This fails with an Error converting value *the JSON string* to "Response" at line 1
Response is the same class file in both applications. What never obvious and apparently invisible mistake am I making here?
The invisible mistake you are making is double-serializing the result. The contract of OkObjectResult is that it will automatically serialize the result object to the negotiated content type (e.g. JSON or XML) and return an OK status. You are serializing the object first and then passing the serialized string to OkObjectResult so it ends up getting serialized twice.
responseMessage = JsonConvert.SerializeObject(response); // serialize to JSON
return new OkObjectResult(responseMessage); // implicit serialization here
Possible solutions:
Allow the implicit serialization to do its thing (recommended):
return new OkObjectResult(response); // implicit serialization of response object
Use a ContentResult instead (good if you need special serialization handling):
responseMessage = JsonConvert.SerializeObject(response); // serialize to JSON
return new ContentResult()
{
Content = responseMessage,
ContentType = "application/json",
StatusCode = 200
};
Deserialize twice on the receiving end (use as a last resort, i.e. you don't control the server):
var doubleSerializedJson = await response.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<string>(doubleSerializedJson);
returned = JsonConvert.DeserializeObject<Response>(json);
check if the returned string is not wrapped in OkObjectResult object.
As far as I know, you don't need the serialization and deserialization, the framework already takes care of everything. If you need to, you could always deserialize to either an anonymous type or cast it from object.
https://www.newtonsoft.com/json/help/html/DeserializeAnonymousType.htm
I would also help if you could share the response class, since it's most likely part of the problem.
As someone old enough to remember when the Simpsons started, I can only answer in the traditional manner:
D'oh!
Using implicit serialization as described by the kind responder above resolved the issue.

Using MultipartContent - It's working, but is it working right?

I've got a little app that generates an HttpMessage with Multipart content ...
using (var client = new HttpClient())
{
using (var content = new MultipartContent("mixed", "----123"))
{
content.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/mixed; boundary=----123");
// repeated calls to content.Add(...)
var result = client.PostAsync(url, content). Result;
Console.WriteLine(result);
}
}
and I have a little HttpServer that listens for POST calls and does this when it gets one...
var streamContent = new StreamContent(inputStream);
streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/mixed; boundary=----123");
var provider = streamContent.ReadAsMultipartAsync().Result;
foreach (var httpContent in provider.Contents)
{
var t = httpContent.Headers.ContentType;
var c = httpContent.ReadAsStringAsync().Result;
}
And it all works.
But if, in my receiver code, I do not include the line streamContent.Headers.ContentType... the receiver crashes on the var provider... line with the error Invalid 'HttpContent' instance provided. It does not have a content-type header value. 'HttpContent' instances must have a content-type header starting with 'multipart/'..
So, whilst I have code that works, it will only work if I know, in advance, what the boundary is going to be.
This can't be right.
I've looked through, and tried, dozens of permutations based on questions here in SO and elsewhere but I can't find anything that works without me setting the ContentType header in the receiver and, therefore, knowing what the boundary value is.
What should I be doing?
UPDATE
If I remove the boundary part of the ContentType header in the receiver, it still crashes, but with a different error...Invalid 'HttpContent' instance provided. It does not have a 'multipart' content-type header with a 'boundary' parameter.
I don't think your server is doing what you think it is doing. new StreamContent(Stream) is used when you create your own stream content with the intent to return it from controller action. And the stream you pass to it should contain the raw data (entity, response body) that will be returned. It doesn't try to interpret the data from the stream in any way. Even if you pass valid http stream in the parameter, it won't try to parse content-type headers from it - you have to supply it. And that's a big IF, you didn't show where you get the inputStream, it's not standard part of MVC.
Actual content you received from client is accessible in Request.Content, along with the proper headers like content-type or boundary. ReadAsMultipartAsync should work on that too, but I never tried that extension in practice.
As a side note, using Task.Result should be last resort. Make your controller action async and await that task.
Edit: for illustration, I think this would work and doesn't require knowing the boundary in advance. Still, it's very suboptimal solution when you can just call Request.Content.ReadAsMultipartAsync():
var streamContent = new StreamContent(inputStream);
streamContent.Headers.ContentType = Request.Content.Headers.ContentType;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
var provider = streamContent.ReadAsMultipartAsync().Result;
foreach (var httpContent in provider.Contents)
{
var t = httpContent.Headers.ContentType;
var c = httpContent.ReadAsStringAsync().Result;
}

How to consume Json string from external API? Json returns with escaped or extra characters.

I spent some time trying to figure this one out so I decided to post it here - hopefully it saves some time to someone else.
I'm building an ASP.Net Core Web API MVC application that accepts a Get request and makes a call to an external API (in this case is the Bing Image Search). When returning a result, it would give me a escaped Json string. Example:
"{\"_type\": \"Images\", \"instrumentation\": {\"pageLoadPingUrl\": \"https:...}
Instead of:
{
"_type": "Images",
"instrumentation": {
"pageLoadPingUrl": "https:....
}
Then, I wanted to pass it back to my web client with all sort of non-successes.
I will post shortly how I solved it.
Cheers!
So the issue was that I was trying to process the reponse content the wrong way. All I had to do is user the JsonConvert library.
My full API method looks like this:
[HttpGet("{id}")]
public async Task<IActionResult> Get(string id)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "Enter your key here");
var uri = new Uri("uri to external API here + any parameters");
var response = await client.GetStringAsync(uri);
var jsonResponse = JsonConvert.DeserializeObject(response);
return Ok(jsonResponse);
}
}
Cheers! :)

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.

WebClient - UploadValues : Get Status Response

I'am trying to pass values from a controller to another controller in another domain. I'am adding data to a NameValueCollection and pass it to another controller [httppost] method and receiving data there mapped to a Model same as i passed from.
Currently i'am running it locally by opening two instance of VS simultaneously. When the both VS is opened the values are passed correctly and the information is written to db correctly and i receive a response like "{byte[0]}". Now when i try stopping the destination controller Project and try to submit data then it wont work but still i get the same response as "{byte[0]}". Can somebody please help me how to return the response command in this scenario. Is there a way a understand the UploadValues are completed or not completed.
.........
.........
NameValueCollection resumeDetails = new NameValueCollection();
resumeDetails.Add("FirstName", "KRIZTE");
byte[] res = this.Post(ConfigurationManager.AppSettings["RedirectionUrl"].ToString(), resumeDetails);
return View("Index");
}
public byte[] Post(string uri, NameValueCollection resumeDetails)
{
byte[] response = null;
WebClient client = new WebClient();
response = client.UploadValues(uri, resumeDetails);
return response;
}
You should not use the WebClient because of problems like this.
Microsoft implemented HttpClient class as a newer API and it has these benefits:
HttpClient is the newer of the APIs and it has the benefits of
has a good async programming model
1- being worked on by Henrik F Nielson who is basically one of the inventors of HTTP, and he designed the API so it is easy for you to follow the HTTP standard, e.g. generating standards-compliant headers
2- is in the .Net framework 4.5, so it has some guaranteed level of support for the forseeable future
3- also has the xcopyable/portable-framework version of the library if you want to use it on other platforms - .Net 4.0, Windows Phone etc.
so I'm gonna show you an example of using HttpClient:
var uri = "http://google.com";
var client = new HttpClient();
try
{
var values = new List<KeyValuePair<string, string>>();
// add values to data for post
values.Add(new KeyValuePair<string, string>("FirstName", "KRITZTE"));
FormUrlEncodedContent content = new FormUrlEncodedContent(values);
// Post data
var result = await client.PostAsync(uri, content);
// Access content as stream which you can read into some string
Console.WriteLine(result.Content);
// Access the result status code
Console.WriteLine(result.StatusCode);
}
catch(AggregateException ex)
{
// get all possible exceptions which are thrown
foreach (var item in ex.Flatten().InnerExceptions)
{
Console.WriteLine(item.Message);
}
}

Categories

Resources