When transferring JSON data from a Webmethod in asp.net C# through an Ajax call in ExtJS 4.2.2, several characters are added to the beginning and end of the string.
JSON Data before leaving C#:
[{"ID":"0","NAME":"ALAN"},{"ID":"1","NAME":"BLAKE"}]
JSON Data as seen by firebug which is received by ExtJS
{"d":"[{"ID":"0","NAME":"ALAN"},{"ID":"1","NAME":"BLAKE"}]"}
This will also happen if the JSON Data has a set root property.
From what it appears it seems as if something somewhere along the line treated the incoming data as a variable in a JSON string or something like that.
Code on the C# end:
[WebService(Namespace = "localhost")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class Director : System.Web.Services.WebService
{
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = true, XmlSerializeString = false)]
public string getData()
{
string json = "[{\"ID\":\"0\",\"NAME\":\"ALAN\"},{\"ID\":\"1\",\"NAME\":\"BLAKE\"}]";
System.Diagnostics.Debug.WriteLine(json);
return json;
}
}
Code for the ExtJS Ajax Call (already has a workaround implemented):
Ext.Ajax.request({
async: false,
url: Test061014.ApplicationPath + '/Director.asmx/getData',
headers: { 'Content-Type': 'application/json' },
scope: this,
success: function (conn, response, options, eOpt) {
var s = conn.responseText;
s = s.substring(6, (s.length - 2));
s = s.replace(/\\/g, "");
categoryData = JSON.parse(s);
},
});
That is inserted by ASP.NET for security reasons. Check out this article for more details.
If you aren’t familiar with the “.d” I’m referring to, it is simply a
security feature that Microsoft added in ASP.NET 3.5’s version of
ASP.NET AJAX. By encapsulating the JSON response within a parent
object, the framework helps protect against a particularly nasty XSS
vulnerability.
They have a nice solution of using the dataFilter property so you can stop worrying about .d. Again, credit to the article, here is their solution. You may need to read the article's Don't make me think section as there are a couple of details I left out.
dataFilter: function(data) {
// This boils the response string down
// into a proper JavaScript Object().
var msg = eval('(' + data + ')');
// If the response has a ".d" top-level property,
// return what's below that instead.
if (msg.hasOwnProperty('d'))
return msg.d;
else
return msg;
},
Related
I have a Angular with .Net Core project, it's very simple in nature, the app just lists people also allows for someone to add a person.
However when I submit a new person the model in the controller is always null.
Here is my Angular-Post method:
public async onSubmit(value: any, valid: boolean, isLive: boolean) {
const model = {
...value,
};
console.log(JSON.stringify(model));
try {
const header = new HttpHeaders()
.set('Content-type', 'application/json');
var success = this.httpClient.post(this.baseUrl + 'person', JSON.stringify(model), { headers: header }).toPromise();
if (success) {
console.log('wooo');
}
} catch (error) {
console.log(error);
}
}
Logging the model to the console shows me the following data:
{"Firstname":"john","Lastname":"Doe","Gender":"2","DateOfBirth":"29/09/1955"}
However in the API controller I see the following:
Can anyone recommend or suggest why this comes through as null? I've tried removing the JSON.stringify but the issue persists.
Hitting the end point via Postman as suggested in the comments the model is populated with the data. Below is the Postman body:
{
"Firstname": "frefrefre",
"Lastname": "dewdewdew",
"Gender": 1,
"DateOfBirth": "2019-01-06T17:16:40"
}
Seems submitting from the Angular frontend sets the Gender variable to a string, and the DateOfBirth to an incorrect format..
You shouldn't need the [FromBody] unless you are also passing URL data in the same call, the issue would appear to be something wrong with the server interpretation of the content type/headers.
Try making the header collection exactly the same as it is in the Postman test.
Also it might be worth simplifying the code a little to trace the issue:
const headers = { 'content-type': 'application/json'}
const body=JSON.stringify(model);
console.log(body)
var success = this.httpClient.post(this.baseURL + 'person', body,{'headers':headers})
I have the following C# webservice (for testing purposes), which I eventually will turn into a WCFWebservice.
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[ScriptService]
public class Albums : WebService {
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public Person GetPeople() {
return new Person("Mike", 12);
}
}
And I call this using the following js:
$(document).ready(function () {
$.ajax({
url: '/webservice/Albums.asmx/GetPeople',
contentType: "application/json; charset=utf-8;",
dataType: "json",
type: 'post',
success: function (data) {
console.log(data);
}
});
});
But the weird thing (to me) is, I can't access data.Name within success().
Somehow, it adds a object d to data.
So if I want to access the name, I need to use: data.d.Name.
Where does this d come from?
It is nothing to worry about. It comes from using the OData protocol on the server:
This pattern ensures JSON payloads returned from OData services are
valid JSON statements, but not valid JavaScript statements. This
prevents an OData JSON response from being executed as the result of a
cross site scripting (XSS) attack.
To find out the nitty-gritty, see http://www.odata.org/documentation/odata-version-2-0/json-format.
This is done by default for both OData formatting and also as an security measure. You can easily remove this by adding the following to the ScriptMethod:
BodyStyle = WebMessageBodyStyle.Bare
Edit: Ok, so it looks like posting as application/json needs to be handled server side separate than a form. Is there any better way to post a form in C# as a complicated object? String:String just doesn't cut it. For example, I want to be able to use Dictionary to produce:
{
"data_map":{"some_value":1,"somevalue":"2"},
"also_array_stuffs":["oh look","people might", "want to", "use arrays"],
"integers_too":4
}
---OP---
I've looked on SO and other places. I'm just trying to POST a JSON string to a URL, but the server side keeps interpreting the content as a string instead of a query dict. We have other clients that aren't in c# that hit the server side fine (in HTML, JS, Objective-C, Java), but for some reason the POST data comes back wonky from the C# client.
C# source:
private static Dictionary<string,object> PostRequest(string url, Dictionary<string, object> vals)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(BaseURL+url);
httpWebRequest.ContentType = "application/json; charset=utf-8";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = JsonFx.Json.JsonWriter.Serialize(vals);
//json = json.Substring(1,json.Length-1);
streamWriter.Write(json);
streamWriter.Close();
}
try
{
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
string response = streamReader.ReadToEnd();
Dictionary<string,object> retval = JsonFx.Json.JsonReader.Deserialize<Dictionary<string,object>>(response);
return retval;
}
}
catch(WebException e)
{
}
return null;
}
This gets called like:
public static void Main (string[] args)
{
Dictionary<string,object> test = new Dictionary<string, object>();
test.Add("testing",3);
test.Add("testing2","4");
Dictionary<string,object> test2 = PostRequest("unitytest/",test);
Console.WriteLine (test2["testing"]);
}
For whatever reason, this is the request object that gets passed though:
<WSGIRequest
GET:<QueryDict: {}>,
POST:<QueryDict: {u'{"testing":3,"testing2":"4"}': [u'']}>,
COOKIES:{},
META:{'CELERY_LOADER': 'djcelery.loaders.DjangoLoader',
'CONTENT_LENGTH': '28',
'CONTENT_TYPE': 'application/json; charset=utf-8',
'DJANGO_SETTINGS_MODULE': 'settings.local',
'GATEWAY_INTERFACE': 'CGI/1.1',
'HISTTIMEFORMAT': '%F %T ',
'HTTP_CONNECTION': 'close',
'LANG': 'en_US.UTF-8',
'QUERY_STRING': '',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_HOST': '',
'REQUEST_METHOD': 'POST',
'RUN_MAIN': 'true',
'SCRIPT_NAME': u'',
'SERVER_PORT': '9090',
'SERVER_PROTOCOL': 'HTTP/1.0',
'SERVER_SOFTWARE': 'WSGIServer/0.1 Python/2.7.2+',
'SHELL': '/bin/sh',
'SHLVL': '1',
'SSH_TTY': '/dev/pts/0',
'TERM': 'xterm',
'TZ': 'UTC',
'wsgi.errors': <open file '<stderr>', mode 'w' at 0x7f3c30158270>,
'wsgi.file_wrapper': <class 'django.core.servers.basehttp.FileWrapper'>,
'wsgi.input': <socket._fileobject object at 0x405b4d0>,
'wsgi.multiprocess': False,
'wsgi.multithread': True,
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
'wsgi.version': (1, 0)}>
[18/Oct/2012 19:30:07] "POST /api/1.0/unitytest/ HTTP/1.0" 200 31
Some of the more sensitive data in the request has been removed, but is irrelevant
Ugh, I hope I don't make a habit of answering my own questions.
So, Posting Json this way is different than a normal form submission. That means if your server side is expecting just a normal form submission it will not work. The C# code does as it's intended to do, but only submits a JSON string as the POST body. While this may be convenient for people who validate, clean, and handle raw input anyway, keep in mind that if you're using a normal web framework, you will have to write an alternate condition to accept the raw string.
If anybody has an idea how to do a form submission in C# containing objects more complex than a hashmap/dictionary of strings, then I will upvote your answer and give you lots of hugs. For now, this hacky nonsense will have to do.
Well, once, a long time ago I implemented a banking app front end, which, made massive use of JSON for communication between client and server.
It was a clean way to find complex object to and from the server, no need to make complex cleanup or raw string processing.
The key was using WebServices designed for ajax on the server side, your web server class should look like this:
[WebService(Namespace = "http://something.cool.com/")]
[ScriptService] // This is part of the magic
public class UserManagerService : WebService
{
[WebMethod]
[ScriptMethod] // This is too a part of the magic
public MyComplexObject MyMethod(MyComplexInput myParameter)
{
// Here you make your process, read, write, update the database, sms your boss, send a nuke, or whatever...
// Return something to your awaiting client
return new MyComplexObject();
}
}
Now, on your client-side, set things up to make ASP.NET talk to you in JSON, I'm using JQuery for making the ajax requests.
$.ajax({
type: 'POST',
url: "UserManagerService.asmx/MyMethod",
data: {
myParameter:{
prop1: 90,
prop2: "Hallo",
prop3: [1,2,3,4,5],
prop4: {
// Use the complexity you need.
}
}
},
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function(response) {
alert(response.d);
}
});
Anything that ASP want to return as the result for your ScriptMethod, is going to be contained in the response.d variable. So, let's say you are returning from the server a complex object, "response.d" is the reference to your object, you access all the object members using the dot notation as usual.
I'm writing an ASP.NET web application that transmits JSON between the client and the server. I have nearly everything complete, except that I cannot seem to transmit from the client the JSON to the ASMX and have it interpreted as anything but a Dictionary<string>
On the server-side, I have
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService]
public class WebService1: System.Web.Services.WebService
{
[WebMethod(EnableSession = true)]
public bool SaveExpression(ExpressionStructure Expression) {
return true;
}
}
On the client-side, I am sending the data using $.ajax(), where expressionObject.expression is an object of .NET class ExpressionStructure received earlier by the client:
var dataSubmit = { Expression: expressionObject.expression };
$.ajax({
url: "WebService.asmx/SaveExpression",
contentType: "application/json; charset=utf-8",
dataType: "json",
type: 'POST',
data: JSON.stringify(dataSubmit),
success: function (msg) {
alert(msg.d);
},
error: function (obj, status, msg) { ajaxServerError(obj, status, msg); }
});
When the SaveExpression method accepts Object, I get a dictionary of strings. When I use ExpressionStructure, I get a 500 Internal Server Error.
The expression being sent is an unmodified version of what is received from this MVC3 controller method.
public ActionResult Expression(int ExpressionID) {
ExpressionStructure es = GetExpressionFromDatabase(ExpressionID);
return new JsonResult {
Data = new {
expression = es,
view = this.RenderPartialView("_Expression_Statement", es)
},
JsonRequestBehavior = System.Web.Mvc.JsonRequestBehavior.AllowGet
};
}
The expression itself looks like this:
{
"Expression":
{
"Name":"Status Desc",
"Type":0,
"Statement":
{
"FormulaItem":
{
"Type":"Replace",
"Parameters":
[
{
"Type":"Char",
"Value":
{
"Value":"[Status]"
},
"Source":"picker"
},
{
"Type":"input",
"Value":
{
"Value":"0,1,2;Home,Driving,Away"
},
"Source":"inputBox"
}
]
}
}
}
}
I've tried changing the ASMX method to take string, ExpressionStructure but both return a 500 Internal Server Error. I've also tried modifying the JSON coming from the client, doing various wrappings (including wrapping the outermost "Expression" with square braces).
What critical element am I missing that is preventing the ASMX from correctly taking the JSON and getting the ExpressionStructure? Optionally, how can I get a string either directly or from the Dictionary so I can perform the JSON convertion manually (which I'd rather not do)?
After building a small test case separated from the main project, I was able to get better information about why the server was returning 500 Internal Server Error. As it happens, the web app was catching a deserialization error and attempting to redirect the request to the error handler page. Since it wasn't an actual page request, the redirect threw an error, which forced a redirect to the error handler page....until too many redirects kicked out an error.
The underlying problem was in the deserializing of the JSON to a rather complicated (collection of) classes on the .NET side. The classes, perfectly decorated with XML attribute decorators, used abstract classes when there was a choice in the schema (ie one element or another). Without similar JSON decorators, the deserialization didn't know how to deserialize the JSON to which class.
I wrote a .ToJson() extension method like so:
public static string ToJson(this object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj);
}
This allows me to do something like myObject.ToJson() to get a JSON string to send back to my application via my .asmx web service. This all works great.
A problem has arisen where I'm building a page that requires massive amounts of asynchronous data be transferred to the page and I'm starting to reach data payloads of upwards of 100k. In looking at the JSON, I've noticed that it encodes whatever I send back, which adds a lot of extra characters. As this application is an admin tool for a very small audience, I'm not really worried a whole lot about this kind of security, but I can't seem to find it in here how to control encoding.
Here's what I'm receiving (example):
{"d":"[{\"OrderId\":1308,\"Approved\":true,
\"Status\":\"\\u003cimg class=\\\"statusicon\\\" src=\\\"/images/ ...
when I should be getting something like:
{"d": [{"OrderId":1308,"Approved":true,"Status":"<img class=\"statusicon\"
src=\"/images/ ...
Is there any way to control whether or not the JSON data gets escaped?
One thing to note is that I'm forced to use this in my .ajaxSetup() to parse this data coming back:
dataFilter: function (data) {
if (data) {
var msg;
if (typeof (JSON) !== 'undefined' && typeof (JSON.parse) === 'function') {
msg = JSON.parse(data);
} else {
msg = eval('(' + data + ')');
}
if (msg.hasOwnProperty('d')) {
return msg.d;
} else {
return msg;
}
}
}
I had this problem. The json library is not returning escaped text. I believe javascript itself is interpreting it as escaped.
<script type="text/javascript">
$(document).ready(function () {
var columnNumber = 1;
var databaseTypes = <%= Html.Raw(ViewBag.DatabaseTypes) %>;
Take a look at the Viewbag area. This is how to inject text within a asp.net mvc view.