In my last SO question, I asked how to modify the serializer settings for Json.NET, which ASP.NET Web API natively uses for (de)serialization. The accepted answer worked perfectly, and I was, for example, able to embed type information into the serialized JSON string.
However, when I try to throw back this JSON string to a Web API action that's expecting the model's parent class, Web API still deserializes to the parent class, removes all data corresponding to the child class, and prevents casting to and detection of the child class.
class Entity { }
class Person : Entity { }
public Person Get() {
return new Person();
}
public bool Post(Entity entity) {
return entity is Person;
}
A simple use case would be doing something like this in jQuery:
// get a serialized JSON Person
$.ajax({
url : 'api/person' // PersonController
}).success(function (m) {
// then throw that Person right back via HTTP POST
$.ajax({
url : 'api/person',
type : 'POST',
data : m
}).success(function (m) {
console.log(m); // false
});
})
I'd expect that by modifying the JsonSerializerSettings of Json.NET to embed type information that it'd be able to read that and at the very least, try to force deserialization to that type, but apparently it does not.
How should I tackle something like this?
Web API really doesn't do any (de)serialization "natively". It happens to have a few MediaTypeFormatters included in the config.Formatters collection by default. Feel free to remove those and create your own MediaTypeFormatter that handles the serialization the way you want it to be done.
MediaTypeFormatters are really not that hard to create.
Actually the 2nd POST call is sending application/x-www-form-urlencoded data and that's why the type information is not picking up by the JsonMediaTypeFormatter. Try setting the contentType to be "application/json".
Also, the data in the 2nd POST request body seems to be encoded and it needs to be decoded before sending back to the service.
I was able to get this to work:
// get a serialized JSON Person
$.ajax({
url: 'api/person' // PersonController
}).success(function (m) {
// then throw that Person right back via HTTP POST
$.ajax({
url: 'api/person',
type: 'POST',
contentType: "application/json",
data: JSON.stringify(m),
}).success(function (m) {
alert(m); // true!
});
})
Related
Im having some unexpected problems with a ajax call within the aspnetboilerplate system.
I want to return an array to populate a select list,
The ajax call fires correctly and hits the controller. The controller returns the results as a model successfully, except it continually hits an error.
here is my controller action:
public async Task<List<DialectDto>> GetAllDialects(int Id)
{
var language = await _languageAppService.GetLanguage(Id);
return language.Dialects;
}
Here is my ajax call
var languageId = $(this).val();
abp.ui.setBusy('#textContainer');
$.ajax({
url: abp.appPath + 'Language/GetAllDialects?Id=' + languageId,
type: 'POST',
contentType: 'application/json',
success: function (content) {
abp.ui.clearBusy('#textContainer');
},
error: function (e) {
abp.ui.clearBusy('#textContainer');
}
});
Inspecting the return object in javascript clearly shows a 200 result with all the data in the responsetext property.
Other posts suggest that the content type isnt specified correctly and this is likely the a json parse error. Ive tried setting the dataType property to both 'json' and 'text' but still get the same response
I just had the same problem in one project. I'm using AspNet Boilerplate on a web .NET5 project.
Just like in your case I had a call to a controller method in my JavaScript code that returned 200 code, while in JS I had a parse error and then the "success" code was not executed. To solve the problem I changed the return value of my controller method. I used to return an IActionResult value (i.e. the Ok() value for ASP.NET). Then I changed the return value to another object (a DTO in my case) and everything went right afterwards. For reference:
Before (not working):
[HttpPost]
public async Task<IActionResult> Method([FromBody] YourClass input)
{
// method logic
return Ok();
}
After (working):
[HttpPost]
public async Task<DtoClass> Method([FromBody] YourClass input)
{
// method logic
return dtoClass;
}
I also tested that returning a JsonResult class works with the framework just like this (e.g. return new JsonResult(result)).
Hope this helps somebody else in the future :)
I have a VB Web API app.
I have a VB class/model like so.
Imports System.ComponentModel.DataAnnotations
Imports System.Web.Http
Public Class MSLDestinationInput
<HttpBindRequired>
<Required>
Public Property ShpmntCntrlNbr() As String
Get
Return m_ShpmntCntrlNbr
End Get
Set(value As String)
m_ShpmntCntrlNbr = value
End Set
End Property
Private m_ShpmntCntrlNbr As String
End Class
This is the controller:
Public Async Function GeneratePDF(data As MSLDestinationInput) As Task(Of IHttpActionResult)
If Not ModelState.IsValid Then
Return BadRequest(ModelState)
End If
Dim oMSLOutput As New MSLOutput
oMSLOutput.url = "api/PrintGenerateMSL"
Return Ok(oMSLOutput)
End Function
I am posting to the controller using jQuery.ajax with this parameters:
url: 'api/PrintGenerateMSL',
data: ko.toJSON(self),
type: "POST",
and everything is working well. However I don't really need to send the entire knockout model. I just need to send some of the properties. I've tried to send this data:
data: {ShpmntCntrlNbr : self.ShpmntCntrlNbr() };
instead of ko.toJSON(self). When the request reaches my controller, I find the parmeter data is empty.
How can I send only the required data to my controller instead of the whole ko view model?
You need to stringify the data. One way to do it is by using JSON.stringify, as you've done.
Most, but not all browsers, include the JSON manipulation functions. The problem is that if someones tries to use your application in a browser that doesn't have this methods, it will crash. Or you'll have to suplly a polyfill.
The good news is that you don't need to worry about it if you use ko.toJSON. In fact ko.toJSON does two things:
unwraps all the observables, if they exist
convert to JSON, by using JSON.stringify
That means that both of this options would work fine:
data: ko.ToJSON({ShpmntCntrlNbr : self.ShpmntCntrlNbr() })
data: ko.ToJSON({ShpmntCntrlNbr : self.ShpmntCntrlNbr })
Note that the property on the second one would be automatically unwrapped. If you took a piece of your viewmodel which is an object tree that includes some observable properties at any level, ko would also unwrap them automatically.
And, best of all, if the browser does not implement JSON.stringify, ko provieds its own implementation.
yes stringify took care of it. it is working now with.
data: JSON.stringify({ShpmntCntrlNbr : self.ShpmntCntrlNbr() }),
I'm trying to serialize a knockout object and pass it into a JSON property called multipleCharge.
This is the ajax code to send data though Get method to a mvc controller
$.ajax({
url: _url,
type: 'GET',
//data: { multipleCharge: ko.mapping.toJS(_vm)},
data: { multipleCharge : { AccountId : 2 } },
dataType: 'json'});
And this is the method
[HttpGet]
public HttpResponseMessage GetSalesInvoiceMultipleCharge
([FromUri]MultipleChargeDto multipleCharge)
{
...
}
Please, note that the ajax method has a comment line. Using the hardcoded line, it works, multipleCharge object is not null, but if I uncomment the another line, it's a bad request in my browser.
Look at this.
Any idea about what's happening. Using the Chrome console, it looks ok; so I can't identify the error.
It is may be IIS problems with very long URL.
See this Issue with URL length in IIS7 (Windows Server 2008) question and related answers.
Also see this http://www.iis.net/configreference/system.webserver/security/requestfiltering documentation.
You could try to solve this problem by editing web.config. But also you could use POST method instead of GET and send your data in request body.
I have created a Spring MVC web app.
The app makes a few calls to the controller. These calls are close/open/end game.
I make these calls using Ajax, so I can handle a response on the top of the page.
ajaxPost = function (url, action, id, onSuccess, onError) {
$.ajax({
type: "POST",
url: url + "?" + action + "=" + id,
success: function(response) {
if(onSuccess !== null) {
onSuccess(response);
}
},
error: function(e) {
if(onError !== null) {
onError(e);
}
}
});
};
The question I have is that I'm using 'POST' for the Ajax request, is that correct, or should it be 'PUT'?
My controller has a default URL, and I'm using the param attribute to decide which method to call, as I have many buttons on the page.
#RequestMapping(params = "open", method = RequestMethod.POST)
#RequestMapping(params = "close", method = RequestMethod.POST)
It doesn't sit well with me that I'm using 'POST' for these calls. Maybe it should be 'PUT'...
Any suggestions? Does it matter?
It depends on what your request should do. So there's no general rule that you should use one over the other, they have different use cases.
POST for creating a record.
PUT for updating an existing record (or putting a record at a specified location/id).
See this wikipedia article for the definitions.
One thing to note is that PUT should be idempotent, doing the same PUT request multiple times should ideally produce the same result as doing a single PUT request. However, POST is not idempotent, so doing several POST requests should (or will) create multiple new records.
So after having read this you should check what your method does, and select the corresponding request method.
Both PUT and POST may create a new record; PUT may also update/change an existing record.
The difference between POST and PUT is that PUT is expected to address the record with it's ID, so that the server knows what ID to use when creating (or updating) the record, while POST expects the server to generate an ID for the record and return it to the client after the record has been created.
Thus, a POST is addressed to the resource as a collection: POST /resource, while PUT is addressed to a single item in the collection: PUT /resource/1
Use POST. Always use POST, unless you're absolutely rock-solid certain that PUT is properly supported by your hosting system.
I have code in my javascript, a ajax to post data:
$.ajax({
url: '/Configurations/GetSelectedPageTranslation',
type: 'POST',
data: { inttype: $("#some").val(), objectType:{prop1: 'adsfa', prop2: 'asdf'}},
success: function (result) {
},
error: function () {
alert('error');
}
});
In the contoller i have a method with signature:
public JsonResult GetSelectedPageTranslation(int inttype, dynamic objectType)
I can have the inttype correctly. However the objectType will not be null but if i do like objectType.prop1, it will throw error. If i will JSON.stringify the object type in the javascript, the objectType in the controller will have a string value.
Could this be possible to directly access the JSON data in the controller using the dynamic data type like this: objectType.prop1 ?
Thanks
There is no out of the box support for dynamic action arguments in MVC3.
However MVC is very extensible so you can add this functionality. What you need to do is to create a custom IModelBinder where you can do the JSON deserialization and build up a dynamic object.
Luckily this article: Making MVC 3 a little moreā¦ dynamic is dealing with the exact same problem, so you can find also sample code and additional links there which should help you get started.