自定义HttpResponse的Filter修改Http Entity Body
HttpResponse的Filter属性允许开发人员定义一个派生于Stream的类型,修改Http Entity Body。比如原来的Web应用程序并没有添加兼容性标记,许多页面也并没有使用Master,各处修改增加了许多工作量。编写一个HttpModule,在Init函数中订阅HttpApplication的ReleaseRequestState事件。
1 // <summary> 2 /// 初始化 3 /// </summary> 4 /// <param name=”context”>上下文</param> 5 public void Init(HttpApplication context) 6 { 7 context.ReleaseRequestState += new EventHandler(OnReleaseRequestState); 8 }
新建一个派生于Stream的类型HeadRequestFilter,并在ReleaseRequestState事件触发后为HttpResponse的Filter属性赋值:
1 /// <summary> 2 /// 释放请求状态 3 /// </summary> 4 /// <param name=”sender”>发送方</param> 5 /// <param name=”e”>事件参数</param> 6 private void OnReleaseRequestState(Object sender, EventArgs e) 7 { 8 HttpResponse response = HttpContext.Current.Response; 9 10 response.Filter = new HeadRequestFilter(response.Filter); 11 }
由于我们只需要为head标签内添加meta标签,所以HttpResponse原来的HttpResponseStreamFilterSink依然可以被使用。需要修改的是Stream定义的Write和Flush函数。在执行Flush函数前需要通过StringBuilder保存旧有输出的HTML:
m_buffer.Append(HttpContext.Current.Response.ContentEncoding.GetString(buffer, offset, count));
在调用Flush函数内部插入meta标签,最后调用HttpResponse输出修改后的Http Entity Body。下面是一个实现插入meta标签的HeadResponseFilter示例:
1 public sealed class HeadResponseFilter : Stream 2 { 3 private const String TAG_HEAD_BEGIN = “<head”; 4 private const String TAG_HEAD_END = “</head>”; 5 6 private StringBuilder m_buffer; 7 private Stream m_stream; 8 9 /// <summary> 10 /// 构造函数 11 /// </summary> 12 /// <param name=”stream”>流</param> 13 public HeadResponseFilter(Stream stream) 14 { 15 m_stream = stream; 16 m_buffer = new StringBuilder(); 17 } 18 19 /// <summary> 20 /// 请求 21 /// </summary> 22 public HttpRequest Request 23 { 24 get 25 { 26 return HttpContext.Current.Request; 27 } 28 } 29 30 /// <summary> 31 /// 响应 32 /// </summary> 33 public HttpResponse Response 34 { 35 get 36 { 37 return HttpContext.Current.Response; 38 } 39 } 40 41 /// <summary> 42 /// 可读 43 /// </summary> 44 public override Boolean CanRead 45 { 46 get { return true; } 47 } 48 49 /// <summary> 50 /// 可查询 51 /// </summary> 52 public override Boolean CanSeek 53 { 54 get { return true; } 55 } 56 57 /// <summary> 58 /// 可写 59 /// </summary> 60 public override Boolean CanWrite 61 { 62 get { return true; } 63 } 64 65 /// <summary> 66 /// 长度 67 /// </summary> 68 public override Int64 Length 69 { 70 get { return 0; } 71 } 72 73 /// <summary> 74 /// 位置 75 /// </summary> 76 public override Int64 Position { get; set; } 77 78 /// <summary> 79 /// 刷新 80 /// </summary> 81 public override void Flush() 82 { 83 String rawHtml = m_buffer.ToString(); 84 Int32 headIndex = rawHtml.IndexOf(TAG_HEAD_BEGIN, StringComparison.CurrentCultureIgnoreCase); 85 Int32 headIndex2 = rawHtml.IndexOf(TAG_HEAD_END, StringComparison.CurrentCultureIgnoreCase); 86 87 if ((headIndex > 0) && (headIndex2 > 0) && (headIndex < headIndex2)) 88 { 89 String rawHead = rawHtml.Substring(headIndex, headIndex2 – headIndex + TAG_HEAD_END.Length); 90 XElement headElement = XElement.Parse(rawHead); 91 IEnumerable<XElement> metaElement = headElement.Descendants(“meta”).Where(p => p.Attribute(“http-equiv”) != null); 92 93 if (metaElement.Count() == 0) 94 headElement.Add(new XElement(“meta”, new XAttribute(“http-equiv”, “x-ua-compatible”), new XAttribute(“content”, “IE=7″))); 95 96 rawHtml = String.Concat(rawHtml.Substring(0, headIndex), headElement.ToString(), rawHtml.Substring(headIndex + rawHead.Length)); 97 } 98 99 this.Response.Write(rawHtml); 100 this.Response.Flush(); 101 } 102 103 /// <summary> 104 /// 读取 105 /// </summary> 106 /// <param name=”buffer”>缓冲区</param> 107 /// <param name=”offset”>偏移量</param> 108 /// <param name=”count”>数量</param> 109 /// <returns>读取数量</returns> 110 public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count) 111 { 112 return m_stream.Read(buffer, offset, count); 113 } 114 115 /// <summary> 116 /// 查找 117 /// </summary> 118 /// <param name=”offset”>偏移量</param> 119 /// <param name=”origin”>参考点</param> 120 /// <returns>位置</returns> 121 public override Int64 Seek(Int64 offset, SeekOrigin origin) 122 { 123 return m_stream.Seek(offset, origin); 124 } 125 126 /// <summary> 127 /// 设置长度 128 /// </summary> 129 /// <param name=”value”>值</param> 130 public override void SetLength(Int64 value) 131 { 132 m_stream.SetLength(value); 133 } 134 135 /// <summary> 136 /// 写入 137 /// </summary> 138 /// <param name=”buffer”>缓冲区</param> 139 /// <param name=”offset”>偏移量</param> 140 /// <param name=”count”>数量</param> 141 public override void Write(Byte[] buffer, Int32 offset, Int32 count) 142 { 143 m_buffer.Append(HttpContext.Current.Response.ContentEncoding.GetString(buffer, offset, count)); 144 } 145 }