ASP.NET Response.Filter - asp.net

I need to create filter that replace tags <h2> in the HTML to <h3>:
My filter
public class TagsFilter:Stream
{
HttpContext qwe;
public TagsFilter(HttpContext myContext)
{
qwe = myContext;
}
public override void Write(byte[] buffer, int offset, int count)
{
string html = System.Text.Encoding.UTF8.GetString(buffer);
html = html.Replace("<h2>", "<h3>");
qwe.Response.Write(html.ToCharArray(), 0, html.ToCharArray().Length);
}
My module
public class TagsChanger : IHttpModule
{
public void Init(HttpApplication context)
{
context.Response.Filter = new TagsFilter(context.Context);
}
I get error System.Web.HttpException:In this context, the answer is not available.

Look at Rick Strahl's post about "Capturing and Transforming ASP.NET Output with Response.Filter".
Response.Filter content is chunked. So to implement a Response.Filter effectively requires only that you implement a custom stream and handle the Write() method to capture Response output as it’s written. At first blush this seems very simple – you capture the output in Write, transform it and write out the transformed content in one pass. And that indeed works for small amounts of content. But you see, the problem is that output is written in small buffer chunks (a little less than 16k it appears) rather than just a single Write() statement into the stream, which makes perfect sense for ASP.NET to stream data back to IIS in smaller chunks to minimize memory usage en route.
Unfortunately this also makes it a more difficult to implement any filtering routines since you don’t directly get access to all of the response content which is problematic especially if those filtering routines require you to look at the ENTIRE response in order to transform or capture the output as is needed for the solution the gentleman in my session asked for.
So in order to address this a slightly different approach is required that basically captures all the Write() buffers passed into a cached stream and then making the stream available only when it’s complete and ready to be flushed.
As I was thinking about the implementation I also started thinking about the few instances when I’ve used Response.Filter implementations. Each time I had to create a new Stream subclass and create my custom functionality but in the end each implementation did the same thing – capturing output and transforming it. I thought there should be an easier way to do this by creating a re-usable Stream class that can handle stream transformations that are common to Response.Filter implementations.
Rick Strahl wrote own implementation of stream filter that permits text replacing in right way.

I did a small example. I think you have to access the original stream, rather than accessing the httpContext.
public class ReplacementStream : Stream
{
private Stream stream;
private StreamWriter streamWriter;
public ReplacementStream(Stream stm)
{
stream = stm;
streamWriter = new StreamWriter(stream, System.Text.Encoding.UTF8);
}
public override void Write(byte[] buffer, int offset, int count)
{
string html = System.Text.Encoding.UTF8.GetString(buffer);
html = html.Replace("<h2>", "<h3>");
streamWriter.Write(html.ToCharArray(), 0, html.ToCharArray().Length);
streamWriter.Flush();
}
// all other necessary overrides go here ...
}
public class FilterModule : IHttpModule
{
public String ModuleName
{
// Verweis auf Name in Web.config bei Modul-Registrierung
get { return "FilterModule"; }
}
void context_BeginRequest(object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
context.Response.Filter = new ReplacementStream(context.Response.Filter);
}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
}
Found the solution at this post on SO. Worked for me.

The problem is that you are applying the filter in the Init event, which only occurs once per application instance (it is essentially close to App_Start).
What you need to do is hook in the BeginRequest event from the Init event, and then apply the filter on BeginRequest.
public void Init(HttpApplication application)
{
application.BeginRequest += BeginRequest;
}
private void BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
var context = app.Context;
context.Response.Filter = new TagsFilter(context);
}

Related

ASP.net cache access causing Collection Modified exception in foreach loop

Ok first things first. This is some exception information given by the support team. I know the line and code where it happens. It happens in a FirstOrDefault call over a dictionary obtained from cache.
1) Exception Information
*********************************************
Exception Type: System.InvalidOperationException
Message: Collection was modified; enumeration operation may not execute.
Data: System.Collections.ListDictionaryInternal
Now I wanted to simulate the problem and I could do it in a simple ASP.net application.
My page has 2 Buttons - Button_Process and Button_Add
The code behind is as follows:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
var data = Cache["key"];
if (data == null)
{
var dict = new Dictionary<int, string>();
for (int i = 0; i < 10; i++)
{
dict.Add(i, "i");
}
Cache["key"] = dict;
}
}
}
protected void ButtonProcess_Click(object sender, EventArgs e)
{
var data = Cache["key"] as Dictionary<int, string>;
if (data != null)
{
foreach (var d in data.Values) //In actual code there is FirstOrDefault here
{
Thread.Sleep(1000);
if (d.Contains("5"))
{
//some operation
}
}
}
}
protected void Button2_Click(object sender, EventArgs e)
{
var data = Cache["key"] as Dictionary<int, string>;
if (data != null)
{
data.Add(new Random().Next(), "101");
Cache["key"] = data;
}
}
}
Now assume there are 2 requests:
Request 1 - Someone clicks on button_Process and some operation on cache object is taking place
Request 2 - Someone clicks on button_Add and the first person gets an exception - collection modified blah blah
I understand the problem - it is happening because we are accessing same bit of memory. I have 2 solutions in my mind:
1. I use a for loop instead of for each (to replace FirstOrDefault in actual code) - I dunno how efficient this operation will be after I make the changes. - I don't ever delete any item from cache so I was thinking of this solution
2. I put some lock over cache object or something on those lines - but I dunno exactly where and how should I lock this object.
Please help me with this. I am not able to figure out an efficient solution. What is the best way to handle such situations?
This happens because you're working directly with object, locating in cache. Good practise, to avoid those exceptions and other wierd behavior (when you accidentally modify cache object) is working with copy of cache data. And there are several ways of achieving it, like doing clone or some kind of deep copy. What i prefer is keeping objects in cache serialized (any kind you like - json/xml/binary or w/e else), since (de)serialization makes a deep copy of your object. Following small code snippet will clarify things:
public static class CacheManager
{
private static readonly Cache MyCache = HttpRuntime.Cache;
public static void Put<T>(T data, string key)
{
MyCache.Insert(key, Serialize(data));
}
public static T Get<T>(string key)
{
var data = MyCache.Get(key) as string;
if (data != null)
return Deserialize<T>(data);
return default(T);
}
private static string Serialize(object data)
{
//this is Newtonsoft.Json serializer, but you can use the one you like
return JsonConvert.SerializeObject(data);
}
private static T Deserialize<T>(string data)
{
return JsonConvert.DeserializeObject<T>(data);
}
}
And usage:
var myObj = new Dictionary<int, int>();
CacheManager.Put(myObj, "myObj");
//...
var anotherObj = CacheManager.Get<Dictionary<int, int>>("myObj");
Check Task Parallel Library for .NET 3.5. It has Concurrent Collections such as ConcurrentStack, ConcurentQueue and ConcurrentDictionary.
http://www.nuget.org/packages/TaskParallelLibrary
The problem is that the cache object is global for appdomain and the data stored in are shared between all request.
The only solution to this problem is to activate a lock when you want to access to the collection and then release the lock (https://msdn.microsoft.com/en-us/library/vstudio/c5kehkcz%28v=vs.100%29.aspx).
(sorry form my bad english)

Call the default asp.net HttpHandler from a custom handler

I'm adding ASP.NET routing to an older webforms app. I'm using a custom HttpHandler to process everything. In some situations I would like to map a particular path back to an aspx file, so I need to just pass control back to the default HttpHandler for asp.net.
The closest I've gotten is this
public void ProcessRequest(HttpContext context) {
// .. when we decide to pass it on
var handler = new System.Web.UI.Page();
handler.ProcessRequest(context);
MemoryStream steam = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
handler.RenderControl(htmlWriter);
// write headers, etc. & send stream to Response
}
It doesn't do anything, there's nothing output to the stream. MS's documentation for System.Web.UI.Page (as an IHttpHandler) say something to the effect of "do not call the ProcessRequest method. It's for internal use."
From looking around it seems like you can do this with MVC, e.g. : MvcHttpHandler doesn't seem to implement IHttpHandler
There is also this thing System.Web.UI.PageHandlerFactory which appears that it would just produce a Page handler for an aspx file, but it's internal and I can't use it directly.
This page: http://msdn.microsoft.com/en-us/library/bb398986.aspx refers to the "default asp.net handler" but does not identify a class or give any indication how one might use it.
Any ideas on how I can do this? Is it possible?
Persistence pays off! This actually works, and since this information seems to be available pretty much nowhere I thought I'd answer my own question. Thanks to Robert for this post on instantiating things with internal constructors, this is the key.
http://www.rvenables.com/2009/08/instantiating-classes-with-internal-constructors/
public void ProcessRequest(HttpContext context) {
// the internal constructor doesn't do anything but prevent you from instantiating
// the factory, so we can skip it.
PageHandlerFactory factory =
(PageHandlerFactory)System.Runtime.Serialization.FormatterServices
.GetUninitializedObject(typeof(System.Web.UI.PageHandlerFactory));
string newTarget = "default.aspx";
string newQueryString = // whatever you want
string oldQueryString = context.Request.QueryString.ToString();
string queryString = newQueryString + oldQueryString!="" ?
"&" + newQueryString :
"";
// the 3rd parameter must be just the file name.
// the 4th parameter should be the physical path to the file, though it also
// works fine if you pass an empty string - perhaps that's only to override
// the usual presentation based on the path?
var handler = factory.GetHandler(context, "GET",newTarget,
context.Request.MapPath(context,newTarget));
// Update the context object as it should appear to your page/app, and
// assign your new handler.
context.RewritePath(newTarget , "", queryString);
context.Handler = handler;
// .. and done
handler.ProcessRequest(context);
}
... and like some small miracle, an aspx page processes & renders completely in-process without the need to redirect.
I expect this will only work in IIS7.
I'm you're using Routing in webforms you should be able to just add an ignore route for the specific .aspx files you want. This will then be handled by the default HttpHandler.
http://msdn.microsoft.com/en-us/library/dd505203.aspx
Another option is to invert the logic by handling the cases in which you do NOT want to return the default response and remap the others to your own IHttpHandler. Whenever myCondition is false, the response will be the "default". The switch is implemented as an IHttpModule:
public class SwitchModule: IHttpModule
{
public void Init(HttpApplication context)
{
context.PostAuthenticateRequest += app_PostAuthenticateRequest;
}
void app_PostAuthenticateRequest(object sender, EventArgs e)
{
// Check for whatever condition you like
if (true)
HttpContext.Current.RemapHandler(new CustomHandler());
}
public void Dispose()
}
internal class CustomHandler: IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.Write("hallo");
}
public bool IsReusable { get; }
}

Using HttpModules to modify the response sent to the client

I have two production websites that have similar content. One of these websites needs to be indexed by search engines and the other shouldn't. Is there a way of adding content to the response given to the client using the HttpModule?
In my case, I need the HttpModule to add to the response sent to the when the module is active on that particular web.
You'd probably want to handle the PreRequestHandlerExecute event of the application as it is run just before the IHttpHandler processes the page itself:
public class NoIndexHttpModule : IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += AttachNoIndexMeta;
}
private void AttachNoIndexMeta(object sender, EventArgs e)
{
var page = HttpContext.Current.CurrentHandler as Page;
if (page != null && page.Header != null)
{
page.Header.Controls.Add(new LiteralControl("<meta name=\"robots\" value=\"noindex, follow\" />"));
}
}
}
The other way of doing it, is to create your own Stream implementation and apply it through Response.Filters, but that's certainly trickier.

Example of Asynchronous page processing in ASP.net webforms (.NET 2.0)

Can someone provide me with a simple example of Asynchronous page processing in ASP.NET Webforms 2.0 (I'm using VS 2010, so new syntax like lambdas are ok)?
I have some long running requests that I don't want tying up IIS threads.
For simplicity's sake, let's say my current code looks like this:
protected void Page_Load(object sender, EventArgs e)
{
string param1 = _txtParam1.Text;
string param2 = _txtParam2.Text;
//This takes a long time (relative to a web request)
List<MyEntity> entities = _myRepository.GetEntities(param1, param2);
//Conceptually, I would like IIS to bring up a new thread here so that I can
//display the data after it has come back.
DoStuffWithEntities(entities);
}
How can I modify this code so that it is asynchronous? Let's assume that I already set async="true" in the aspx page.
EDIT
I think I figured out how to get what I'm looking for. I've put the example code in an answer here. Feel free to point out any flaws or changes that can be made.
I asked some folks on the ASP.NET team. Here's their emailed response to me, and now, to you.
All that code ends up doing is spinning up a new thread and performing delegate invocation on that thread. So now there are two threads running: the request thread and the new thread. Hence this sample actually has worse performance than the original synchronous code would have had.
See http://www.asp.net/web-forms/tutorials/aspnet-45/using-asynchronous-methods-in-aspnet-45 for a sample on how to write and consume async methods in ASP.NET.
Here is a simple example of asynchronous processing.
protected void Page_Load(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
ThreadPool.QueueUserWorkItem(state => Dokimes_Programming_multithread_QueryWorkThead.ThreadProc2());
Debug.Write("Main thread does some work, then sleeps.");
// If you comment out the Sleep, the main thread exits before
// the thread pool task runs. The thread pool uses background
// threads, which do not keep the application running. (This
// is a simple example of a race condition.)
// Thread.Sleep(4000);
txtDebug.Text += "ended";
Debug.Write("end.");
}
// This thread procedure performs the task.
static void ThreadProc(Object stateInfo)
{
// No state object was passed to QueueUserWorkItem, so stateInfo is null.
Debug.Write(" Hello from the thread pool 1.");
}
static void ThreadProc2()
{
// No state object was passed to QueueUserWorkItem, so stateInfo is null.
Debug.Write("Hello from the thread pool 2.");
}
Other way
You can use the PageAsyncTask, see here a full example:
http://msdn.microsoft.com/en-us/library/system.web.ui.pageasynctask.aspx
Something like
clAsynCustomObject oAsynRun = new clAsynCustomObject();
PageAsyncTask asyncTask = new PageAsyncTask(oAsynRun.OnBegin, oAsynRun.OnEnd, oAsynRun.OnTimeout, null, true);
Page.RegisterAsyncTask(asyncTask);
Page.ExecuteRegisteredAsyncTasks();
I think I discovered how to do what I wanted to accomplish... though it may not be the best way, feel free to chime in.
At the time of writing there was only one answer in this thread, by Aristos. While he gave an example of executing an asynchronous request, what I wanted was a way to tell ASP.NET to execute some long running method, release the IIS thread so it can be available to service other requests, and then come back when the method finished.
Here's what I came up with, using the same (or similar) example in the question:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Web.UI;
namespace WebApplication2
{
public class MyEntity
{
public string Name { get; set; }
}
public class MyRepository
{
public List<MyEntity> GetEntities(string param1, string param2)
{
Thread.Sleep(10000);
return new List<MyEntity> {new MyEntity {Name = "John Smith"}};
}
}
public partial class Default : Page
{
private readonly MyRepository _myRepository = new MyRepository();
private List<MyEntity> _myEntities;
protected void Page_Load(object sender, EventArgs e)
{
}
private void DoStuffWithEntities()
{
Response.Write("<br/><br/><b>" + _myEntities[0].Name + "</b><br/><br/>");
}
protected void _btnProcess_Click(object sender, EventArgs e)
{
AddOnPreRenderCompleteAsync(BeginExecution, EndExecution, null);
}
private void GetEntities()
{
string param1 = _txtParam1.Text;
string param2 = _txtParam2.Text;
//This takes a long time (relative to a web request)
_myEntities = _myRepository.GetEntities(param1, param2);
}
private IAsyncResult BeginExecution(object sender, EventArgs e, AsyncCallback callback, object state)
{
var t = new ThreadStart(GetEntities);
return t.BeginInvoke(callback, null);
}
private void EndExecution(IAsyncResult result)
{
//Conceptually, I would like IIS to bring up a new thread here so that I can
//display the data after it has come back.
DoStuffWithEntities();
}
}
}

Determine size of page via HttpModule

Here's an easy one for you:
I'm currently logging request duration via an HttpModule and I'd like to find out the number of bytes each page is as well.
HttpContext.Current.Response.OutputStream.Length throws a NotSupportedException.
What's an easy way to do this?
I have an HttpModule that implements a stream rewriter. It derives from the Stream class. In my HttpModule I have the following code:
void app_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpResponse response = HttpContext.Current.Response;
response.Filter = new MyRewriterStream(response.Filter);
}
In the stream class I have the following code that overrides the default Write method:
public override void Write(byte[] buffer, int offset, int count)
{
string outStr;
outStr = UTF8Encoding.UTF8.GetString(buffer, offset, count);
//Do useful stuff and write back to the stream
}
You can just take the length of the string at the second point

Resources