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.
Related
I have attempted to modify one of my api controller to allow for the creation of multiple reservations by allowing one of the parameters to be passed in as a pipe delimited string. The method and class can be seen here:
public class ReservationsController : ApiController
{
public HttpResponseMessage PostReservation(string eRaiderUserName, string SpaceNumbers)
{
char[] delimiter = { '|' };
string[] spaces = SpaceNumbers.Split(delimiter);
bool saved = true;
foreach(string space in spaces)
{
var reservation = new Reservation { eRaiderUserName=eRaiderUserName, SpaceNumber=Convert.ToInt32(space) };
if (true)
{
reservation.Game = db.Games.FirstOrDefault(g => g.ID == AppSettings.CurrentGameID);
db.Reservations.Add(reservation);
db.SaveChanges();
//HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, reservation);
//response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = reservation.ID }));
//return response;
}
else
{
saved = false;
//return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
db.SaveChanges();
if (saved)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = 1 }));
return response;
} else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
}
I have a form that posts what I think should be the right information, but I keep getting this error:
{"$id":"1","Message":"No HTTP resource was found that matches the request URI 'http://localhost:58463/api/Reservations'.","MessageDetail":"No action was found on the controller 'Reservations' that matches the request."}
The (modified) save method in the api is still definitely a work in progress. But what is keeping this from finding the web api controller? Here is the firebug output:
As pointed out, the problem is that a POST action can only transfer the data posted in the body to a single object (for technical reasons).
That means that you can get data from the route data, from the querystring, and from the body, with the following limitations:
data from querystring or route data must be single values (i.e. they cannnot be classes), in any number
you can have only one parameter of the action with data coming from the request body, but this can be a complex class
you can make any combination of this, i.e. a single or complex param coming from the body, and any number of single parameters coming from the route data or the querystring.
So, the most generic way to solve your problem (i.e. that can be easyly applied to other classes where you need to pass complex data, even more complex than this case) is this:
First, make a class which has properties for all the needed data,in your case:
public class ReservationData
{
public string eRaiderUserName { get; set; }
public string SpaceNumbers { get; set; }
}
Second, use this class as the type of the received parameter in your action:
public HttpResponseMessage PostReservation(ReservationData reservationData)
With this code the formatter can map all the data in the request body to a single parameter in the action. You can use JSON or formdata formats, like the generated by jQuery.
NOTE: the property names must case-sensitively match the name of the posted parameters.
This is because you send x-www-form-urlencoded data to controller, to handle this data you must use [FromBody] before parameter like
public HttpResponseMessage Post([FromBody] string name) { ... }
but this approach has a lot of limitation:
1) There can be only one parameter marked [FromBody] attribute (it can be complex type)
2) The data must be encoded as =value not as key=value .
You can read it here description and how make it work here example .
If it possible i recommend you to send Json data to controller, without this limitation.
Web API has limited support to map POST form variables to simple parameters of a Web API method. Web API does not deal with multiple posted content values, you can only post a single content value to a Web API Action method.
public HttpResponseMessage PostReservation(string eRaiderUserName, string SpaceNumbers)
{ //...}
and you are trying to call using jQuery:
$.ajax({ url: 'api/reservations', type: 'POST', data: { ... }, dataType: 'json', success: function (data) {alert(data);} });
Unfortunately, Web API can’t handle this request and you’ll get error. But if you pass parameters using query string, It’ll work:
$.ajax({ url: 'api/reservations?eRaiderUserName=2012&SpaceNumbers=test', type: 'POST', dataType: 'json', success: function (data) { alert(data); } });
But it’s not good solution and not applicable for complex objects. So here are the different ways to do it.
Using Model Binding (Preferred)
Using Custom Parameter Binding
FormDataCollection
Query String
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;
},
I am sending some data from jquery.ajax to aspx, parsing there and writing on response, but that data is coming in error part, I think some error is being occured which is not being shown, but the correct data is getting returned in error part. Code is below.
JQUERY
var json = "{'uname':'" + $("#uname").val() + "','pwd':'" + $("#pwd").val() + "'}";
alert(json);
$.ajax({
type: "POST",
url: "DataProcess.aspx?Save=1",
data: json,
contentType: "application/json; charset=utf-8",
dataType: "json",
async: false,
success: function (msg) {
alert(msg);
},
error: function (msg) { alert("failed: " + msg.responseText); }
});
DataProcess.aspx.cs
namespace Test
{
public partial class DataProcess : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
ProcessAjaxRequest();
}
private void ProcessAjaxRequest()
{
if (Request.ContentType.Contains("json") && Request.QueryString["Save"] != null)
SaveMyData();
}
private void SaveMyData()
{
System.IO.StreamReader sr = new System.IO.StreamReader(Request.InputStream);
string line = "";
line = sr.ReadToEnd();
JObject jo = JObject.Parse(line);
Response.Write(line);
Response.Write((string)jo["uname"]);
Response.Write((string)jo["pwd"]);
}
}
}
So i am getting what ever the response is in the error part, what is wrong?
K i got the problem, above code is correct, Now my other doubt is, can i call a particular nonstatic method from this same aspx.cs, I mean when i tried something like this "DataProcess.aspx/Test?Save=1". It gives error saying not a web method, I declared method as
[WebMethod]
public void Test(){
}
K guys, I thought the problem was gone, just now i observed that, if i write a string to response something like this Response.Write("success"), its comming in jquery error block, but when i write something like this "Response.Write(0);", its comming in success block. The first one should also come, someone explain whats the problem
Thanks
not sure the exact problem but your json looks like a string. it should be like this
var json = {'uname':'" + $("#uname").val() + "','pwd':'" + $("#pwd").val() + "'};
I am assuming that you have already figured out about PageMethods (i.e. a static method decorated with WebMethod keyword).
Regarding your later question, its not possible to use non-static (instance) methods as Page Methods. The reason is quite simple - an instance method could access instance variables including control tree. Without view-state, ASP.NET cannot guarantee a correct control tree state in post-back scenarios. For such needs, UpdatePanel is the way to go - as it provides AJAX within the ASP.NET control model. Page Methods are meant for stream-lined communication where request/response data are minimalistic without overheads (i.e. the only meaningful data that you wants to communicate between client/server).
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.
Hopefully this is a fairly easy question. Pardon my ignorance, but I am mostly a PHP/Zend developer, so i am struggling a little in C# and Visual Studio with a json/ajax issue. Is there something obvious I am missing? Any help will be appreciated. Should I be looking at List<>?
Here is the error I receive when I fire the javascript ajax function:
"Unknown web method getwidgets."
I have a dataset in C#, that I run through a JSON converter method. This works well and returns my data in a JSON string.
private widgetsBL widgetsBLObject = new widgetsBL();
[WebMethod]
public String getwidgets()
{
DataSet results = new DataSet();
results = widgetsBLObject.selectTheWidgets();
string jsresults = MyClassLibrary.JqueryTools.GetJSONString(results.Tables[0]);
return jsresults;
}
Here is the jsresults:
{"Table" : [ {"widgetid" : "1","widgetname" : "gizmo1000","widgetdescription" : "very cool widget"},
{"widgetid" : "2","widgetname" : "gizmo2000","widgetdescription" : "decent widget"},
{"widgetid" : "3","widgetname" : "gizmo3000","widgetdescription" : "terrible widget"} ]}
My Javascript call:
$.ajax({
type: "POST",
url: "my.aspx/getwidgets",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
// do stuff with returned data
}
});
getwidgets needs to be static
[WebMethod]
public static String getwidgets()
Remember that if you want your method to be exposed to calls from JavaScript, you need to mark your method with ScriptMethodAttribute. Thus making it look like this:
[ScriptMethod]
[WebMethod]
public static String getwidgets()
{
// Your core here
}
I would return in the method, the object itself and not not a serialized version of it because ASP.NET will JSON serialize it for you if you mark it as [ScriptMethod]; so in the client your variable data.d will contain the object itself and not a simple string that later you have to deserialize, as in your current implementation.
You are mixing technologies: my.aspx is for rendering HTML content, but it can be used to implement REST functionality.
In your case, the easiest would be to implement your code as part of the Page_Loaded() method. Make sure you clear the response first (so you don't have any additional markup in the response). Furthermore, you might want to set the content type of your response to JSON (rather than the default html):
protected void Page_Load(object sender, EventArgs e)
{
Response.ClearContent();
Response.ContentType = "application/json";
DataSet results = new DataSet();
results = widgetsBLObject.selectTheWidgets();
string jsresults = MyClassLibrary.JqueryTools.GetJSONString(results.Tables[0]);
return jsresults;
}
Then retrieve you JSON string at my.aspx (no getwidgets).
Also, since you are not posting any data, consider using GET rather than POST in your AJAX call.