Web API json responds faulty json [duplicate] - c#

I am trying to fix an ASP.NET WebAPI method where a Json response is required. However it's returning a string instead.
Initially it was returing XML format, but I've added this line to the mvc code in App_Start\WebApiConfig.cs in order to return Json by default.
config.Formatters.Remove(config.Formatters.XmlFormatter);
We've updated the c# method as follows to use NewtonSoft:
public string Get()
{
string userid = UrlUtil.getParam(this, "userid", "");
string pwd = UrlUtil.getParam(this, "pwd", "");
string resp = DynAggrClientAPI.openSession(userid, pwd);
JsonSerializer ser = new JsonSerializer();
string jsonresp = JsonConvert.SerializeObject(resp);
return resp;
}
The resp var is coming back as a string type:
"{status:\"SUCCESS\",data:[\"4eb97d2c6729df98206cf214874ac1757649839fe4e24c51d21d\"]}"
and jsonresp var looks like this :
"\"{status:\\\"SUCCESS\\\",data:[\\\"4eb97d2c6729df98206cf214874ac1757649839fe4e24c51d21d\\\"]}\""
and in Chrome's F12 dev tools, the data object is :
""{status:\"SUCCESS\",data:[\"4eb97d2c6729df98206cf214874ac1757649839fe4e24c51d21d\"]}""
and in Console tools, the result of angular.fromJson(data) :
"{status:"SUCCESS",data:["4eb97d2c6729df98206cf214874ac1757649839fe4e24c51d21d"]}"
I would appreciate some advice on how to properly return the Json object, and NOT in any string type.
UPDATE
By intercepting the resp var, and using Mr. Chu's suggestion below, I can successfully achieve a nice clean Json object on the client.
The key is that resp needs to contains double quotes around both key:value pairs:
public HttpResponseMessage Get()
{
string userid = UrlUtil.getParam(this, "userid", "");
string pwd = UrlUtil.getParam(this, "pwd", "");
string resp = DynAggrClientAPI.openSession(userid, pwd);
resp = "{\"status\":\"SUCCESS\",\"data\":[\"194f66366a6dee8738428bf1d730691a9babb77920ec9dfa06cf\"]}"; // TEST !!!!!
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(resp, System.Text.Encoding.UTF8, "application/json");
return response;
}
in Chrome console, the response is :
Object {status: "SUCCESS", data: Array[1]}
data: Array[1]
status: "SUCCESS"
__proto__: Object

resp is already a JSON string, but it is not valid JSON (the keys are not wrapped in quotes ("). If it is returned to angular, the JavaScript JSON.parse() method is unable to deserialize it. However, you can use JSON.NET in deserialize it to a JObject and serialize it again into valid JSON and create your own HttpResponseMessage...
public HttpResponseMessage Get()
{
string userid = UrlUtil.getParam(this, "userid", "");
string pwd = UrlUtil.getParam(this, "pwd", "" );
string resp = DynAggrClientAPI.openSession(userid, pwd);
var jObject = JObject.Parse(resp);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(jObject.ToString(), Encoding.UTF8, "application/json");
return response;
}
Or you can just return the JObject and have Web API serialize it for you...
public JObject Get()
{
string userid = UrlUtil.getParam(this, "userid", "");
string pwd = UrlUtil.getParam(this, "pwd", "" );
string resp = DynAggrClientAPI.openSession(userid, pwd);
var jObject = JObject.Parse(resp);
return jObject;
}
In either case, the Web API call should return this JSON, which is now valid...
{
"status": "SUCCESS",
"data": [
"4eb97d2c6729df98206cf214874ac1757649839fe4e24c51d21d"
]
}
In the angular code, you'd have to dig out the session id which is stored in an array called data...
userService.openUserSession(rzEnvJson).then(function (response) {
var sessionResponse = response.data; // or simply response, depending if this is a promise returned from $http
$rootScope.rgSessionVars.sessionID = sessionResponse.data[0];
});

The key to what is going on here is in the comment made by Mike Cheel; serialization is happening twice, once in the OP's code and once by Asp.Net WebAPI. That is why a string is returned instead of a Json object.
I was encountering the exact same thing. Here is a hello world example showing the problem. I first did something like this:
[Route("getall")]
public string GetAllItems()
{
var result = new
{
x = "hello",
y = "world"
};
return JsonConvert.SerializeObject(result);
}
I then tried to so something like this, thinking that I needed to return IHttpActionResult to resolve this:
[Route("getall")]
public IHttpActionResult GetAllItems()
{
var result = new
{
x = "hello",
y = "world"
};
return Ok(JsonConvert.SerializeObject(result));
}
Both these controller actions gave me a string rather than the Json object that I was wanting; so I go this:
"{\"x\":\"hello\",\"y\":\"world\"}"
Finally I saw the comment by Mike and realized that I needed to return the .Net object directly and just let WebAPI handle the serialization. So instead of returning this:
return Ok(JsonConvert.SerializeObject(result));
return this:
return Ok(result);
Then I got the result that I was expecting:
{"x":"hello","y":"world"}

I don't see what this has to do with AngularJS, but your problem is simple. Your data object is JSON encoded. So you could almost certainly access data.JsonRequestBehavior and it would be 1. But your Data field inside it is AGAIN JSON-encoded. You need to decode it before trying to use it - it's just a string when you get to this callback:
var myData = angular.fromJson(data.Data);
console.log(myData.data);
Note that your data.Data object is itself another wrapper - an array. You almost certainly want myData.data[0] to go into that sessionID field...

These other solutions weren't working for me, but I was able to get something like this to work:
[HttpGet]
public async Task Get(CancellationToken cancellationToken)
{
string userid = UrlUtil.getParam(this, "userid", "");
string pwd = UrlUtil.getParam(this, "pwd", "");
await using (Stream contentStream = await DynAggrClientAPI.openSessionStreamAsync(userid, pwd, cancellationToken))
{
Response.StatusCode = (int)HttpStatusCode.OK;
Response.ContentType = "application/json";
await contentStream.CopyToAsync(Response.Body, cancellationToken);
}
}

I had a similar problem (raw json in string, like resp) and this worked fine for me in .Net 6. I haven't checked previous versions.
[HttpGet]
public IActionResult Get()
{
string userid = UrlUtil.getParam(this, "userid", "");
string pwd = UrlUtil.getParam(this, "pwd", "" );
string resp = DynAggrClientAPI.openSession(userid, pwd);
return Content(resp, "application/json"):
}

Related

Unexpected character encountered while parsing value line 1, position 1

I'm building .Net Core 3.1 Web API. To send requests to Web API. I'm using System.Net.Http library. I put JSON into POST request body. This is Web API side handler:
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class MyController : ControllerBase
{
[HttpPost]
public int HandleRequest([FromBody] string jsonString)
{
return 1;
}
}
The code below sends empty ArrayList to Web API but it always returns "Unexpected character encountered while parsing value line 1, position 1.".
var serializedData = new JavaScriptSerializer().Serialize(new ArrayList());
var content = new StringContent(serializedData, Encoding.UTF8, "application/json");
var response = client.PostAsync(url, content);
return response.Result.Content.ReadAsStringAsync();
I've tried to use JsonConvert and played around the object to serialize but it always returns the same error. But then I've found that if I make serialization twice then it starts working. So code below works - sends the request without any errors and the data on Web API side is correct.
var serializer = new JavaScriptSerializer();
var serializedData = serializer.Serialize(serializer.Serialize(new ArrayList()));
var content = new StringContent(serializedData, Encoding.UTF8, "application/json");
var response = client.PostAsync(url, content);
return response.Result.Content.ReadAsStringAsync();
Why do I need to do double serialization to make it work?
First, when you are using async methods you should add await keyword:
var response = await client.PostAsync(url, content);
return await response.Result.Content.ReadAsStringAsync();
Secondly, you are sending an array, so why not just try to send it and get as object not string:
var updateData = new ArrayList();
var jsonObject = JsonConvert.SerializeObject(updateData);
var content = new StringContent(jsonObject, Encoding.UTF8, "application/json");
var res = await apiClient.PutAsync(url, content);
And then in WebAPI:
[HttpPost]
public int HandleRequest([FromBody] ArrayList list)
{
return 1;
}
var serializedData = new JavaScriptSerializer().Serialize(new ArrayList());
Console.WriteLine(serializedData);
// Output: []
var serializer = new JavaScriptSerializer();
var serializedData = serializer.Serialize(serializer.Serialize(new ArrayList()));
Console.WriteLine(serializedData);
// Output: "[]"
You have an error because your endpoint expects string but in first serialization you are sending an array.
Second serialization works, because first you serialize ArrayList to string and then that string to string. In the end you by accident send a serialized string and endpoint can deserialize it.
Error "Unexpected character encountered while parsing value line 1, position 1." means that server receives at first character [ but it expects " because it tries deserialize JSON to string - but it was array.

Error converting JSON response from backend to type with JsonConvert.DeserializeObject

So I am having this weird problem with deserializing a response from my BackEnd, The request works fine and the BackEnd succesfully responds with a result.
This is the error I get:
'Error converting value "{"Succes":true,"AuthKey":"$2a$13..."}" to type 'FrontEnd.LoginUserResponse'. Path '', line 1, position 96.'
The code I am using to make the HTTP call and deserialize the string:
public async Task<bool> loginUser(LoginUserData login)
{
HttpContent httpContent = new StringContent(JsonConvert.SerializeObject(login), Encoding.UTF8);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage responseMessage = await httpClient.PostAsync("http://ip/webBackEnd/api/user/login", httpContent);
string response2 = responseMessage.Content.ReadAsStringAsync().Result;
LoginUserResponse response = JsonConvert.DeserializeObject<LoginUserResponse>(response2);
if (response.Succes)
{
return true;
}
else { return false; }
}
I tried making a response2 to check the value and I have noticed it does something weird with 3 backslashes. This might be the reason why this is occuring.
This is response2 that visual studio shows when I click the magnifying glass:
"{\"Succes\":true,\"AuthKey\":\"$2a$11$tQCw4zGGd2J2fXAxAN68Ruu3xheTuMKq4EHbeLtc9DAa2rgzJe8bS\"}"
When I hover on visual studio:
https://imgur.com/a/jUyLz6d
This is the Class that it is converting to
public class LoginUserResponse
{
[JsonProperty("succes")]
public bool succes { get; set; }
[JsonProperty("authkey")]
public string authkey { get; set; }
}
The Backend code:
[HttpPost]
[Route("login")]
public string Login([FromBody]LogInData logInData)
{
IReadUser.LogInRequest request = new IReadUser.LogInRequest(logInData);
IReadUser.LogInResponse backResponse = readUser.logIn(request);
LogInResponse response = new LogInResponse();
response.succes = backResponse.Succes;
response.authkey = backResponse.AuthKey;
return JsonConvert.SerializeObject(response);
}
EDIT // SOLUTION
Ok so, the front-end was fine, it was my backend code sending a double serialised string. I used
return JsonConvert.SerializeObject(response);
When I also could have used
return response;
So if you every get an error like this, it's probably the backend doubling up on the serialization.
Thanks for all the help!
Couple of things:
1. you should await responseMessage.Content.ReadAsStringAsync():
public async Task<bool> loginUser(LoginUserData login)
{
var httpContent = new StringContent(JsonConvert.SerializeObject(login), Encoding.UTF8);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var responseMessage = await httpClient.PostAsync("http://ip/webBackEnd/api/user/login", httpContent);
var response2 = await responseMessage.Content.ReadAsStringAsync();
var response = JsonConvert.DeserializeObject<LoginUserResponse>(response2);
return response.Succes
}
And 2. Based on your image it looks like the response from your backing service is being serialized twice.
1:
"{\"Succes\":true,\"AuthKey\":\"$2a$11$tQCw4zGGd2J2fXAxAN68Ruu3xheTuMKq4EHbeLtc9DAa2rgzJe8bS\"}"
2:
"\"{\\\"Succes\\\":true,\\\"AuthKey\\\":\\\"$2a$11$tQCw4zGGd2J2fXAxAN68Ruu3xheTuMKq4EHbeLtc9DAa2rgzJe8bS\\\"}\""
now to deserialize you have to do it twice
var s = JsonConvert.DeserializeObject<string>(response2);
var response = JsonConvert.DeserializeObject<LoginUserResponse>(s);
Probably best to fix the service if that's actually what is happening

JsonDeserializer check before try to convert Model

Read simple api call code bellow. Here i am getting call response in- IRestResponse response and its a json response. Then using JsonDeserializer() i am trying to convert it to a C# Model which is WallMartData model. ( i think i dont need to share model code here bcoz it doesn't matter for this question ). Now from this same response sometime i will get a json response which match with my model WallMartData and some time it will return other json response. Now my question is- before i try to convert my json response to WallMartData Model i want to check if this is a valid convertable json. If its not valid convartable for this WallMartData model then it will skip try to convert. Bcoz when its fails to convert i am getting invalid json exception on c#. Thats why i need to check before try to convert. Any solution?
string url = "http://api.example.com/v1/items?apiKey=" + Token + "&upc=" + UPC;
var client = new RestClient(url);
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
var deserializer = new JsonDeserializer();
var wmr = deserializer.Deserialize<WallMartData>(response);
You can try to create a method use try .... catch to check the JSON string is or isn't valid.
private static bool IsValidJson<T>(string strInput,out T obj)
{
obj = default(T);
try
{
obj = JsonConvert.DeserializeObject<T>(strInput);
return true;
}
catch (JsonReaderException jex)
{
return false;
}
catch (Exception ex)
{
return false;
}
}
Then you can use bool to check the DeserializeObject whether success.
WallMartData wmr;
if(IsValidJson<WallMartData>(response,out wmr)){
//... your logic with wmr
}
I think you can just use try catch block and it will be enough. But if you really need to validate your JSON, you can use JSON Schema. You can generate schema for your class using JsonSchemaGenerator
I suggest using JsonSchema by Json.Net
more info here
let's say that your WallMartData class looks like this
public class WallMartData
{
[JsonProperty("email", Required = Required.Always)]
public string Email;
[JsonProperty("first_name")]
public string firstName;
[JsonProperty("last_name")]
public string lastName;
}
Then you can easily use the schema checker
JSchemaGenerator generator = new JSchemaGenerator();
JSchema schema = generator.Generate(typeof(WallMartData));
string json = #"...";
JObject wallMartData = JObject.Parse(json);
if(wallMartData.IsValid(schema))
{
//if json matching the schema aka the class account
}
else
{
//the json is invalid
}

json string not being parsed correctly

I'm working with a Rest Api. I send the username and password when the user clicks on login from an MVC App but I keep getting back error
400 Bad Request
Now I've debugged through the code and realized the string being parsed to the api is as follows
"{\"username\":\"email#somedomain.com\",\"password\":\"mypassword\"}"
So I decided to remove the \ from the string as follows:
string jsonString = JsonString(model.Email, model.Password);
string data = jsonString.Replace(#"\", "");
But for whatever reason the back slashes are not being removed :/ The string should be parsed as:
{"username":"email#somedomain.com","password":"mypassword"}
Here is the full code:
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("myurl");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
string jsonString = JsonString(model.Email, model.Password);
string data = jsonString.Replace(#"\", "");
HttpContent content = new StringContent(data, Encoding.UTF8, "application/json");
HttpResponseMessage messge = client.PostAsync("api/Account/Login", content).Result;
if (messge.IsSuccessStatusCode)
{
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
And the JsonString method is as follows:
private string UserString(string us, string ps)
{
var json = new JavaScriptSerializer().Serialize(
new
{
username = us,
password = ps
});
return json;
}
api/Account/Login method looks like this
[AllowAnonymous]
[Route("Login")]
public async Task<IHttpActionResult> Login(UserModel userModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityUser result = await _repo.FindUser(userModel.UserName, userModel.Password);
return Ok();
}
And find FindUser method looks like this
public async Task<IdentityUser> FindUser(string userName, string password)
{
IdentityUser user = await _userManager.FindAsync(userName, password);
return user;
}
You cannot remove \ characters. It is escape character for special characters that cannot be normal part of strings, in this case " is character for begining and end of strings. When you need to use " inside string you have to insert \ before that. So that the result is \". See c# specs here. The problem causing 400 is probably somewhere else. Can you provide your api/Account/Login method?
you can probably strip out a lot of code here.
using (var httpClient = new HttpClient())
{
httpClient.PostAsJsonAsync("<your full url>", new UserModel { Email = "whatever", Password = "password" });
}
PostAsJsonAsync will handle serializing the model, and setting up the http call.
PostAsJsonAsync is part of the Web Api Client Nuget package.
I would also set a breakpoint in the Login method to find out exactly what model error is the Login method.

C# JSON POST connect (Method not allowed)

I have problem with connect to JSON server.
In user-manual:
The Interface is implemented as a standard HTTP Service. Using the service requires an authentication through the “Login” method. A Session Id is returned on success which has to be passed on every function call unless otherwise stated.
The expected data format when sending or receiving data is JSON.
All data must be passed using POST.
The session Id is of type Guid
Example:
Login
Description: Used to authenticate a user.
Url: /Login
Signature: Guid Login(string id, string username, string password)
END OF MANUAL
I wrote this code:
var webAddr = "https://xxx/Login";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
httpWebRequest.ContentType = "application/json; charset=utf-8";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "{\"Login\":[{"
+ "\"id\" : 1213213,"
+ "\"username\" : asdasdasd,"
+ "\"password\" : \"adasdsadasd\","
+ "}]}";
streamWriter.Write(json);
streamWriter.Flush();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
MessageBox.Show(result.ToString());
}
And message is: Method not allowed.
How can I send and recive data from this serwer?
You are formatting your JSON incorrectly. The JSON should look like this:
{"id":"1213213","username":"asdasdasd","password":"adasdsadasd"}
Notice: quotes around each name and value. And "Login" should not be part of the JSON.
However, the problem is really that you are doing this all manually. Instead, let .NET format the JSON for you and handle the HTTP request. To do this, create a structure for the arguments:
class Login
{
public string id { get; set; }
public string username { get; set; }
public string password { get; set; }
}
Use NuGet to add references to "Json.Net" and "Microsoft ASP.NET Web API Client Libraries." Now you can write this:
static async Task Login()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://www.censored.de/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.Timeout = TimeSpan.FromSeconds(30);
Login l = new Login() { id = "12345", password = "abcde", username = "safsdfasdf" };
var sTemp = JsonConvert.SerializeObject(l); // DEBUG: Just so I can see the JSON
var response = await client.PostAsJsonAsync("/censored/Service.svc/Login", l);
Guid g;
if (response.IsSuccessStatusCode)
{
g = await response.Content.ReadAsAsync<Guid>(); // This gives you the GUID
}
//DEBUG:
// var rawResponse = await response.Content.ReadAsStringAsync();
// Console.WriteLine(response);
}
}
Notice that I used "async" and "await" keywords. If you are not familiar with calling an async function, you can change the "await" line to this temporarily:
var response = await client.PostAsJsonAsync("/censored/Service.svc/Login", l).Result;
This results in a 400 Bad Request with this message.
{"ErrorMessage":"Die Anmeldedaten sind ungültig.","StackTrace":null}
Which Google tells me means that the credentials are wrong. I assume that is the response one would expect with this user/password combination.

Categories

Resources