I'm trying to convert a web forms page with a number of code-behind webmethod functions into an MVC view with a controller.
The data I get back is different. They have properly formatted JSON, but the webmethod returns JSON like this:
{"d":"{\"Success\":true,\"Data\":{\"QuoteId\":340439,\"LoginId\":40,
And the controller returns:
"{\"QuoteId\":340444,\"LoginId\":40,
Its not wrapping it in data.d like it is set up to handle in the javascript and there are no Success or Data objects. And when I try to parse it ($.parseJSON(data)) like I did with the webmethod, it gives me the old error at line 1 message.
I'm sure that if I played with it enough I could get it to work with the way the data is coming through, but I have many pages that I need to covert in the future and I'm just wondering if there is an easy way to get the controller to format it like the webmethod does.
I'm pretty sure that I know why the data is formatted differently, but it would make my life easier if I could just return the data in the same way. I've tried returning a JsonConvert.SerializeObject(obj), which is just a string, and a Return Json(obj), which I'm guessing is just a string as well, but they both return stuff the same, non-data.d way.
Okay, so I've decided to wrap the data in a "d", so that I can have a "data.d" in the Ajax result.
But for some reason the dates aren't being formatted correctly when I just send the data back via:
return Json(thewrappeddata);
they turn out weird.
So basically what I have to do is this:
public class JSONReturn {
public object d;
public static object Wrap(object data) {
return new JSONReturn() { d = data };
}
}
var thedata = new TonsofData();
return Json(
JSONReturn.Wrap(
JsonConvert.SerializeObject(thedata,
new JsonSerializerSettings {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
}
)
)
);
It seems like I'm "jsonifying" the data twice here, once with JsonConvert.SerializeObject() and once with Json() (I THINK that serializes), but it seems to work and now I get what I need and all the JS just works as it did before. If I leave out the JsonConvert.SerializeObject(), the Json() formats the dates incorrectly and it causes all sorts of failure down the line.
Related
Why does my action method only bind the first word of a string I pass into it using a query string?
For example, in jquery, I build a queryString from the results of an ajax call:
success: return(resultData){
var queryString = "?ok=true&message=" + resultData.message;
}
Then I try to load a view into a dialog by calling a controller and passing the queryString
$dialogHandle.load("/Account/RegisterStatus" + queryString, function() { ... });
At this point the queryString correctly hold an entire message. However if I break in my controller:
public ActionResult RegisterStatus(bool ok, string message)
{
//break here
}
I notice that ok binds correctly but message only contains the first word of the error message passed in.
How can I pass a sentence as one string parameter?
Is there a better way to do this, without query string?
EDIT: hmm now that I think about it does make sense since urls cant have space but then how do I accomplish this... is there a specific word delimiter in the default model binder?
It's all about URL escaping: escape("It's me!") // result: It%27s%20me%21
Do that around your resultData.Message and it should work better. For debugging purposes, use Fiddler2 or some Web Inspector to see what request is being send. This is really valuable when you are debugging AJAX...
And of course, do the reverse in C#: HttpUtility.UrDecode Method (String)
So, there are a wealth of Flex articles online about how to handle a .NET WebMethod that returns a DataSet or DataTable. Here is an example:
Handling web service results that contain .NET DataSets or DataTables
So, I know how to use result.Tables.<tablename>.Rows and the like. But what I cannot seem to figure out or find online is how to go the other direction - a method to pass objects or tables back to the .NET Webservice from Flex, without stooping to passing XML as a string, or making huge web service methods that have one parameter for each property/column of the object being stored. Surely others, smarter than I, have tackled this issue.
I am using ASP.NET 2.0 Typed DataSets, and it would be really nice if I could just pass one object or array of objects from Flex to the web service, populate my Typed DataTable, and do an Update() through the corresponding typed TableAdapter. My dream would be a [WebMethod] something like one of these:
public void SaveObject(TypedDataTable objToSave) { ... }
public void SaveObject(TypedDataSet objToSave) { ... }
I've had the typed datatables saving to the database, I know how to do that part and even a few tricks, but we had XML being passed back-and-forth as a string - eww. I'm trying to get to a more object-based approach.
The best object based approach is AMF. I assume its probably a bit late in your your development cycle to change your integration layer, but otherwise I dont know of a way to get around marshalling your object(s) back into XML or separating them out into their primitive components.
For .NET implementations of AMF check out:
FlourineFX(FOSS)
WebORB for .NET
Its amazing how easy things become once AMF is used, for example using the Mate MVC framework and an AMF call passing a complex object to the server looks something like this:
<mate:RemoteObjectInvoker instance="yourWebservice" method="saveComplexObject" showBusyCursor="true" >
<mate:resultHandlers>
<mate:CallBack method="saveComplexObjectSuccess" arguments="{[resultObject]}" />
</mate:resultHandlers>
<mate:faultHandlers>
<mate:MethodInvoker generator="{DataManager}" method="presentFault" arguments="{fault}" />
</mate:faultHandlers>
</mate:RemoteObjectInvoker>
With result and fault handlers being optional.
The direction I ended up going was close to what I hoped was possible, but is "hack-ish" enough that I would consider SuperSaiyen's suggestion to use AMF/ORM a better solution for new/greenfield projects.
For sake of example/discussion, let's say I am working with a Person table in a database, and have a typed DataSet called PeopleDataSet that has PersonTableAdapter and PersonDataTable with it.
READ would look like this in .NET web service:
[WebMethod]
public PeopleDataSet.PersonDataTable GetAllPeople() {
var adapter = new PersonTableAdapter();
return adapter.GetData();
}
... which in Flex would give you a result Object that you can use like this:
// FLEX (AS3)
something.dataProvider = result.Tables.Person.Rows;
Check out the link I put in the question for more details on how Flex handles that.
CREATE/UPDATE - This is the part I had to figure out, and why I asked this question. The Flex first this time:
// FLEX (AS3)
var person:Object = {
PersonID: -1, // -1 for CREATE, actual ID for UPDATE
FirstName: "John",
LastName: "Smith",
BirthDate: "07/19/1983",
CreationDate: "1997-07-16T19:20+01:00" // need W3C DTF for Date WITH Time
};
_pplWebSvcInstance.SavePerson(person); // do the web method call
(For handling those W3C datetimes, see How to parse an ISO formatted date in Flex (AS3)?)
On the .NET web service side then, the trick was figuring out the correct Type on the web method's parameter. If you go with just Object, then step into a call with a debugger, you'll see .NET figures it is a XmlNode[]. Here is what I figured out to do:
[WebMethod]
public int SavePerson(PeopleDataSet p) {
// Now 'p' will be a PeopleDataSet with a Table called 'p' that has our data
// row(s) (just row, in this case) as string columns in random order.
// It WILL NOT WORK to use PeopleDataSet.PersonDataTable as the type for the
// parameter, that will always result in an empty table. That is why the
// LoadFlexDataTable utility method below is necessary.
var adapter = new PersonTableAdapter();
var tbl = new PeopleDataSet.PersonDataTable();
tbl.LoadFlexDataTable(p.Tables[0]); // see below
// the rest of this might be familiar territory for working with DataSets
PeopleDataSet.PersonRow row = tbl.FirstOrDefault();
if (row != null) {
if (row.PersonID > 0) { // doing UPDATE
row.AcceptChanges();
row.SetModified();
}
else { // doing CREATE
row.CreationDate = DateTime.UtcNow; // set defaults here
row.IsDeleted = false;
}
adapter.Update(row); // database call
return row.PersonID;
}
return -1;
}
Now, the kluge utility method you saw called above. I did it as extension method, that is optional:
// for getting the Un-Typed datatable Flex gives us into our Typed DataTable
public static void LoadFlexDataTable(this DataTable tbl, DataTable flexDataTable)
{
tbl.BeginLoadData();
tbl.Load(flexDataTable.CreateDataReader(), LoadOption.OverwriteChanges);
tbl.EndLoadData();
// Probably a bug, but all of the ampersand (&) in string columns will be
// unecessarily escaped (&) - kluge to fix it.
foreach (DataRow row in tbl.Rows)
{
row.SetAdded(); // default to "Added" state for each row
foreach (DataColumn col in tbl.Columns) // fix & to & on string columns
{
if (col.DataType == typeof(String) && !row.IsNull(col))
row[col] = (row[col] as string).Replace("&", "&");
}
}
}
Please be gentle, as I'm still new to web programming and -very- new to Ajax!
I've created a C# function which extracts data from an mssql database, formats it to a json string and returns that. Now I need to make a call from my javascript (jQuery) slider through an aspx page that is related to the C# code file.
I have actually never done anything like this before, from what I could tell by googling I need to use xmlHttpRequest, but how exactly do I make the function get hold of this string?
It would be awesome if someone had some example code that shows how this works.
The simplest way to do this is to convert your function into an ASHX file that writes the JSON to the HTTP response.
You can then call it using XmlHttpRequest, although you can call it much more easily using jQuery.
You can call it with jQuery like this:
$.get("/YourFile.ashx", function(obj) { ... }, "json");
It's relatively easy with jQuery if you mark the C# function as a [WebMethod] or make it part of a ASP.NET webservice. Both these techniques make it easy to have the response automatically converted into a JSON object by ASP.NET, which makes processing on the client easier (IMHO).
The example below is if the page has a WebMethod named GetData, but it's trivial to change the URL if you create a service.
$.ajax({ url: "somepage.aspx/GetData",
method: "POST", // post is safer, but could also be GET
data: {}, // any data (as a JSON object) you want to pass to the method
success: function() { alert('We did it!'); }
});
On the server:
[WebMethod]
public static object GetData() {
// query the data...
// return as an anonymous object, which ASP.NET converts to JSON
return new { result = ... };
}
I have constructed several web services that successfully serialize my .NET types into JSON. However, I'm running into an issue getting the JSON to be fully compatible with the YUI library and the DataTable in particular.
The YUI DataTable can be fully configured using JSON objects. I created the following struct in .NET to represent a given YUI column definition:
public struct YuiColumnDefinition{
public string key;
public string label;
public bool sortable;
public string formatter;
}
The 'formatter' property is how you instruct the YUI table to use a custom javascript function when displaying a given column. The problem is that, since formatter is defined as a String, ASP.NET wraps it's value in double quotes upon serialization, and YUI no longer recognizes the value as a JavaScript token:
JSON YUI expects
[
{key:"1", label:"Order Date", formatter:myCustomJavaScriptFunction, sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
]
JSON ASP.NET creates
[
{key:"1", label:"Order Date", formatter:"myCustomJavaScriptFunction", sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
]
Anyone have a solution outside of modifying the YUI source code?
Thanks
Change your parsing code.
Think of json as a replacement for xml, you wouldnt put a variable/function in xml. In either, you could easily identify the name or type (say from a list or enum) of the formatter to use. Then your parsing code would know that it should assign a variable/method as the "formatter" property.
Its just incorrect to return an actual variable/function in a callback like that. You could make it work but honestly its not the way to go.
I would do the following...
Change your return json to this.
[
{key:"1", label:"Order Date", formatterName:"myCustomJavaScriptFunction", sortable:true},
{key:"2", label:"Customer Name", formatterName:null, sortable:false}
]
Then in JS, lets assume that json is stored in variable returnedObj
function augmentReturnedObject(returnedObj)
{
// validate that returnObj.formatterName (as a variable) is not undefined
var isValidObj = (window[returnedObj.formatterName] !== undefined);
if (isValidObj)
{
// this will return the actual function / variable, here we are assigning it to returnedObj.formatter
returnedObj.formatter = window[returnedObj.formatterName];
}
else
{
returnedObj.formatter = null;
}
}
You could easily reduce that down to this without much thought
function augmentReturnedObject(returnedObj)
{
var specifiedMethod = window[returnedObj.formatterName];
returnedObj.formatter = (specifiedMethod === undefined) ? null : window[returnedObj.formatterName];
}
So in the end you'd take your json object, and do augmentReturnedObject(returnedObj); and at that point you can pass returnedObj to YUI
As Allen correctly pointed out:
Its just incorrect to return an actual variable/function in a callback like that.
Still, I couldn't find anywhere in the YUI documentation that spells out how to deal with javascript functions returned as strings from either JSON or XML.
Thank goodness for blogs. As pointed out in this one, the "proper" way to register custom javascript formatting functions is by using YAHOO.widget.DataTable.Formatter:
JSON Column Definitions as returned from .ASMX
[
{key:"1", label:"Order Date", formatter:"myCustomJavaScriptFunction1", sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
{key:"3", label:"State", formatter:"myCustomJavaScriptFunction2", sortable:false}
]
Javscript to wire up the YUI DataTable
YAHOO.widget.DataTable.Formatter.myCustomJavaScriptFunction1= this.myCustomJavaScriptFunction1;
YAHOO.widget.DataTable.Formatter.myCustomJavaScriptFunction2= this.myCustomJavaScriptFunction2;
function myCustomJavaScriptFunction1(elCell, oRecord, oColumn, oData) {
//do something
}
function myCustomJavaScriptFunction2(elCell, oRecord, oColumn, oData){
//do something
}
I have a list of error codes I need to reference, kinda like this:
Code / Error Message
A01 = whatever error
U01 = another error
U02 = yet another error type
I get the Code returned to me via a web service call and I need to display or get the readable error. So I need a function when passed a Code that returns the readable description. I was just going to do a select case but thought their might be a better way. What is the best way / most effieient way to do this?
Use a Dictionary, (in C#, but the concept and classes are the same):
// Initialize this once, and store it in the ASP.NET Cache.
Dictionary<String,String> errorCodes = new Dictionary<String,String>();
errorCodes.Add("A01", "Whatever Error");
errorCodes.Add("U01", "Another Error");
// And to get your error code:
string ErrCode = errorCodes[ErrorCodeFromWS];
You would use a dictionary. A dictionary uses a hashmap internally for performance, so it is good in that regard. Also, because you want this to go as quickly as possible by the sounds of it, I would statically initialize it in its own class instead of, for example, in an XML file or slimier. You would probably want something like:
public static class ErrorCodes
{
private static Dictonary<string, string> s_codes = new Dicontary<string, string>();
static ErrorCodes()
{
s_codes["code"] = "Description";
s_codes["code2"] = "Description2";
}
public static string GetDesc(string code)
{
return s_codes[code];
}
}
That way, if you wanted to move the back end to a file instead of being static, then you could.