deserialize httpclient's content result yields intermittend null values - json

I have a async method in a pcl:
public async Task<T> Get<T>(string method) where T : EntityBase
{
try
{
Log.Info($"RequestURL {_client.BaseAddress}{method}");
var response = await _client.GetAsync(method);
var content = await response.Content.ReadAsStringAsync();
if (response.StatusCode != HttpStatusCode.OK)
{
return HandleResponse<T>(response); //this method just handles failures and logs the info in aspecial way
}
Log.Info($"Response {content}");
return JsonConvert.DeserializeObject<T>(content);
}
catch (Exception ex)
{
Log.Error($"Making api request to {method}. --- {ex.ToString()}");
return default(T);
}
}
my handleresponse method:
private T HandleResponse<T>(HttpResponseMessage responseMessage) where T : EntityBase
{
var resultType = RequestResultType.Success;
var message = "OK";
switch (responseMessage.StatusCode)
{
case HttpStatusCode.Unauthorized:
resultType = RequestResultType.Failure;
message = "Unauthorised";
break;
}
Log.Info($"StatusCode: {responseMessage.StatusCode}");
Log.Info($"ResultType: {resultType}");
Log.Info($"Message: {message}");
return (T)Activator.CreateInstance(typeof(T), resultType, message);
}
The object represented in T above:
public class TaskItemList: EntitBase
{
public List<TaskItem> Tasks { get; set; }
}
The problem I have, is that this same method, when called, randomly returns T as null. Sometimes T is partially populated, with some of the properties being null
When i call the API via the browser, there is always a value.
I suspect that the response content is not yet completely formed by the time json is trying to deserialize it, but how does one handle this?

Related

The type initializer for 'Newtonsoft.Json.Utilities.JavaScriptUtils' threw an exception

I am having problems deserialising JSON into its corresponding C# Object.
My server returns valid JSON: {"success":false,"message":"Please enter a valid Email Address."}
My server is using a Laravel project and code to return JSON is:
return response()->json(['success' => false, 'message' => 'Please enter a valid Email Address.']);
The error given is "The type initializer for 'Newtonsoft.Json.Utilities.JavaScriptUtils' threw an exception."
This happens with various versions of Newtonsoft.Json, including the latest (11.0.2) and going back as far as 7.0.1. Didn't have this error before but I have updated Xamarin Forms today (3.0.0.550146).
Exception info here: https://pastebin.com/GWPJdj6T
Line 81 is call to JsonConvert.DeserializeObject
public class RegisterResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("message")]
public string Messsage { get; set; }
}
public static async Task<string> RequestAccessCode(string email) {
try {
using (var client = new HttpClient()) {
var response = await client.PostAsync(baseAddress + requestAccess, new FormUrlEncodedContent(new[] {
new KeyValuePair<string, string>("email", email.Trim())
}));
var responseContent = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RegisterResponse>(responseContent);
return result.Messsage;
}
}
catch (Exception exception) {
return exception.FormatExceptionInfo(false);
}
}

org.json.JSONException: No value for name

What could be the reason of this error in the code below?
loginButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick (View v){
final String e_mail = e_mailEditText.getText().toString();
final String password = passwordEditText.getText().toString();
// Response received from the server
Response.Listener<String> responseListener = new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonResponse = new JSONObject(response);
boolean success = jsonResponse.getBoolean("success");
if (success) {
String name = jsonResponse.getString("name");
// int age = jsonResponse.getInt("age");
Intent intent = new Intent(login.this, Welcome.class);
intent.putExtra("name", name);
// intent.putExtra("age", age);
intent.putExtra("e_mail", e_mail);
login.this.startActivity(intent);
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(login.this);
builder.setMessage("Login Failed")
.setNegativeButton("Retry", null)
.create()
.show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};
LoginRequest loginRequest = new LoginRequest(e_mail, password, responseListener);
RequestQueue queue = Volley.newRequestQueue(login.this);
queue.add(loginRequest);
}
});
Check if you have the key first:
if (jsonObject.has("name")) {
String name = jsonObject.getString("name");
}
For others users which have the org.json.JSONException: No value for //your parameter.
In this case you should check if the name is empty.
For example using method jsonResponse.optString("name").
Live example:
if (success) {
String name = jsonResponse.optString("name"); //will get name value or return empty String
if (!name.equals("")) {
//Your code if name is exist
Intent intent = new Intent(login.this, Welcome.class);
intent.putExtra("name", name);
intent.putExtra("e_mail", e_mail);
login.this.startActivity(intent);
} else {
//Your code if the name is empty
}
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(login.this);
builder.setMessage("Login Failed")
.setNegativeButton("Retry", null)
.create()
.show();
}
Can't say for sure without knowing the context (or the line number of the exception), but my money would be on the call:
jsonResponse.getString("name")
Most likely, the JSON received from the server doesn't contain any name/value pairs with name name.

send custom error status code with JArray

I have webapi, that return JArray.
There is any way to send response with some status code that I pick (like 422, 4XX )?
//GET api/UserControl/GetUserName
public JArray GetUserName()
{
JArray json = new JArray();
try
{
string UserID= getUserID();
if (!string.IsNullOrEmpty(UserID) || UserID== "None Was found")
{
var result = JsonConvert.SerializeObject(donorRep.GetUserFullName(UserID), Formatting.None,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
try
{
json = JArray.Parse(result);
}
catch
{
json.Add(result.ToString());
}
}
else
{
json.Add("There was an issue while retrieving your UserID.");
}
}
catch (JsonSerializationException ex)
{
json.Add("There was an issue while retrieving your IDSID. Please contact support");
}
return json;
}
for example if there is an error here than in the UI :
json.Add("There was an issue while retrieving your UserID");
Wrap your array inside a JSON object, then add additional properties for the status code and any other "meta" information you might need. For example, you might try to make the JSON response look something like this:
{
"statusCode" : 422
"errorMessage" : "Error message, if any, goes here."
"results" : [
"item 1",
"item 2",
"etc.",
"you can also use objects here instead of strings if your data is more complex"
]
}
Then make your code something like this:
// GET api/UserControl/GetUserName
public JObject GetUserName()
{
JArray resultArray = new JArray();
int statusCode = 200; // success
string errorMessage = null;
try
{
string UserID= getUserID();
if (!string.IsNullOrEmpty(UserID))
{
var fullName = donorRep.GetUserFullName(UserID);
resultArray.Add(fullName);
}
else
{
statusCode = 421; // error
errorMessage = "There was an issue while retrieving your UserID.";
}
}
catch (Exception ex)
{
statusCode = 422; // error
errorMessage = "There was an issue while retrieving your IDSID. Please contact support.";
}
JObject response = new JObject();
response.Add("statusCode", statusCode);
response.Add("errorMessage", errorMessage);
response.Add("results", resultArray);
return response;
}
You'll have to adjust your client side code to be able to extract the parts of the response. If you're using jQuery, for example, you could get the data something like this:
$.get("/api/UserControl/GetUserName")
.done(function(data) {
var statusCode = data.statusCode;
if (statusCode == 200) {
var userName = data.results[0];
alert("Success! User name is " + userName);
}
else {
alert("Failed with code " + statusCode + ". message: " + data.errorMessage);
}
});
For sake of completeness I should also mention that in Web API you don't have to manually build the JSON using JObjects, JArrays, etc. As an alternative, you can use strongly-typed classes and return those from your methods directly, and Web API will serialize them to JSON for you. Of course the structure of the classes has to match the JSON you want to return. For example if you wanted to do that approach, you would define a class like this:
class ResponseData
{
public int statusCode { get; set; }
public string errorCode { get; set; }
public List<string> results { get; set; }
public ResponseData()
{
results = new List<string>();
}
}
Then you can do:
public ResponseData GetUserName()
{
ResponseData response = new ResponseData { statusCode = 200 };
try
{
string userID = getUserID();
if (!string.IsNullOrEmpty(UserID))
{
var fullName = donorRep.GetUserFullName(UserID);
response.results.Add(fullName);
}
else
{
response.statusCode = 421; // error
response.errorMessage = "There was an issue while retrieving your UserID.";
}
}
catch (Exception ex)
{
response.statusCode = 422; // error
response.errorMessage = "There was an issue while retrieving your IDSID. Please contact support.";
}
return response;
}

Webapi custom JsonMediaTypeFormatter

I am trying to create a custom JSONMediaTypeFormatter in which posts some json parameters to a webapi call. I need to encrypt the returned data from the webapi, hence writing a custom mediatypeformatter.
In my webapiconfig I clear all formatters and only add the custom formatter.
config.Formatters.Clear();
config.Formatters.Add(new CipherMediaFormatter());
In my custom media type formatter I add in the relevant headers and types but i still cant call my web api. It gives me an error
No MediaTypeFormatter is available to read an object of type 'Param' from content with media type 'application/json
The code for the mediatypeformatter
public class CipherMediaFormatter : JsonMediaTypeFormatter
{
private static Type _supportedType = typeof(object);
public CipherMediaFormatter()
{
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/octet-stream"));
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
}
public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
{
base.SetDefaultContentHeaders(type, headers, mediaType);
headers.ContentType = new MediaTypeHeaderValue("application/json");
}
public override bool CanReadType(Type type)
{
return true;
}
public override bool CanWriteType(Type type)
{
return true;
}
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
var taskSource = new TaskCompletionSource<object>();
try
{
var ms = new MemoryStream();
readStream.CopyTo(ms);
taskSource.SetResult(ms.ToArray());
}
catch (Exception e)
{
taskSource.SetException(e);
}
return taskSource.Task;
}
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
var taskSource = new TaskCompletionSource<object>();
try
{
if (value != null)
{
var jsonData = JsonConvert.SerializeObject(value);
ICryptographicService cgService = new CryptographicService();
string apiKey = string.Empty;
string pattern = #"api\/(.*)?\/(\d+)?";
var match = Regex.Match(HttpContext.Current.Request.RawUrl, pattern);
....
}
}
catch (Exception e)
{
taskSource.SetException(e);
}
return taskSource.Task;
}
}
The problem was ReadFromStreamAsync causing the mediatypeformatter to error.

Json deserialization Mvc 4 Knockout js

I'm having problems when I try to deserialize json into a object in MVC4.
I have a Viewmodel:
public class TestViewModel
{
public string Code { get; set; }
}
On the view I get the model and serialize the object using Json.net
var Vm = function(data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
self.GetResults = function() {
$.ajax({
type: "POST",
url: '#Url.Action("Action", "Controller")',
data: ko.mapping.toJSON(self),
success: function(data) {
alert('OK');
}
});
};
};
var viewModel = new Vm(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model)));
ko.applyBindings(viewModel);
My problem is when I call GetResults the action in the controller, all properties are null.
My Json is:
{"Code":"TestCode"}
I have the same structure in a MVC3 project and works fine. I'm missing something in MVC4?
Cheers!
We've noticed that in some scenarios jQuery will embed the data in a Form in the Request. When this happens, the values are not automatically mapped to the object type in the Controller method.
To get around this, you need to do two things:
1) Check to see if the data is serialized. I found an easy way to do this and dumped it in an extension method:
public static class WebContextExtensions
{
public static bool IsDataSerialized(this HttpContext context)
{
return context.Request.Params.AllKeys[0] == null;
}
}
2) IF IsDataSerialized returns true, you need to deserialize the data into your object type. We wrote a GenericDeserializer method to do that as well:
public class GenericContextDeserializer<T> where T : new()
{
public T DeserializeToType(HttpContext context)
{
T result = new T();
if (context != null)
{
try
{
string jsonString = context.Request.Form.GetValues(0)[0].ToString();
Newtonsoft.Json.JsonSerializer js = new Newtonsoft.Json.JsonSerializer();
result = js.Deserialize<T>(new Newtonsoft.Json.JsonTextReader(
new System.IO.StringReader(jsonString)));
}
catch (Exception ex)
{
throw ex;
}
}
else
throw new NullReferenceException();
return result;
}
}
Now put it all together in your Controller method:
[HttpPost]
[HttpOptions]
public HttpResponseMessage MyAction(JsonData data)
{
var results = Request.CreateResponse();
try
{
data = new GenericContextDeserializer<JsonData>().DeserializeToType(HttpContext.Current);
// do stuff with data
results.StatusCode = HttpStatusCode.OK;
}
catch (Exception ex)
{
results.StatusCode = HttpStatusCode.InternalServerError;
}
return results;
}
If you want more detail, it's in the second half of a blog post I wrote.