How to receive Non-primitive data in WebMethod? - javascript

I am trying to pass an array of bytes to my WebMethod through JavaScript.
The problem is that my WebMethod returns with the message "The test form is only available for primitive methods as parameters."
When I change data type to string or any other kind of primitive variables the WebMethod accepts it goes on.
I'm transforming an object into a byte[], named msgpackEnvio using MessagePack(The transformation itself occurs well), and then sending to WebService.
Here's my request to WebService using jquery
$.ajax({
beforeSend: function (request) {
request.setRequestHeader("Content-Type", "application/json");
},
processData: false,
dataType: "json",
url: url,
data: msgpackEnvio,
type: "POST",
error: function (data) {
$('#upload-load').hide();
$('.bt-enviar-excel').removeAttr('disabled', 'disabled');
var msg = document.getElementsByClassName('msg')[0];
msg.style.color = 'red';
msg.innerHTML = 'Erro interno servidor!';
},
success: //some code
In fact, the code above doesn't matters at all, my problem is in the webService, and how to make it receive an array or perhaps an object.
And here's my WebMethod that should be able to receive a byte[]
[WebMethod]
public string TestMessagePack(byte[] name)
{
//my code
return "Finish";
}

You can use base64 encoding and decoding to send byte arrays as strings. Anything more than that and you might want to use post requests and JSON serialization/deserialization instead. There's not going to be a generic one size fits all conversion between complex C# objects and javascript objects which is why only primitive parameters are allowed, so for nonprimitive things you'll need a translation layer. That's normally going to mean some form of string serialization and deserialization.

Related

AJAX/Spring MVC: JSON Object rendering in bracket notation in POST request

Why would a JSON be rendered in bracket notation when bound to a Web Request?
I have an application that makes 2 REST Controller calls in a row.
First, an address is read from a form, serialized, AJAXed to a validation endpoint, and returned to the UI/JS as a Response object payload. This part works fine.
$.ajax({
method: 'POST',
url: '/validate-shipping-address',
data: $shippingInfoForm.serialize()
}
#PostMapping(path = "/validate-shipping-address")
public RestResponse<Address> validateShippingAddress(
#ModelAttribute("shippingForm") ShippingForm shippingForm) {
return validateAddress(shippingForm.getAddress());
}
If the response is successful, that payload (the address) is sent directly into the AJAX call for the second endpoint. This request blows up with a 400 and never enters the actual method.
$.ajax({
method: 'POST',
url: '/shipping-stuff',
data: {
"shippingAddress": validationResponse.payload,
"shipDate": shipDate,
"csrfToken": csrfToken
}
}
#PostMapping(path = "/shipping-stuff")
public RestResponse<?> doShippingStuff(
#RequestParam(name = "shippingAddress") Address shippingAddress,
#RequestParam(name = "shipDate") #DateTimeFormat(pattern = "yyyy-MM-dd") Date shippingDate) {
doStuff(); // Never hit
}
After much analysis, the issue is that Spring MVC cannot deserialize the address passed as an Address object. In the request, I see that the address fields are rendered as address[city], address[state], etc. instead of the standard dot notation address.city, address.state (as the first request has it). If I manually access them via the request using the bracket notation as the param name, it will pull out the value. e.g. request.getParameter("address[city]");.
When I use Chrome dev tools debugger to inspect the response from the first and the object entering the second AJAX call, they look like valid JSON. The Network:Form Data section in Chrome differs though - It shows the dot notation for the first, successful call and the bracket notation for the second, unsuccessful call.
Form Data (first call):
address.firstName=John&address.lastName=Doe&address.addressLine1=123+Main+St&address.city=New+York+City&address.state=NY&address.postalCode=12345&csrfToken=XXXX
Form Data (second call): (%5B = '[' and %5D = ']')
shippingAddress%5BfirstName%5D=John&shippingAddress%5BlastName%5D=Doe&shippingAddress%5BaddressLine1%5D=123+MAIN+ST&shippingAddress%5Bcity%5D=New+York+City&shippingAddress%5Bstate%5D=NY&shippingAddress%5BpostalCode%5D=12345&shippingAddress%5BzipFour%5D=6789&shipDate=2019-05-25&csrfToken=XXXX
So there really are 2 sub-questions:
(1) Is there something in the JS handling that would cause the address to be passed in the bracket form instead of the normal JSON form? I'd think if I could get around this then Spring MVC should work like normal.
(2) Is there a way to make Spring MVC able to handle this without resorting to JSON.stringify and then parsing with Gson/ObjectMapper directly in the controller code?
I've tried all permutations I can think of involving custom wrapper objects, JSON.stringify, #RequestParam, #ModelAttribute, and bare (no annotations). I also tried stringify-ing the whole AJAX payload and using #RequestBody on a wrapper object in the controller, but then the call fails as the csrfToken is not detected.
I've read through this, this, and this, which informed the attempts above.
For now, I've worked around the issue with JSON.stringify and Gson (option 2 above), but would rather make Spring MVC do the work automatically.
Work around:
$.ajax({
method: 'POST',
url: '/commercial-checkout/shipping-stuff',
data: {
"shippingAddress": JSON.stringify(shippingAddress),
"shipDate": shipDate,
"csrfToken": csrfToken
}
});
#PostMapping(path = "/shipping-stuff")
public RestResponse<?> doShippingStuff( //
#RequestParam(name = "shippingAddress") String shippingAddressJson,
#RequestParam(name = "shipDate") #DateTimeFormat(pattern = "yyyy-MM-dd") Date shipDate) {
Address address = gson.fromJson(shippingAddressJson, AddressImpl.class);
}
As per your comment,
I also tried stringify-ing the whole AJAX payload and using
#RequestBody on a wrapper object in the controller, but then the call
fails as the csrfToken is not detected.
When you use #RequestBody you need to create corresponding POJO object to deserialise your JSON. Also you need to add content-type property in your AJAX to indicate the server that you are sending an JSON.
$.ajax({
method: 'POST',
data: 'json',
content-type: 'application/json',
url: '/commercial-checkout/shipping-stuff',
data: {
"shippingAddress": JSON.stringify(shippingAddress),
"shipDate": shipDate,
"csrfToken": csrfToken
}
});
Add a POJO as I mentioned,
public class AddressPOJO {
shippingAddress,
shipDate,
csrfToken
//getter / setters
}
Modify your controller method,
#PostMapping(path = "/shipping-stuff", consumes = "application/json")
public RestResponse<?> doShippingStuff( #RequestBody AddressPOJO addressPJO) {
// do your logic..
}

JSON Stringify Not Working on Array (ASP.NET)

EDIT2: Found the solution, please see JSON, AJAX, and ASP.NET
The problem ended up being that the parameter in the ajax call is NOT sent to the WebMethod on the server side as a string. The JSON string in the data field of the ajax call is actually converted to the object type specified in the WebMethod's parameter. That being said, my problem was that the data was not being passed into the WebMethod as a string, but a different object type.
EDIT: Looking at the browser console in Chrome, i'm getting this error: Failed to load resource: the server responded with a status of 500 (Internal Server Error). Digging in deeper, I have the following response:
{"Message":"Type \u0027System.Collections.Generic.IDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]\u0027 is not supported for deserialization of an array.","StackTrace":" at System.Web.Script.Serialization.ObjectConverter.ConvertListToObject(IList list, Type type, JavaScriptSerializer serializer, Boolean throwOnError, IList\u0026 convertedList)\r\n at System.Web.Script.Serialization.ObjectConverter.ConvertObjectToTypeInternal(Object o, Type type, JavaScriptSerializer serializer, Boolean throwOnError, Object\u0026 convertedObject)\r\n at System.Web.Script.Serialization.ObjectConverter.ConvertObjectToTypeMain(Object o, Type type, JavaScriptSerializer serializer, Boolean throwOnError, Object\u0026 convertedObject)\r\n at System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit)\r\n at System.Web.Script.Serialization.JavaScriptSerializer.Deserialize[T](String input)\r\n at System.Web.Script.Services.RestHandler.GetRawParamsFromPostRequest(HttpContext context, JavaScriptSerializer serializer)\r\n at System.Web.Script.Services.RestHandler.GetRawParams(WebServiceMethodData methodData, HttpContext context)\r\n at System.Web.Script.Services.RestHandler.ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)","ExceptionType":"System.InvalidOperationException"}
The following ajax call is never executed, and it seems like the data parameter does not have a valid value. If data is set equal to a string literal with valid JSON, there is no problem. There just seems to be an issue converting javascript objects into a valid parameter for data in the ajax call. Please see the below code snippet in my aspx file:
<script type="text/javascript">
$(document).ready((function () {
$('#mytable tr').click(function (event) {
var array = [];
$(".tdval").each(function () {
array.push({
value: $(this).html()
});
});
alert(JSON.stringify(array)); //produces what appears to be valid json after calling stringify: [{"value":"val1"},{"value":"val2"}]
$.ajax({
type: "POST",
url: "schedule.aspx/GetCurrentTime",
data: JSON.stringify({ array }), //this seems to be the issue
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response){
alert(response.d + "lol");
},
complete: function () {
alert("complete");
}
});
});
}));
</script>
And this is the method in the C# code behind that the ajax call posts to:
[System.Web.Services.WebMethod]
public static string GetCurrentTime(string name)
{
return "Hello ";
}
Interestingly, I am able to change data: JSON.stringify({ array }) in the ajax call to some random json as a string literal and it works fine as it should. But when using stringify, the "success" callback method from the ajax call is never executed. Any thoughts?
You have the array inside an object when you call JSON.stringify(), so the JSON you're sending will look like:
{ array: [{"value":"val1"},{"value":"val2"}] }
instead of what you alerted earlier in the function.
Instead, use:
data: JSON.stringify(array),

Difference between filter_input and direct acces on $_POST after objective ajax request

I have different results by using filter_input(INPUT_POST, 'attribute') and $_POST['attribute'] and don't know why this happens.
The Post-Request is send by a JavaScript build with JQuery and looks like that:
// type javaScript
var formData = {
field_a: "valueA",
field_b: "",
field_c: undefined
};
$.ajax({
url: 'serverAddress',
data: {action: 99, formData: formData},
dataType: 'json',
method: 'post',
success: function(){
console.log(arguments)
}
});
My PHP-Script looks like that:
// type php
$requestMethod = INPUT_POST;
$response = [
"fi-result" => filter_input($requestMethod, 'formData'),
"direct-result" => $_POST['formData'];
];
echo json_encode($response);
the result what is coming back is not what i was awaiting because the access via filter_input returns falsein my tests and not an json object like the direct access on the super global $_POST.
// type json response
{
"fi_result": false,
"direct-result": {
"field_a": "valueA",
"field_b": ""
}
}
Why are there differences between using filter_input and direct access on $_POST?
I don't want to access the super global $_POST. Is there any way to use filter_input like above without encode formData to a String in JavaScript and decode it in PHP one simple step after encoding?
By the way. I'm using TypeScript to generate my JavaScript. That is not supporting the FormData Object (transpiler throws error on new FormData()). So i can't use this.
I found the answer deep in the PHP docs. POST is not build to transport deep object. And filter_input method tries to get simple datatypes like string or int. this method does not parse internal so i have to send it as JSON string and decode it or i can't use filter_input in my case.
i took the first and send now strings.

WCF Service Not Parsing jsonP Parameters correctly

I'm sending a call like so:
$.ajax({
url: myURL+functionName,
contentType: "application/json; charset=utf-8", //also tried application/javascript
cache: false,
dataType: "jsonp",
data: parameter,
success:function(response){
alert("great success!");
},
error:function(error) {alert("boo-urns");}
Which is fine and everything looks like it should. The parameter object comes in with a value like so:
{ac:"a8d8e6ef-5907-4978-11cf-39add52b996c",
date:"20160626",
deviceId:"W-eXXX9680-13X3-9317-f1b3-eXXXXe50",
komponenten:Array[5],
menu_kf_nr:1,
mz_nr:"3",
pt_kf_nr:1,
pt_nr:"311701",
sessionId:"b22bXXX6-20XX-4XXX-9ed6-0dXXb297"}
Here is the server side method:
public bool functionName(string ac, string date, string deviceId, List<komponenten> komponenten, int? menu_kf_nr, string mz_nr, int pt_kf_nr, string pt_nr, string sessionId) {
//do stuff with the data
}
And then on the server side though my params only come as follows:
ac "a8d8e6ef-5907-4978-11cf-39add52b996c" string
date "20160626" string
deviceId null string
komponenten null System.Collections.Generic.List<kompnenten>
menu_kf_nr null int?
mz_nr "3" string
pt_kf_nr 1 int
pt_nr "311701" string
sessionId null string
Can anyone tell me where I need to look in order to eliminate the nulls?
Thanks in advance
You should pass your parameters individually in the aJax call, also, the C# side is probably expecting JSON data to be received.
Take a look here to see how to pass multiple parameters in an aJax call.
Note the second part of the accepted answer, using JSON.Stringify

Getting JSON Object Result from $.post()

I'm attempting to call a web service via AJAX in a WebForms application.
My script looks something like this:
$.post('UpdateServer.asmx/ProcessItem',
'itemId=' + $(this).text(),
function (result) {
alert(result);
});
My web service looks something like this.
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class UpdateServer : System.Web.Services.WebService
{
[WebMethod]
public string ProcessItem(int itemId)
{
return new JavaScriptSerializer().Serialize(
new { Success = true, Message = "Here I am!" });
}
}
The web method is called as expected and with the expected argument. However, the argument passed to my success function (last parameter to $.post()) is of type document and does not contain the Success and Message members that I'm expecting.
What's are the magic words so that I can get back the object I'm expecting?
EDIT
On closer inspection, I can find the data I'm looking for as follows:
result.childNodes[0].childNodes[0].data:
"{"Success":true,"Message":"Server successfully updated!"}"
The reason you're seeing that odd structure of nodes that end with JSON is because you're not calling the service the necessary way to coax JSON out of ASMX ScriptServices and then returning a JSON string anyway. So, the end result is that you're returning an XML document that contains a single value of that JSON string.
The two specific problems you're running into right now are that you're manually JSON serializing your return value and you're not calling the service with a Content-Type of application/json (.NET needs that to switch to JSON serializing the response).
Once you fixed those issues, you'd also run into an "invalid JSON primitive" error due to the data parameter being URL encoded instead of a valid JSON string.
To get it working, do this on the server-side:
[ScriptService]
public class UpdateServer : System.Web.Services.WebService
{
[WebMethod]
public object ProcessItem(int itemId)
{
return new { Success = true, Message = "Here I am!" };
}
}
You could also create a data transfer object (aka ViewModel) to return instead of using an anonymous type and object, if you want.
To successfully get raw JSON out of that, do this on the client-side:
$.ajax({
url: 'UpdateServer.asmx/ProcessItem',
type: 'post',
contentType: 'application/json',
data: '{"itemId":' + $(this).text() + '}',
success: function(result) {
// This will be { d: { Success: true, Message: "Here I am!" } }.
console.log(result);
}
});
If you have a few minutes, read through the posts in the communication section of jQuery for the ASP.NET developer. You'll find a lot of that information helpful as you continue down this path.
Note: The links that helmus left were relevant. Nothing has fundamentally changed between 2.0 and present with regards to using ASMX ScriptServices to communicate via JSON. If you're interested in the truly cutting edge approach to this problem in .NET, ASP.NET Web API is the way to go.
Add this attribute to your ProcessItem method:
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
Be more explicit in your $.post call.
$.ajax({
type:'post',
url:'UpdateServer.asmx/ProcessItem',
data: {'itemId':$(this).text()}
}).done(function (result) {
alert(result);
});

Categories