View Full Version : How to set Content-Type on request header
dotnetCarpenter
03-25-2007, 04:29 PM
Hi all.
Does anyone know how to change the default request header when using the grid? Right now it is "application/x-www-form-urlencoded" which won't work with Windows Communication Fundation (Indigo) because it expects "application/json".
I use the classic yui-ext setup.
I've tried to change _default_post_header:'application/x-www-form-urlencoded' to _default_post_header:'application/json' in YAHOO.util.Connect in yui-uilities.js but that stopped it from working.
I also found this snippet of documentation on yahoos website:
setHeader
private static void setHeader ( o )
Accessor that sets the HTTP headers for each transaction.
Parameters:
o <object> The connection object for the transaction.
Returns: void
I have no idea of how to implement this into the existent Ext code.. What should I do?
Any ideas?
Thanks in advance!
Animal
03-26-2007, 04:29 AM
Do you mean "Content-Type"? Because in the grid's request for data, the content type will not be Json, but a standard form POST ("application/x-www-form-urlencoded" unless you change the method to "GET") It will in fact be a urlencoded string of name/value pairs.
You can if you must, set the Content-Type header using
YAHOO.util.Connect.setDefaultPostHeader("application/json");
But that would be wrong wouldn't it?
Do you mean the "Accept" header which specifies what kind of data the browser wants to see returned?
YAHOO.util.Connect.initHeader("Accept", "application/json");
I managed to get Ext working with Microsoft AJAX by creating my own proxy class.
I simply copied the HttpProxy, renamed it, and modified the load function as follows -
load : function(params, reader, callback, scope, arg){
if(this.fireEvent("beforeload", this, params) !== false){
YAHOO.util.Connect.setDefaultPostHeader(false);
YAHOO.util.Connect.initHeader("Content-Type", "application/json; charset=utf-8");
params = Ext.util.JSON.encode(params);
this.conn.request({
params : params,
request: {
callback : callback,
scope : scope,
arg : arg
},
reader: reader,
callback : this.loadResponse,
scope: this
});
}else{
callback.call(scope||this, null, arg, false);
}
},
The only difference to HttpProxy is that it sets the content type and encodes the parameters as JSON.
I also managed to get the grid to populate from a .net web service by creating another proxy class and modifying the loadResponse function as follows -
loadResponse : function(o, success, response){
if(!success){
this.fireEvent("loadexception", this, o, response);
o.request.callback.call(o.request.scope, null, o.request.arg, false);
return;
}
var result;
try {
var doc = response.responseXML;
var root = doc.documentElement || doc;
var json = eval("("+root.firstChild.nodeValue+")");
result = o.reader.readRecords(json);
}catch(e){
this.fireEvent("loadexception", this, o, response, e);
o.request.callback.call(o.request.scope, null, o.request.arg, false);
return;
}
o.request.callback.call(o.request.scope, result, o.request.arg, true);
},
The only difference to HttpProxy is in the try block - it simply extracts the JSON from the xml response.
I haven't used either of these in anger yet, so happy to get any feedback on these techniques.
I'm really excited about the possibilities of Ext - thanks Jack!
Animal
03-26-2007, 07:04 AM
Seems strange to embed Json inside XML! Is this some pre-existing stuff that you have to work with?
rodiniz
03-26-2007, 08:02 AM
Seems strange to embed Json inside XML! Is this some pre-existing stuff that you have to work with?
It seems that he is using .net WebServices.... and a .net Webservice will return a XML.
By default .net web services return XML, so if you return JSON it gets wrapped up in something like -
<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="http://myurl.com/">{ ... JSON ... }</string>
You can actually coax them to return JSON by adding a [ScriptService] attribute to the webservice, and [ScriptMethod] to each method.
This works perfectly if you are allowing Microsoft AJAX to serialise your arrays and objects. However if (like me) you want to perform custom JSON serialisation, it was adding additional quotes around the JSON I was trying to return :(
I did find a workaround to this - add [ScriptMethod(ResponseFormat=ResponseFormat.Xml)] attribute to the methods. This returns your response exactly as you format it, however it also sends a response header of application/xml (doesn't seem to cause any problems but I didn't really like this solution)
As I said, I'm still experimenting with the best ways to integrate Ext and MS AJAX.
Animal
03-26-2007, 08:33 AM
So why not just return XML and use an XmlReader?
TBH I hadn't looked at the XmlReader until now - I was able to change my examples to Xml pretty easily and it seems a much nicer approach.
Thanks for the suggestion Animal
dotnetCarpenter
03-27-2007, 10:20 AM
Thanks to all of you! Rab your load function work for me. Nice!
To clearify I use .NET 3.5 (it's not released yet). In my pursuit to find the best JSON intergration with .NET I've found the next edition of WCF (Windows Communication Foundation March CTP which contains C# 3.0, VB.NET 9, Linq and much much more). This WCF version ships with a JSON serializer/deserializer and is by far the easiest way of working with (any) AJAX libraries from .NET that I have seen. It works a bit different than normal ASP.NET webservices in that you define the bindings in web.config and let WCF take care of the rest. You still have to mark your objects for serialization but it's easy.
My problem now is that the grid is still not showing eventhough I can see the request/response is valid in Firebug and no errors shows up in the console. I'll try to dig in and see what I can find...
Animal
03-27-2007, 10:27 AM
It could be a mismatch between the XML and the Ext.data.Record's "mapping" property which is the DomQuery path to extract the data from the XML.
Or it could be a mistake in the ColumnModel, where the "dataIndex" property is used to access the Record's field by the "name" index.
dotnetCarpenter
03-27-2007, 11:10 AM
Thanks for the quick reply.
It could be a mismatch between the XML and the Ext.data.Record's "mapping" property which is the DomQuery path to extract the data from the XML.
Oppose to Rab I don't use XML but only JSON. My data looks like this (one record):
{"Events":[{"BookingPeriod":"1.1.06-1.1.06","EventID":8,"IsBookable":false,"MyNumberOfBookings":4,"MyNumberOfEvents":0,"Name":"Royal Golf vognen (El bil) (Med 1 Prime Chauffør)","NumberOfBookings":24,"NumberOfEvents":0}],"TotalCount":1}
I saw in the example data (http://www.yui-ext.com/forum2/topics-remote.php) that integers is also in quotes but don't know if it matters.
Or it could be a mistake in the ColumnModel, where the "dataIndex" property is used to access the Record's field by the "name" index.
I have given same name to the "name" and "mapping" property in my DataRecord - is that wrong?
As far as I can see from Firebug the proxy works but the data never gets from the proxy to the reader. Maybe it's an error in the JSON reader?
Here's my complete code, it is very close to the example code:
Ext.onReady(function(){
// create the data store
var ds = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: 'BookEventService.svc/BookEventEndpoint/GetEvents'
}),
// create reader
reader: new Ext.data.JsonReader({
root: 'Events',
totalProperty: 'totalCount',
id: 'EventID'
},[
{name: 'EventID', mapping: 'EventID', type: 'int'},
{name: 'Name', mapping: 'Name', type: 'string'},
{name: 'BookingPeriod', mapping: 'BookingPeriod', type: 'string'},
{name: 'NumberOfEvents', mapping: 'NumberOfEvents', type: 'int'},
{name: 'NumberOfBookings', mapping: 'NumberOfBookings', type: 'int'},
{name: 'MyNumberOfEvents', mapping: 'MyNumberOfEvents', type: 'int'},
{name: 'MyNumberOfBookings', mapping: 'MyNumberOfBookings', type: 'int'},
{name: 'IsBookable', mapping: 'IsBookable'}
]),
// turn off remote sorting
remoteSort: false
});
// the column model has information about grid columns
// dataIndex maps the column to the specific data field in
// the data store
var cm = new Ext.grid.ColumnModel([{
id: 'id',// id assigned so we can apply custom css (e.g. .x-grid-col-event b { color:#333 })
header: "Event",
dataIndex: 'EventID',
width: 420
},{
id: 'event',// id assigned so we can apply custom css (e.g. .x-grid-col-event b { color:#333 })
header: "Event",
dataIndex: 'Name',
width: 420
},{
header: "Kan bookes i perioden",
dataIndex: 'BookingPeriod',
width: 100
},{
header: "Samlet antal events",
dataIndex: 'NumberOfEvents',
width: 100
},{
header: "Samlet antal booket",
dataIndex: 'NumberOfBookings',
width: 100
},{
header: "Mit antal events",
dataIndex: 'MyNumberOfEvents',
width: 100
},{
header: "Mit antal booket",
dataIndex: 'MyNumberOfBookings',
width: 100
},{
header: "bookable",
dataIndex: 'IsBookable',
width: 100
}]);
// by default columns are sortable
cm.defaultSortable = true;
// create the editor grid
var grid = new Ext.grid.Grid('events-grid', {
ds: ds,
cm: cm,
selModel: new Ext.grid.RowSelectionModel({singleSelect:true}),
enableColLock:false
});
// make the grid resizable, do before render for better performance
var rz = new Ext.Resizable('events-grid', {
wrap:true,
minHeight:100,
pinned:true,
handles: 's'
});
rz.on('resize', grid.autoSize, grid);
// render it
grid.render();
// trigger the data store load
ds.load({params:{userid:1, max:10, skip: 0, showall: true}});
});
Animal
03-27-2007, 11:25 AM
The totalProperty in your Reader is wrong. The property in the JSON is "TotalCount", so you need:
totalProperty: 'TotalCount',
dotnetCarpenter
03-27-2007, 12:16 PM
Thanks! Sometimes it helps to have a second pair of eyes :-)
But it doesn't change anything. It looks like the library throws an error in Ext.data.Store.recordType. Not sure what happends though... I've tried to change all the JSON properties to strings (with quotes) like in the example (http://www.yui-ext.com/forum2/topics-remote.php) but in vain.
Can you tell me what the "type" is for boolean in a DataRecord and if there gonna be an easier way of changing the Content-Type in request headers in the beta release (I noticed you signature says Support-team, so I figure you might have some insight info)?
Cheers, Jon.
tryanDLS
03-27-2007, 01:26 PM
The type for a boolean mapping is 'bool'. If you're still having problems, you should debug thru the Store's load process to see exactly where you're failing. That code is very straightforward and it should be pretty easy to see what fails.
dotnetCarpenter
03-27-2007, 03:42 PM
Ok. I rand the debugger and found (BTW nice lib work) that firebug makes some very surprising jumps. Anyway it seems that everything is built up nicely but when the load fires everything goes to s!@§*...
Actually I don't know where it goes wrong. The parameters are passed to the server, the server respond, the JSON looks correct (I've posted it before in this thread) but the data property of Store is always empty. I don't know where exactly the "real" load operation happens. If just the code would fail somewhere but it doesn't.
Let me explain how I see it and then you can correct me where I'm wrong (the in-page code is posted earlier in this thread). The Store function accepts an object called "config", this contains two objects "proxy" and "reader" and a property, "remoteSort" which is set to false.
The proxy object contains an object called "conn" which has the url from which the data is collected from. This holds the correct uri. The reader object has one object, "meta". This contains the parameters I set in my in-page script, so I'm betting it's the Ext.data.JsonReader.
The parameters are root: 'Events', totalProperty: 'TotalCount' and id: 'EventID'. Aside from the meta object the JsonReader contains four functions: recordType, override, read and readRecords.
recordType is showed in red in Firebug since I started my debug adventure (I have noted this in a earlier post).
Now if I look into the recordType I can see that it contains eight undefined "fields" that looks like composite objects. The weird thing is that I can expand these undefined objects and see that they contains a lot of information.
I can see an Array called "keys" that holds the names/mappings from the in-page code also an Array of objects called "items" containing name, mapping, type, sortDir etc.
If I turn back to data.Store it nows has all the keys and items from the JsonReader. The same happens for grid.ColumnModel, so everything looks good to go.
When the browser reach ds.load({params:{userid:1, max:10, skip: 0, showall: true}}); this happens:
The code jumps to Ext.extend(Ext.data.Store, Ext.util.Observable, { and exits on this.proxy.load(p, this.reader, this.loadRecords, this, options);
it then jumps to Ext.extend(Ext.data.HttpProxy, Ext.data.DataProxy, { which runs this slightly modified code snippet:
load : function(params, reader, callback, scope, arg){
if(this.fireEvent("beforeload", this, params) !== false){
YAHOO.util.Connect.setDefaultPostHeader(false);
YAHOO.util.Connect.initHeader("Content-Type", "application/json; charset=utf-8");
params = Ext.util.JSON.encode(params);
this.conn.request({
params : params || {},
request: {
callback : callback,
scope : scope,
arg : arg
},
reader: reader,
callback : this.loadResponse,
scope: this
});
}else{
callback.call(scope||this, null, arg, false);
}
}
What's really weird is that both blocks of code appear to get executed?!? Maybe firebug can't track the caller and this code is called async? I think there is a bug in the recordType code but I can't figure out what.. Any ideas?? I also noted that in the example code there is () around the entire JSON dataset which there is not around mine. Could that be the error source?
I'm starting to get under pressure to make this work so I really hope someone out there could point me in the rigth direction. Thanks in advance!
dotnetCarpenter
03-27-2007, 04:22 PM
Ok. I've checked if it made a differents if I put the JSON data in () by putting it around the response:
9873 Ext.extend(Ext.data.JsonReader, Ext.data.DataReader, {
9874 read : function(response){
9875 var json = '(' + response.responseText + ')'; // here I put an extra pair of ()
9876 var o = eval("("+json+")");
9877 if(!o) {
9878 throw {message: "JsonReader.read: Json object not found"};
9879 }
9880 return this.readRecords(o);
The response look as expected BTW. The above code is called by this:
9733 loadResponse : function(o, success, response){
9734 if(!success){
9735 this.fireEvent("loadexception", this, o, response);
9736 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9737 return;
9738 }
9739 var result;
9740 try {
9741 result = o.reader.read(response); // the above code is called by this line
9742 }catch(e){
9743 this.fireEvent("loadexception", this, o, response, e);
9744 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9745 return;
9746 }
9747 o.request.callback.call(o.request.scope, result, o.request.arg, true);
Unfortuantely it didn't make any difference...
dotnetCarpenter
03-27-2007, 05:03 PM
I'm sorry.. Maybe it's true that you can't see the forest for the trees! I've been looking the wrong place. It works I just hadn't added the css/images that makes it visible. I owe you all many thanks! Now I can go home and sleep :-)
Maybe this will teach me. hehe
Regards, Jon.
vBulletin® v3.8.4, Copyright ©2000-2010, Jelsoft Enterprises Ltd.