ラベル http の投稿を表示しています。 すべての投稿を表示
ラベル http の投稿を表示しています。 すべての投稿を表示

2012年3月26日月曜日

ResponseFilter & Content-Encoding header, cleared by WriteExceptionJsonString

Hi There,

I have a ResponseFilter adding HTTP compression to my .aspx, ashx, and now .asmx requests, similar to http://blog.madskristensen.dk/post/HTTP-compression-in-ASPNET-20.aspx.

I've just starting using it to compress the web services in a project, which has worked great reducing the traffic immensely. To do this, I've added a response filter to the Response object, and set the "Content-Encoding" header to "gzip". Works great for normal pages and "MyService.asmx/js" requests as well.

Normally, when an exception is thrown inside a web service, the exception is serialised, passed along to the client, and handled by the error callback in javascript code. No worries there.

The error handling stopped working after added the compression as a response filter to the .asmx/js requests. It turns out that the method handles the error "WriteExceptionJsonString" found in "System.Web.Script.Services.RestHandler" clears the response headers before it serialises the exception. This isn't too bad in itself, but the response filter is still in place, so the gzip compression is still used, but now the client consuming the request doesn't know it needs decoding and a javascript error then occurs "result has no properties"

line 4860, result = new Sys.Net.WebServiceError(false, result.Message, result.StackTrace, result.ExceptionType);

Is there a way around this, while still allowing the compression? Or is this some kind behavioral bug in the underlying MS AJAX Rest handler? If theSystem.Web.Script.Services.RestHandler clears the headers... and the content... should it also remove any response filters?

I had a brain wave shortly after posting the previous message.

The HTTP Module I am using did all the Response.Filter setup in the PreRequestHandlerExecute event. That included the Response.AppendHeader("Content-Encoding", "gzip"). The header was subsequently cleared by the System.Web.Script.Services.RestHandler.WriteExceptionJsonString method, but the filter still compress the output stream.

So instead, I moved the Response.AppendHeader("Content-Encoding", "gzip") into the PreSendRequestHeaders event. Now the header gets set correctly for the Response.Filter GZipStream just before the headers are sent.

Hope that helps someone else.

2012年3月24日土曜日

Returning a datatable

Based on trying to return a DataSet, I have decided to try and justreturn a DataTable (fyi. http://forums.asp.net/1077805/ShowPost.aspx).
The problem is that in my javascript callback, I need to get at thedata that is returned. What methods are available on the returneddata? I have a datatable with several datarows, but I don't knowwhat methods are available to actually get at the data. BTW, I amtrying to do this programmatically and am not interested in adeclarative solution to this. When I attempt to alert out to theUI, result.length is coming through a undefined.
Web Service:
[WebMethod]
public System.Data.DataTable ReturnDataSet()
{
DataTable dt = new DataTable();
DataRow dr;
dt.Columns.Add(new DataColumn("tblStateId",System.Type.GetType("System.Int32")));
dt.Columns.Add(new DataColumn("State",System.Type.GetType("System.String")));
dr = dt.NewRow();
dr["tblStateId"] = 1;
dr["State"] = "Tennessee";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["tblStateId"] = 2;
dr["State"] = "Alabama";
dt.Rows.Add(dr);
return (dt);
}
Client Side Javascript:
function LoadTest(){
Samples.AspNet.WebServiceTest.ReturnDataSet(ReturnDataTableCallBack);
}

function ReturnDataTableCallBack(result)
{
var i = 0;
var ddl = document.getElementById("sState");
var optionItem;
iLength = document.getElementById("sState").options.length;
document.getElementById("sState").visible = true;
document.getElementById("txtAreaResult").value = result.get_data();
for(i=0; i<iLength; i++)
{
document.getElementById("sState").options[0] = null;
}

for(i=0; i<result.length; i++)
{
document.getElementById("sState").options.add(newOption(resultIdea [I]["State"],resultIdea [I]["tblStateId"]))
}
}


I think you should be able to write code like:
for (var i = 0; i < results.get_length(); i++) {
alert(results.getItem(i).State);
}
As a side note: there are several ways that you can use to 'discover'the structure of an object. One way is to usedebug.dump(object,name[,recursive[,indentationPadding]]), which shouldadd information about an object to the trace. Another way is to loopthrough the members of an object, like "for (m in myObject) alert(m);".

Great information. It got me on the right track. I foundthat the following pseudo-javascript was what was necessary:
for(i=0; i<result.get_length(); i++)
{
var optAdd = newOption(result.getItem(i).getProperty("State"),result.getItem(i).getProperty("tblStateId"));
document.getElementById("sState").options.add(optAdd);
}
The .State and .tblStateId properties did not return a value, but thegetProperty("State") and getProperty("tblStateId") did what wasnecessary.
Do you have a complete example of the debug.dump() method?
Wally

You could use "get_State()" instead of "getProperty('State')" too (which I think is nicer).
To use debug.dump, try something like "debug.dump(results, "my results", true)".

How would you do this declaratively? For example, if I wanted to bind the first state returned to a label...I've tried this, and it doesn't work:
<binding id="binding1" dataContext="dataSource1" dataPath="data[0].State" property="text" automatic="false" />
I can't figure out what to put in the dataPath. Anyone have some guidance?