.NET (295) administrative (41) Ajax (42) AngularJS (2) ASP.NET (144) bicycle (2) books (180) browser (8) C# (133) cars (1) chess (28) CodePlex (10) Coma (8) database (47) deployment (3) Entity Framework (2) essay (110) flash/shockwave (2) flex (1) food (3) friend (2) game (20) idea (5) IIS (8) javascript (82) LInQ (2) Linux (6) management (4) manga (42) misc (670) mobile (1) movies (90) MsAccess (1) murder (2) music (64) mysql (1) news (99) permanent (1) personal (68) PHP (1) physics (2) picture (307) places (12) politics (13) programming (503) rant (120) religion (3) science (43) Sharepoint (3) software (58) space (1) T4 (2) technology (11) Test Driven Development (4) translation (2) VB (2) video (97) Visual Studio (44) web design (46) Windows API (8) Windows Forms (3) Windows Server (5) WPF/Silverlight (63) XML (11)

Monday, February 19, 2007

ASP.Net Ajax and Response.Write or response filters The message received from the server could not be parsed.

Update: If you've experienced random PageRequestManagerParserErrorException errors that seem to vanish at a simple page refresh, read this post instead: An intermittent PageRequestManagerParserErrorException

I've built a TranslationFilter object that tries to.. well... translate ASP.Net pages. Everything ran smoothly until I had to use ASP.Net Ajax. I got the infamous error "Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.".

Starting analysing the problem, I soon understood that the Ajax requests go through the same Response mechanism as a normal page request, but the output is different. In case of normal page requests you get the HTML of the page, while in Ajax requests you get something formated like this:
contentLength|controlType|controlID|content|


If one uses Response.Write, the text is inserted both in the page HTML and the Ajax request format, resulting in something like "blablabla10|updatePanel|UpdatePanel1|0123456789" which cannot be parsed correctly and results in an error. The ScriptManager.IsInAsyncPostBack property shows us if the request is Ajax or not, so we can condition the Response.Write on this.

Also, if changing the content with a HttpResponse.Filter, the length of the content is no longer equal with the declared value. So what must be done is first detect if the content is Ajax. Unfortunately we cannot check the state of the ScriptManager from inside the HttpResponse.Filter, but we can check the format of the string to modify, then modify the content AND the contentLength, else it will all result in error.

Update: the content might not be changed by you! As one of the people asking me for help on the chat discovered, the web server provider might want to put in some ads, regardless if the request is an Ajax one, thus breaking the format. You need to patch the javascript ajax engine in order to work, that means changing the content the javascript function will get in order to not cause errors. You may find the solution here.

As an example, here is my Translate method:
        private string RecursiveTranslateAjax(string content)
        {
            Regex reg = new Regex(@"^(\d+)\|[^\|]*\|[^\|]*\|",
                         RegexOptions.Singleline);
            Match m = reg.Match(content);
            if (m.Success)
            {
                int length = To.Int(m.Groups[1]);
                reg = new Regex(
                         @"^(\d+)(\|[^\|]*\|[^\|]*\|)(.{" + length + @"})\|"
                         , RegexOptions.Singleline);
                m = reg.Match(content);
                if (m.Success)
                {
                    string trans = Translate(m.Groups[3].Value);
                    return trans.Length + m.Groups[2].Value 
                       + trans + "|"
                       + RecursiveTranslateAjax(content.Substring(m.Length));
                }
            }
            return Translate(content);
        }


Update:
I met this problem also when in the page there were Unicode characters. Everything works perfectly, then you can't postback anything, because some user text contains Unicode chars. The solution I used for this was to get the offending text (whether in Page.Render or in some other places based on specific situations) and take every character and check if it is ASCII. Web Pages should be UTF8 so any character bigger than 127 should be translated into a web page Unicode char &#[char code];

The code:

string s=[my string]
StringBuilder sb=new StringBuilder();
for (int c=0; c<s.Length; c++)
{
if (s[c]>127) sb.Append("&#"+((int)s[c])+";");
else sb.Append(s[c]);
}
s=sb.ToString();


Here is the full code
    private string RecursiveTranslateAjax(string content)
    {
        // look for the basic Ajax response syntax
        Regex reg = new Regex(@"^(\d+)\|[^\|]*\|[^\|]*\|", 
              RegexOptions.Singleline);
        Match m = reg.Match(content);
        // if found, search deeper, by taking 
        // into account the length of the html text
        if (m.Success)
        {
            // custom method to get an integer value
            int length = To.Int(m.Groups[1]); 
            reg = new Regex(@"^(\d+)(\|[^\|]*\|[^\|]*\|)(.{" + length + @"})\|",
                  RegexOptions.Singleline);
            m = reg.Match(content);
            if (m.Success)
            {
                string trans = Translate(m.Groups[3].Value);
                return
                    trans.Length + m.Groups[2].Value + 
                    trans + "|" + 
                    RecursiveTranslateAjax(content.Substring(m.Length));
            }
        }
        // if not Ajax, just translate everything,
        // it must be a normal PostBack or a string of some sort.
        return Translate(content);
    }
 
    // this method only fixes the weird characters
    // but you can put here any string change you would like
    // like search and replace some words.
    private string Translate(string content)
    {
        // Html code all chars that are not ASCII, thus getting rid of strange or Unicode characters
        StringBuilder sb = new StringBuilder();
        for (int c = 0; c < content.Length; c++)
        {
            if (content[c] > 127) sb.Append("&#" + ((int) content[c]) + ";");
            else sb.Append(content[c]);
        }
        return sb.ToString();
    }
 
    protected override void Render(HtmlTextWriter writer)
    {
        //base.Render(writer);
        // render to my own text writer
        HtmlTextWriter tw=new HtmlTextWriter(new StringWriter());
        base.Render(tw);
        // get the Rendered content of the page
        string content = tw.InnerWriter.ToString();
        content = RecursiveTranslateAjax(content);
        writer.Write(content);
    }


To.Int method
public static int Int(object o)
{
    if (o == null) return 0;
    if (IsNumericVariable(o)) return (int) CastDouble(o);
    string s = o.ToString();
    if (s == "") return 0;
    Match m = Regex.Match(s, "(-\\d+|\\d+)");
    if (m.Success)
        try
        {
            return Int32.Parse(m.Groups[0].Value);
        }
        catch
        {
        }
    return 0;
}
private static double CastDouble(object o)
{
    if (o is byte) return (byte) o;
    if (o is int) return (int) o;
    if (o is long) return (long) o;
    if (o is float) return (float) o;
    if (o is double) return (double) o;
    if (o is decimal) return (double) (decimal) o;
    throw new ArgumentException("Type is not convertable to double: " + o.GetType().FullName);
}

61 comments:

Anonymous said...

You are the real techie...

rospy said...

awesome... I think a lot of developers banged their heads to find a solution on this

thank you very much

Anonymous said...

This is real techie. So where would I implement this method? Can any one clearify this?

Siderite said...

Use this whenever you need to alter the Response of pages that use ASP.NET Ajax. Like when you need to use Response.Filter. Or Response.Write (although I can't imagine why you would).

Aqw3R said...

It works! :] Thx, man. Good job.

Mike said...

I'm trying to use this for some of my code, however, I'm trying to figure out what you are doing with To.Int() function, and why not use the .length property on the group[1]?

Thanks,
Mike

Siderite said...

It's about the format of the Ajax response: contentLength|controlType|controlID|content|

First I am searching for the first three strings (and I am using To.Int, a custom object method, to transform string into int to find the contentLength), then I am searching for the first three strings plus a content of that exact length. Otherwise I might just find three strings separated by | that have no connection with the Ajax Response format.

toto said...

hello,
thanks for your idea. But can you give us un example how to use your translate methode to avoid exception problem
near page_load or in others place

Anonymous said...

I don't have any response.write() in my code. i m just using an update panel to refresh dropdownlist from database.

I want to know where i can use your function and what's content parameter

un example will be good idea
thank's

Siderite said...

The content parameter represents the rendered content of the page. I will update the text of the article to also show an overridden Render that uses the function.

Anonymous said...

Can you post your To.Int() method?

Siderite said...

Ok, I've posted it at the end of the article. It's not the best possible method, though.

Anonymous said...

I had to use Response.Write because the erstwhile ASP.net 1.1 used it to export an excel sheet...so when I used your code snippet to render the control of the GridView data in an Excelsheet it still gave the issue.

If I make the button which cuases the asych postback into a Postbakc trigger then there is no issue but if I retains its Aysnch behaviour the browser craps out .

Any help here?

AC

Siderite said...

Well, you can't export an Excel in Ajax. There is no way to do it, as you want to change the headers of the response, then send a formatted text that can be interpreted by Excel, like a table.
And as Ajax Responses are in the format detailed above in the article, you can't do that.
Not to mention that an Ajax PostBack only sends some information to a javascript function that then changes the page, so there is no download of any kind, the page doesn't change.

Anonymous said...

Thanks for the update, mate. Well I guess I have to head to the drawing board to review th decision of going with AJAX.

Do you have any idea of how we could go wherein we need to deliver via a browser download of an excelsheet created on the fly but since the data retrieval may take time communicating with the end user about processing going on[This was the real impetus in going for AJAX]

Once again thanks,
AC

Siderite said...

Ajax is good, but not for the Excel thing. You can have the entire site in Ajax, but the page that renders the Excel as a normal page. Then you can use a hyperlink that opens that page or a javascript that opens a window with that page.

Ajax is normally used for partial rendering. I can see no scenario in which you have a page and you want to render excel formatted data in some part of it.

If the creation of the excel file takes some time, then you can use Ajax to show an intermediary page, so you get the UpdateProgress working and everything and in that page you open the new window I was talking about with Javascript.

Ben said...

Ahhh, but it is not impossible to write out an excel file or a jpg or whatever you may want to write back to the user..

It is impossible to simply response.binarywrite from the event click, yes, but not impossible to make it seem to the user that they are getting the excel file from the modal pop-up.. I have several pages that do just this.

Heres how to do it. In your server side method for your modal_buttonClick put the file that you want in a session variable, put it in a db, store it on the server, whatever you want, but you need to store it so you can get it back, once that is done Response.write back to yourself with some querystring or other session variable set so you can check it in the page load and react off of it.

Once you capture in your page load that this is your special re-direct response.write your excel file as you normally would..

The user is presented with the save dialog and it appears as if you never moved off that page.. You can still click and navigate in your modal..

Try it if you don't believe me :)

Ben said...

My apologies.. I didn't mean response.write back to yourself.. I meant response.REDIRECT back to yourself.

Siderite said...

It is obviously possible, the thing is that it would not be Ajax, just opening a new window.

Anonymous said...

I follow your code, but still meet error...
Can you post your IsNumericVariable() method?

Is this correct if I wrote it:

bool IsNumericVariable(object o)
{
return (o is byte || o is int || o is long || o is float || o is double || o is decimal);
}

Anonymous said...

Sorry, it works now. Thanks a lot :)

Anonymous said...

You are a lifesaver! This code worked great.

Anonymous said...

hi,

Your solution just works when response content of Ajax is small. If return content is large, the return content will be splited into many pieces like this:

Content piece 1:

4596|label|lblTextLong|this is a text, a very long text content. this is a text, a very long text content

Content piece 2:

this is a test, a very long text content. this is a test, a very long text content. this is a test, a very long text content... |

In this case, your parser to filter will meet the error (!?)

So, Is there any changes to avoid this case?

Thanks.

Siderite said...

Am I to understand that Ajax is splitting the content in more pieces when the data is long? I have tried it with 23Mb pages. Are you sure? I have never encountered such a behaviour before.

Anonymous said...

can u tell me where to use the code
infact iam having response.write in else path of button clickevent.i dont know where to use the code
pls help me

Siderite said...

Heh, in your case, the solution is to remove the Response.Write.

In a normal page use a Label or a LiteralControl and change its Text property instead of using Response.Write.

If you intend to export a PDF or to send some other type of binary or custom formatted content to the user, then you can't do it in Ajax. You need a regular postback. Make a page that specifically sends your custom response and then use a Response.Redirect to that page, even from an Ajax call.

The code above is to allow you to change the output of the Page Render method, even during ajax calls.

baljeet said...

As i read in ur discussion that we cant export to excel by using ajax. Actually i am using the same scenerio. By clicking on a link button i have to export the data of datalist to excel sheet. I am using the ajax on this page. I am getting the error if i put the link button inside the update panel. When i put that link button outside the update panel and datalist is still inside the updatepanel so error occurs. Can u help me out some way why its happening like this and how your code can help me out??

Siderite said...

There are three ways of achieving your desired result:
1. put the button outside ANY updatepanel
2. set the button as a postback trigger in the Triggers section of ScriptManager
3. programatically use the instance method ScriptManager.RegisterPostBackControl on your control.

snakebyte said...

An easy way to achieve this is to:

1) do all the processing you need to do to create your byte[]
2) put that into a session variable
3) use a line similar to below in the line after you’ve saved your session variable

ScriptManager.RegisterStartupScript(Page, GetType(), "MyScript", "window.open('MyBinaryWritePage.aspx');", true);


5) Create “MyBinaryWritePage.aspx” to simply read this session variable and Binary.Write the contents to the session variable, clearing out the session variable when it’s done.

The only downside is it acts like a pop-up window and you have to use session variables, but if your byte[] isn’t too big, it’s not a problem.

Siderite said...

Why not do a Response.Redirect("MyBinaryWritePage.aspx")? It has the exact same effect. Unfortunately, it gets blocked by any popup blocker. What I normally use in this situation is provide a hyperlink to the same page, but with an export excel parameter in the URL.

snakebyte said...

Response.Redirect will make you leave the page you're on. Typically, you want to stay on the page that made the initial request and pop up the output into an attachment or open into a new page. Besides that, the new request will wipe out any ViewState you're working with and any initialization you may have performed for that page.

Using a hyperlink will cause the user to click twice to get the output. The method above will be one post back, you keep your page, viewstate and initialization and AJAX doesn't throw a fit.

Siderite said...

Well, I've used Response.Redirect succesfully many times and it didn't destroy any state in the parent page. That is because the page I would redirect to would set a response header of application/bin or some other thing like that and the browser would open it as a download, not as a new page.

I did encounter one problem, though, during Ajax postbacks, because the UpdateProgress control would appear and then freeze on the page, even with animated gifs. A forced normal postback would fix that, but then the anti-popup would block it.

Siderite said...

Oh, and clicking on the parent page afterwards would work, as proof that the viewstate is not lost, even when persisted in the Session.

snakebyte said...

I see what you're doing. Yes, the redirect also works, but like you said, any animation you have on your update panel doesn’t know the page comes back and just hangs. Did you find a way around this besides forcing another post back and getting caught in the pop-up blocker?

The method I provided above doesn’t cause any of these problems. However, the problem I’m facing now seems to be caused by our proxy. As I stated above, it does get caught by a pop-up blocker, but when I’m running the code in the DMZ, the only way I can get it to render is to hold down the Ctrl key, even after trusting the page.

Siderite said...

No, no solution, but then I didn't try the most weird html scams like opening the download page in an iframe or other stuff like that.

My solution (and the widely solution adopted on the Internet it seems) is to redirect anyway, popup blockers be damned, and also provide a "if the download doesn't start..." link.

What I don't understand is why do you persevere in sending the download like this. I mean, if the user clicks something, then it can just as well be a hyperlink and not a button. If they don't click, why send them a file?

snakebyte said...

I’m using link buttons, contained in any number of rows in a grid view. I don’t initiate a download unless they click on a link button.

I don’t want the user to have to be redirected to a new page each time they want to view a file in the grid view. On top of that, not only are these files stored as BLOBs in a database, I don’t have access to the database, I need to call a separate 3rd-party process to feed me back each file as a byte array. The link button could request 1 or several files, resulting in multiple calls to this 3rd party process.

When the user clicks the link button, I need to decided what needs to be streamed back to the user, bundle the files into a single byte array and present them back as a single file download without forcing the user to leave the page.

Siderite said...

I completely understand that. But your link would look like "Download.aspx?name=..." or "?nameCode=FH44jdd3D". In the code you would set the header as application/binary or anything like that and the page would not be opened like a page, but like a download. Also, being a hyperlink, the problem with the popup will not occur anymore.

Lee Baker said...

This code was a life saver. Thanks heaps for sharing.

Bharat Jadhav said...

The solution to exporting data from an AJAX page to an Excel spreadsheet is posted on this page:
http://forums.asp.net/t/1047851.aspx

Siderite said...

Then it isn't Ajax, it's a regular postback.

Bharat Jadhav said...

Agreed, it is a regular postback. But the button being used to export data to an Excel file and the Gridview being exported to the Excel file are placed inside an Update Panel. Forcing the button within the update panel to cause a full postback will eliminate the PageRequestManagerParserErrorException error. Moreover, only that control within the Update Panel will cause a full postback.

So, you are right - technically we are forcing the control within the UpdatePanel to display un-AJAX like behaviour, but in my opinion, it is one of the easier solutions of exporting data from the page/UpdatePanel to an excel file without transferring control to another page.

Zuberi said...

I am getting the same error but not all the time? I am failed to understand how to overcome this error, actually you gave the solution in C language and I didn't understand it, can you please give the same solution for VB.NET developers ... That will be nice. Thanks in Advance

MIZ

Zuberi said...

Please give the same solution for VB.NET developers.

Thanks in advance

MIZ

Troner said...

Here is the easiest solution. I initially used Response.Write(). My problem was how to close the window and I was encountering the nagging error message about not being able to parse.

Before I used:

Response.Write("[script language='text/javascript']{window.opener=this;window.close(0);}[/script]")

Note that in writing [script] and [/script] above, I should be using < and > instead of [ and ], but the comments wouldn't go through.

This time, I simply used:
MainPanel.ResponseScripts.Add("{window.opener=this;window.close(0);}")

where MainPanel is a RadAjaxPanel in my page.

And it worked!

Here is where I got the solution:

http://www.telerik.com/community/forums/aspnet/ajax/response-write-is-not-working-in-ajax-page.aspx

Ramesh Chatakolla said...

This is an excellent article. Helped me a lot.

Anonymous said...

Hi,
I am getting the same ajax error. After debugging I came to know that the request string is the same before and after implimenting the code.
but I still get an error.
Please suggest.

Siderite said...

Use the chat to contact me when I am online. There are several ways you can get that error.

The easiest way to debug is to intercept the Render method of the page in question and , in Debug Mode, get a string with what is rendered. Examining that string usually gives you the answer.

c said...

Hi Siderite,

Using your code I am trying to refresh page(label with a confirmation) in an update panel on a page which uses a master page. The click event within the update panel fires and code executes (updates the db), but the label is not refreshed. Any help much appreciated

- Chex691

Sudev Gandhi said...
This comment has been removed by the author.
Sudev Gandhi said...

excellent post...

In my application I've to post large amount of data... and the responce is also large.

I tried with DEFLATE/GZIP filters but MS AJAX was not ready to accept those encoding... so I had to mannually trim the unnecessary whitespace from the response to reduce the size of the trip.

This article gave me the correct direction how to modify the AJAX response.

Thanks A Lot.

Siderite said...

I find it strange that Ajax is not GZipped by the web server. After all, they are regular web requests from its standpoint.

Ümit said...
This comment has been removed by the author.
Ümit Akkaya said...

Very good article thanks.

I want to ask something on example.

I'm sending MessageID to DeleteMessage.ashx named Generic handler using Javascript Ajax, I check the message and send back Response.Write("True"); to update GridView using UpdatePanel, if the value which is came back from Handler isn't "True" i dont update GridView.

When i refresh UpdatePanel using that javascript code.

__doPostBack('ctl00_ContentPlaceHolder1_UpdatePanel1', '');

But its sending that error.

Is your code will also work for my problem?

Siderite said...

The way you describe it, there is a page that returns True/False and you are accessing it using ajax. Based on the response there, you force a postback which then throws the error.

I would first remove the ajax call and use a hardcoded true/false value. If the error is still there, it is not related to the ajax call. If the error dissappears, I must say it must be because of the way you perform the ajax call, mechanism not described in your request. Good luck!

Ümit Akkaya said...

I think there is no problem with Ajax call script because i use it on another page(this page didnt have UrlRewriting rule) and when i make same things UpdatePanel working.

I make UrlRewriting rule on the page which is throwing error and i dont use the another page. I meet this error after using UrlRewrite.


Can you told me how UrlRewrite rule affect Response?

Siderite said...

I haven't met with this problem myself, but I found this link that seems to address your issue: http://forums.iis.net/t/1161674.aspx

Ümit Akkaya said...
This comment has been removed by the author.
Ümit Akkaya said...
This comment has been removed by the author.
Ümit Akkaya said...

I found what is causing the problem.

URLRewriter rewrites url
For example
http://localhost:***/ShowCategory.aspx?CatID=75

to

http://localhost:***/asp-net-4_75


and UpdatePanel using this URL to response
http://localhost:***/asp-net-4_75

and i cant found the solution for my UrlRewriting dll.

So;
I decided to use IIS UrlRewriter. I found the solution in the link which is you send in comment above for same problem on IIS.

Thank you :)

Anonymous said...

Hi,
thanks for your post I am new here.
I just don't understand where to post this code?
I post it in cs file but it showing error on To.int method and IsNumericvariable() giving error ..


plz help.

Marbella Consulting said...

This really helped me fix a problem that has been bugging me for months, thanks a million