Deserialize HttpError ModelState - asp.net-web-api

Wit the latest webapi bits I now have
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
My client is an MVC website that reads the value from the response. I finally got it to read the HttpError object from the response, but loading the ModelState is... not intuitive to say the least.
Is there a cleaner way to write this?
var httpError = response.Read<HttpError>();
var errors = httpError["ModelState"] as JObject;
foreach (var error in errors)
foreach (var message in error.Value.Values<string>())
{
modelState.AddModelError(error.Key, message);
}

While an error response, as you've identified, can readily be deserialised into an HttpError, the ModelState value within it only ends up as a JObject.
You've probably already tried something like:
var errors = ((JObject)errorMsg["ModelState"])
.ToObject<ModelStateDictionary>();
Or maybe:
var errors = ((JObject)errorMsg["ModelState"])
.ToObject<Dictionary<string, ICollection<ModelError>>>();
And found that it won't convert. The best alternative I could find was:
var errors = ((JObject)errorMsg["ModelState"])
.ToObject<Dictionary<string, IList<string>>>();
Which makes your iteration marginally neater:
foreach (var err in errors)
foreach (var msg in err.Value)
{
modelStateDictionary.AddModelError(err.Key, msg);
}
The problem, as I see it, is a string won't deserialise to a ModelError instance, and is further compounded by ModelErrorCollection unfortunately hiding its base class constructor of Collection(IList list).

How are you making the API calls? Client-side? If so, I noticed Brad Wilson using the below format for receiving AJAX responses via jQuery in a recent demo. This may not be what you're looking for but it's been working well for me:
$.ajax({
url: "/apicontorller/action/",
contentType: "application/json",
type: "POST",
data: jsonData,
statusCode: {
200: function (data) {
},
404: function () {
alert('404');
},
400: function () {
alert('invalid');
},
500: function () {
alert('error');
},
// ...
}
});
The nice thing about this is you can handle the actual HTTP status codes. In my experience, error messages/data can be accessed by providing a parameter to the predicate function. I'm still learning about REST and the WebAPI platform so if anyone can provide a better implementation, I certainly welcome it!

Related

How can I save a response of HTTP GET Request in variable?

Here is my API code
#GetMapping("/api/test")
public String test() {
return "hello";
}
Then I will send request to this API by using ajax.
function test() {
$.ajax({
type: "GET",
url: "/api/test",
success: function(response) {
}
})
}
I want to save the value of ajax call response (In this case "hello") in variable. Please let me know how to do it.
Several ways of doing it !
As far as only a String message is concerned you can just store it like below
`var myResponse=response;`
Alternatively, you can also access values if response is an object (JSON reponse I mean)
for e.g. `var resp=response.message; //Assuming message is a key in response JSON`
For testing, use alert(response) or console.log(response) to see how your response is coming and manipulate accordingly !
Feel free to refer this link for detailed Ajax walkthrough https://www.tutorialspoint.com/jquery/jquery-ajax.htm
Hope these helps!

Why is asp.net webAPI always returning text/html?

I would like to create webservices returning json. However, I'm always getting 'text/html' as the responses content type.
First shot:
public StringContent Get()
{
List<Cell> list = new List<Cell>();
Cell c = new Cell("Cell1");
Cell c2 = new Cell("Cell2");
list.Add(c);
list.Add(c2);
return new StringContent(
Newtonsoft.Json.JsonConvert.SerializeObject(list),
Encoding.UTF8,
"application/json");
}
Responsecontent: System.Net.Http.StringContent
second shot:
public List<Cell> Get()
{
Cell c = new Models.Cell("Cell1");
List<Cell> list = new List<Cell>();
list.Add(c);
return list;
}
Responsecontent: System.Collections.Generic.List`1[TestApp.Models.Cell]
This is how I access the endpoint:
$.ajax({
url: "http://localhost:54787/Cell/Get",
type: "GET",
contentType:"application/json",
accepts: {
text: "application/json"
},
success: function (response) {
$("#result").html(JSON.parse(response));
},
error: function (xhr, status) {
alert("error");
}
});
If you have no good reason to do serialization manually, you should use Web API default mechanism by returning object instead of StringContent. For example, you can change your method to return List<Cell> directly.
public List<Cell> Get()
{
// return List<Cell> just like you write a typical method
}
This way, you will not get text/html anymore. However, you will still get XML in Chrome. It is because Chrome's default HTTP Accept header contains application/xml, and it is supported by default in Web API. If you have no need to support XML result, so you can remove it by the following code during startup (maybe in Global.asax)
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
PS: If you don't know whether you need XML or not, then you don't need it.

Asynchronously populate Sammy.Storage cache

I'm having difficulty accessing requestJSON on a jQuery $.ajax object outside of the success callback. If I do:
var ajax_request = $.ajax({
dataType: 'json',
contentType: 'application/json'
});
console.log(ajax_request.responseJSON);
// this results in `undefined`
How can I access the responseJSON without adding a .success() callback? If I inspect ajax_request in Firebug, I can see the responseJSON property, and the data I expect, but I can't access it via:
ajax_request.responseJSON
More specifically, I'm building an SPA using Sammy and Knockout. In some routes, I need to be able to get JSON from cache, and if it doesn't exist, get the value from a service call and then set it into cache:
var cached_json = storage.fetch('cached_json', function() {
// make service call
return $.getJSON(url);
});
event_context.render('template.tpl', {'json': cached_json}).appendTo('#my-target');
But, of course, calling storage.fetch doesn't cause the rest of the code to pause until $.getJSON is complete. This is the part I can't quite figure out how to structure.
here's how i would implement it
responseJSON = "";
$.get("myurl.php",function(jdata){
responseJSON = jdata;
},"json");
i like to see the ajax method at a glace, but in your case you can do the same by
....
success : function(jdata){ responseJSON = jdata; }
....
PS: i believe that initializing the blank responseJSON is not required since any variable without var is in global scope, but it would help for clarity
I ended up solving this by creating a deferred object that gets or creates the value I need:
function get_or_create_cache(storage, key, service_endpoint) {
return $.Deferred(function(deferred) {
var c = storage.get(key);
if (c === null || c === undefined) {
$.when(jsonp_service_request(service_endpoint)).done(function(json) {
storage.set(key, json);
deferred.resolve(json);
});
}
else {
deferred.resolve(c);
}
}).promise();
}
In this function, storage refers to a Sammy.Storage instance. jsonp_service_request is a local function that returns a jsonp response, taking into account the location.hostname for local development, where I'm pointing to local.json files, or a remote environment, where I'm calling into an actual API. jsonp_service_request returns an $.ajax function.
Then in my Sammy route, I can do:
this.get('#/', function(event_context) {
$.when(get_or_create_cache(storage, 'my-cache-key', 'service-endpoint'))
.then(function(json) {
event_context.render('my-template.template', {'value-name': json})
.appendTo('#my-target');
});
});

jQuery Ajax - Cant parse json?

I got a very strange problem, I thought this worked before but it doesn't any more. I dont even remember changing anything. I tried with an older jQuery library.
I got an error that says: http://i.imgur.com/H51wG4G.png on row 68: (anonymous function). which refer to row 68:
var jsondata = $.parseJSON(data);
This is my ajax function
I can't get my alert to work either because of this error. this script by the way is for logging in, so if I refresh my website I will be logged in, so that work. I also return my json object good as you can see in the image. {"success":false,"msg":"Fel anv\u00e4ndarnamn eller l\u00f6senord.","redirect":""}
When I got this, I will check in login.success if I got success == true and get the login panel from logged-in.php.
$('#login_form').submit(function()
{
var login = $.ajax(
{
url: '/dev/ajax/trylogin.php',
data: $(this).serialize(),
type: 'POST',
}, 'json');
login.success(function(data)
{
var jsondata = $.parseJSON(data);
console.log(jsondata);
if(jsondata.success == true)
{
$.get("/dev/class/UI/logged-in.php", function(data) {
$(".login-form").replaceWith(data);
});
}
else
{
alert(jsondata.msg);
$('#pwd').val('');
}
});
return false;
});
Thank you.
If the response you have showed in the attached screenshot is something to go by, you have a problem in your PHP script that's generating the JSON response. Make sure that thePHP script that's generating this response (or any other script included in that file) is not using a constant named SITE_TITLE. If any of those PHP files need to use that constant, make sure that that SITE_TILE is defined somewhere and included in those files.
What might have happened is that one of the PHP files involved in the JSON response generation might have changed somehow and started using the SITE_TITLE costant without defining it first, or without including the file that contains that constant.
Or, maybe none of the files involved in the JSON generation have changed, but rather, your error_reporting settings might have changed and now that PHP interpreter is outputting the notice level texts when it sees some undefined constant.
Solving the problem
If the SITE_TITLE constant is undefined, define it.
If the SITE_TITLE constant is defined in some other file, include that file in the PHP script that's generating the response.
Otherwise, and I am not recommending this, set up your error_reporting settings to ignore the Notice.
Your response is not a valid JSON. You see: "unexpected token <".
It means that your response contains an unexpected "<" and it cannot be converted into JSON format.
Put a console.log(data) before converting it into JSON.
You shoud use login.done() , not login.success() :)
Success is used inside the ajax() funciton only! The success object function is deprecated, you can set success only as Ajax() param!
And there is no need to Parse the data because its in Json format already!
jQuery Ajax
$('#login_form').submit(function()
{
var login = $.ajax(
{
url: '/dev/ajax/trylogin.php',
data: $(this).serialize(),
type: 'POST',
}, 'json');
login.done(function(data)
{
var jsondata = data;
console.log(jsondata);
if(jsondata.success == true)
{
$.get("/dev/class/UI/logged-in.php", function(data) {
$(".login-form").replaceWith(data);
});
}
else
{
alert(jsondata.msg);
$('#pwd').val('');
}
});
return false;
});

KnockoutJS with IE8, occasional problems with Stringify?

A number of our users are still on IE8. Some of them occasionally are reporting problems when trying to post data to our servers (via a big button labeled "SAVE").
There is a script error that IE8 shows, which is: Unexpected call to method or property access, always pointing to the same line in the KnockoutJS 2.2.0 (debug, for now) library, line 450, which is as follows:
return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
The method in my code that is at the root of the stack trace where this happens is this:
self.saveSingle = function (onSuccess, onFailure) {
ko.utils.arrayForEach(self.days(), function (day) {
day.close();
});
var jsonData = ko.toJSON(self);
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: applicationLocation + "/api/assignmentapi/save",
data: jsonData,
success: function (data) {
self.status(data.Status);
self._isDirty(false);
ko.utils.arrayForEach(self.days(), function (day) {
day.clean();
});
if (onSuccess)
onSuccess();
},
error: function (data) {
onFailure();
},
dataType: "json"
});
};
We do strip out a number of properties that are not necessary to our POST as we convert the object to JSON, using this approach: http://www.knockmeout.net/2011/04/controlling-how-object-is-converted-to.html
OurType.prototype.toJSON = function () {
var copy = ko.toJS(this);
delete copy.someUnneededProperty1;
delete copy.someUnneededProperty2;
delete copy.someUnneededProperty3;
delete copy.someUnneededProperty4;
return copy;
}
When it fails, it fails consistently on the line
var jsonData = ko.toJSON(self);
Now here comes the real mess:
It's not consistently happening
It doesn't happen to all IE8 users
We can't consistently reproduce it
The structure of our model that we're serializing doesn't appear matter
The jscript.dll is the current version for IE8
I was also experiencing this issue. Digging deeper I found a few things:
It was only failing occasionally, I found this by running the code in the console
The code in the data-bind was trowing an exception except the message was being swallowed due to IE8 gobbling up the message when using a try {} finally {} block (without catch).
Removing the try finally revealed a cannot parse bindings message.
When I started to get close to figuring out the issue (digging deep into the knockout code) it seemed to disappear in front of my eyes. This is the section of code it was failing on, catching the exception at the end of the code:
ko.utils.extend(ko.bindingProvider.prototype, {
'nodeHasBindings': function(node) {
switch (node.nodeType) {
case 1: return node.getAttribute(defaultBindingAttributeName) != null; // Element
case 8: return ko.virtualElements.virtualNodeBindingValue(node) != null; // Comment node
default: return false;
}
},
'getBindings': function(node, bindingContext) {
var bindingsString = this['getBindingsString'](node, bindingContext);
return bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
},
// The following function is only used internally by this default provider.
// It's not part of the interface definition for a general binding provider.
'getBindingsString': function(node, bindingContext) {
switch (node.nodeType) {
case 1: return node.getAttribute(defaultBindingAttributeName); // Element
case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
default: return null;
}
},
// The following function is only used internally by this default provider.
// It's not part of the interface definition for a general binding provider.
'parseBindingsString': function(bindingsString, bindingContext, node) {
try {
var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache);
return bindingFunction(bindingContext, node);
} catch (ex) {
throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString);
}
}
});
But yea, it stopped becoming reproducible so I came up with a hack that I tested and works earlier, just retrying the data parsing. So this:
data-bind="value: ko.computed(function(){return ko.toJSON(appViewModel.model()[0])})"
Became this:
data-bind="value: ko.computed(function(){while (true) { try { var json = ko.toJSON(appViewModel.model()[0]); return json; }catch(e){}}})"
Yes, it's very yucky, but it seems to do the trick until our users no longer need IE8 or the Knockout issue is fixed.
I have no idea if this will fix it, but you can use the mapping plugin to go between JS and JSON:
var mapping = {
'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}
var viewModel = ko.mapping.toJS(data, mapping);
Taken from my answer to this question
I'd give this a try and see if it helps, as there's nothing obviously wrong in your approach.
Are you sure it's IE8 users who are hitting the issue? IE7 does not support JSON.stringify. You'll need to include the json2.js library to support IE7 and lower.

Resources