惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
J
Java Code Geeks
雷峰网
雷峰网
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
云风的 BLOG
云风的 BLOG
V
Visual Studio Blog
V
Vulnerabilities – Threatpost
S
Securelist
The Hacker News
The Hacker News
The Register - Security
The Register - Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Help Net Security
Help Net Security
G
Google Developers Blog
Hugging Face - Blog
Hugging Face - Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
M
MIT News - Artificial intelligence
AI
AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The GitHub Blog
The GitHub Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Schneier on Security
Schneier on Security
N
Netflix TechBlog - Medium
T
The Blog of Author Tim Ferriss
Google DeepMind News
Google DeepMind News
Hacker News - Newest:
Hacker News - Newest: "LLM"
H
Hacker News: Front Page
博客园 - 司徒正美
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
B
Blog
Microsoft Azure Blog
Microsoft Azure Blog
大猫的无限游戏
大猫的无限游戏
Security Latest
Security Latest
Engineering at Meta
Engineering at Meta
N
News and Events Feed by Topic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Threat Research - Cisco Blogs
U
Unit 42
V
V2EX
V2EX - 技术
V2EX - 技术
L
LINUX DO - 最新话题
aimingoo的专栏
aimingoo的专栏
Microsoft Security Blog
Microsoft Security Blog
Recorded Future
Recorded Future
P
Privacy & Cybersecurity Law Blog
美团技术团队
小众软件
小众软件
F
Fortinet All Blogs

博客园 - Angelo Dell'inferno

[ASP.NET] 基于.Net Remoting 的网站访问监控模块 [ASP.NET] HyperLink + Image 实现动态图片链接 [ASP.NET] (转)地址栏参数的判断 [ASP.NET] 实现Label自动换行 [ASP.NET] 验证码生成 [ASP.NET] 实现客户端浏览服务端目录的页面 [ASP.NET]Treeview 控件显示服务端目录文件夹及文件 [C#] GridView导出到Excel [C#] 获取两个时间点的时间间隔 [SQL Server][转]数据库并发控制——活锁&死锁 [JavaScript] 防止页面被嵌入Iframe [SQL Server] 存储过程事务 [SQL Server] T-SQL 连接数据库方法 [C#] 杀Excel进程 [ASP.NET] (原创)自定义GridView分页 [C#] 将DataSet内容导入到Excel (矩阵区域导出) [ASP.NET] 服务器端下载文件实现 [Oracle] 日期相关操作 [ORACLE] 函数大全
[转]Run a Http Response Filter together with an Ajax Update Panel
Angelo Dell'inferno · 2009-02-17 · via 博客园 - Angelo Dell'inferno

quote:

Erik Lenaerts
(http://www.dotnet6.com/blogs/erik_lenaerts/archive/2007/07/21/run-a-http-response-filter-together-with-an-ajax-update-panel.aspx)


Run a Http Response Filter together with an Ajax Update Panel

This post continues from part one. In this previous post we created a Http Response Filter that translated content based on a Token replacement technique.

In this article we'll discuss how to apply this technique when you are using an Ajax Update Panel. The problem with the update panel is that the XmlHttp request made to the server is responded with partial html fragments. Nothing wrong with that in essence, however, our Http Response Filter from part one requires a full html document, more exactly it looks for the end html tag (</html>) to know when all of the content has been received. 

Ajax update panel Specifics

Requests made by Ajax for the Updatepanel are a bit different than regular requests more exactly:

  • They are of content-type "text/plain"
  • They are requested with a Request Header "x-microsoftajax"

Also the responses server by the Ajax server side code are different:

  • They are not wrapped in <html> tags because they are partial html fragments
  • They contain a 'special header' (read here and here for very useful info).
  • They contain two parts, one part with the content and another one containing things like the viewstate .

Mmh, so if we don't have any end html tag, how will we know when we have received all of our content? Well the answer lies in the "special header" which looks like :

Header: Length + ‘|’ + type + ‘|’ + id + ‘|’

Body:     html content 

Footer: ‘|’

The first part in this header contains the length of the body so that's really interesting. As you might recall from the previous part, ASP.Net serves our data in chunks of +-28K by calling the write method several times. So, we'll keep on collecting the data until we have received as much as bytes as specified in the length.

If you intercept the response of an Update panel by using for example Fiddler, you'll notice that besides the content there's also a second part that contains data like the viewstate. This is surely not something you want to translate. So, our goal is to only process part one "the content".

So, after all of the content has been collected, we can translate the tokens. However, when changing the content, the length of it will change as well. So, we need to recalculate the new length and set it accordingly into the special header.

The filter

Knowing all of this, we can start by creating our filter.

    /// <summary>
    /// The <c>AjaxTranslationFilter</c> class  
    /// </summary>
    
public class AjaxTranslationFilter HttpFilter
    { 
        private StringBuilder _responseHtml;
        
private int _contentLength 0;
        
private bool _partOne = true

        
/// <summary>
        /// Initializes a new instance of the <see cref="AjaxTranslationFilter"/> class.
        /// </summary>
        /// <param name="stream">The stream on which the filter will work.</param>
        
public AjaxTranslationFilter(Stream stream)
            : 
base(stream)
        
{
        } 
        /// <summary>
        /// When overridden in a derived class, writes a sequence of bytes to the current stream and advances 
        /// the current position within this stream by the number of bytes written.
        /// </summary>
        /// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
        /// <param name="count">The number of bytes to be written to the current stream.</param> 
        
public override void Write(byte[] bufferint offsetint count)
        
{
            
string newContent = null; // Note that this method is potentially called several times by ASP.NET
            // The buffer is not written at once, but depending on the size, in blocks of 27~29 Kbytes
            //
            // Since this is an Ajax call, we'll find the total number of bytes in the 'special' Ajax header
            //
            // Info
            // * http://weblogs.asp.net/leftslipper/archive/2007/02/26/sys-webforms-pagerequestmanagerparsererrorexception-what-it-is-and-how-to-avoid-it.aspx
            // * http://www.manuelabadia.com/blog/SyndicationService.asmx/GetRssCategory?categoryName=Ajax 
            // get buffer content
            
string strBuffer System.Text.UTF8Encoding.UTF8.GetString(bufferoffsetcount); // This filter is called in two parts but only the first part contains content that could be translated
            
if (_partOne)
            
{
                
// determine the content length during first run
                
if (_contentLength == 0)
                
{
                    
// Check for a valid Ajax header
                    
Regex regEx = new Regex(@"^(?<length>\d+)\|[^\|]*\|[^\|]*\|"RegexOptions.Singleline);
                    
Match m regEx.Match(strBuffer);
                    
if (m.Success)
                    
{
                        
// Read the length  
                        Group group 
m.Groups["length"];
                        
_contentLength Convert.ToInt32(group.Value); // initialise the StringBuilder (we  assume that translations increase
                        // the size by 20%
                        
_responseHtml = new StringBuilder((int)(_contentLength 1.2)); }
                    
else
                        throw new 
SystemException("Unable to parse content length from Ajax header");
                
} // Add buffer to total buffer
                
_responseHtml.Append(strBuffer); // Is all data received?
                
if (_responseHtml.Length >= _contentLength)
                
{
                    
//we have received all the data by now, so we can translate the content 
                 

                    
string ajaxContent _responseHtml.ToString(); // Translate the tokens in the html 
                    
string translatedContent TranslateContent(ajaxContent); // Calculate new content length 
                    
int newContentLength translatedContent.Length - (_responseHtml.Length _contentLength);

                    // Set new content length  
                    Regex regex2 = new Regex(@"^(?<length>\d+)(?<rest>\|[^\|]*\|[^\|]*\|)"RegexOptions.Singleline);
                   

newContent regEx2.Replace(translatedContentnewContentLength "${rest}"); 

                    
if (translatedContent != null)
                    
{
                        
byte[] data System.Text.UTF8Encoding.UTF8.GetBytes(newContent); // Write to the stream
                        
BaseStream.Write(data0data.Length);
                    
}

                    _partOne 

= false;
                
}
            }
            
else
            
{
                
// After the first part has been processed, just forward the other content to the browser.
                // this can also occur in multiple times if this 'rest'-data is totally larger than +-28K
                
BaseStream.Write(bufferoffsetcount);
            
}

        } 
    }

When to register the Filter

We know that the content contains 2 parts. The first part where the actual content is provided and in the second part stuff like viewstate and client side event binding is produced. As mentioned, for our translations, we are only interested in the first part.

If we look at the points in time when these two parts are rendered by Ajax then we can see the following order:

PreRequestHandlerExecute
- - Part One
PostRequestHandlerExecute
ReleaseRequestState
- - Part two

So I figured, I just unregister the filter on the PostRequestHandlerExecute event, but I didn't found any way to do that. Because of this, I had to deal with the concept of part one and two in my filter code.

As opposed to HtmlTranslationFilter of my previous article we can't register the Filter on the ReleaseRequestState, since it would be too late to receive Part One. However when we register our filter before the Page/Control Request Handler (as explained here), then we don't know our content type and we can't check for "text/plain" content types. Too solve this dilemma, we check to register our AjaxTranslationFilter based on the Header=x-microsoftajax. Since the header is part of the request we can register our filter at any point in time before the PreRequestHandlerExecute in our case the BeginRequest:

    /// <summary>
    /// Handles the BeginRequest event of the httpApplication.
    /// </summary> 
    
private void httpApplication_BeginRequest(object senderEventArgs e)
    
{
        
// Check if we need an AjaxTranslationFilter.
        // Note that the check requires an Request header, therefore we can register this filter early
        // if we would register it too late then we would miss out 
        // some data that is handled by the Ajax ScriptModule HttpModule
        
if (!string.IsNullOrEmpty(_context.Request.Headers["x-microsoftajax"]))
        
{
            
// Create a new filter and insert it onto the page
            // This filter will later on, translate all content
            
_context.Response.Filter = new AjaxTranslationFilter(_context.Response.Filter); 
        
    }

To execution chain is then as this:

BeginRequest
- - Ajax Filter Registered
PreRequestHandlerExecute
- - Ajax Filter - Part One
PostRequestHandlerExecute
ReleaseRequestState
- - Ajax Filter - Part two

- Enjoy