I have an HTTP Module that I use to clean up the JSON returned by my web service (see http://www.codeproject.com/KB/webservices/ASPNET_JSONP.aspx?msg=3400287#xx3400287xx for an example of this.) Basically it relates to calling cross-domain JSON web services from javascript.
There is this JsonHttpModule which uses a JsonResponseFilter Stream class to write out the JSON and the overloaded Write method is supposed to wrap the name of the callback function around the JSON, otherwise the JSON errors out as needing a label. However, if the JSON is really long, the Write method in the Stream class is called multiple times, causing the callback function to incorrectly get inserted midway through the JSON. Is there a way in the Stream class to wrap the callback function around the stream at the end or to specify that it write all of the JSON in 1 Write method instead of in chunks??
Here's where it calls the JsonResponseFilter in the JsonHttpModule:
public void OnReleaseRequestState(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
if (!_Apply(app.Context.Request)) return;
// apply response filter to conform to JSONP
app.Context.Response.Filter =
new JsonResponseFilter(app.Context.Response.Filter, app.Context);
}
Here's the Write method in the JsonResponseFilter Stream class that gets called multiple times:
public override void Write(byte[] buffer, int offset, int count)
{
var b1 = Encoding.UTF8.GetBytes(_context.Request.Params["callback"] + "(");
_responseStream.Write(b1, 0, b1.Length);
_responseStream.Write(buffer, offset, count);
var b2 = Encoding.UTF8.GetBytes(");");
_responseStream.Write(b2, 0, b2.Length);
}
Thanks for any help!
Justin
The reason it fires the method multiple times is because it will buffer the contents and then send it to the output stream. Here is an example which shows how to create ViewState mover HttpModule. You can get some idea from the implementation. Scroll down to the bottom and see the result.
http://www.highoncoding.com/Articles/464_Filtering_Responses_Using_ASP_NET_Response_Filters.aspx
Another solution is to write ResponseStream in Flush method. Like in this example.
I modified JsonHttpModules Flush method and used StringBuilder to store stream in Write method like Justin.
/// <summary>
/// Override flush by writing out the cached stream data
/// </summary>
public override void Flush()
{
if (_sb.Length > 0)
{
string message = _context.Request.Params["callback"] + "(" + _sb.ToString() + ");";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(message);
_responseStream.Write(buffer, 0, buffer.Length);
}
// default flush behavior
_responseStream.Flush();
}
public override void Write(byte[] buffer, int offset, int count)
{
string json = System.Text.Encoding.UTF8.GetString(buffer, offset, count);
_sb.Append(json);
}
This way you don't have to try to guess the end of incoming stream.
Related
I'm trying to write a global error handler for ASP.NET web api that is able to log the request details of requests that cause unhandled exception in my api. I've registered the below GlobalExceptionHandler class in my OWIN startup class, but I'm unable to retrieve the content of any data posted in the body of requests.
public class GlobalExecptionHander : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
var body = context.Request.Content.ReadAsStringAsync().Result;
//body here is an empty string
context.Result = new UnhandledErrorActionResult
{
Request = context.Request,
};
}
}
In my startup class
config.Services.Replace(typeof(IExceptionHandler), new GlobalExecptionHander());
Since I just came across this exact problem, I was amazed to find this question without an answer! I hope you've managed to solve the problem after all this time. I'd still like to answer this question regardless.
The thing is that by the time your GlobalExceptionHandler handles the exception, something (like Newtonsoft Json or any other request handler) has already read the contentstream of the HTTP request. When the stream is read, you cannot read it again, unless there was some way to reset that stream to its initial position...
public override void Handle(ExceptionHandlerContext context)
{
string requestContent = "";
using(System.IO.Stream stream = context.Request.Content.ReadAsStreamAsync().Result)
{
// Set the stream back to position 0...
if (stream.CanSeek)
{
stream.Position = 0;
}
// ... and read the content once again!
requestContent = context.Request.Content.ReadAsStringAsync().Result;
}
/* ..Rest of code handling the Exception.. */
}
The reason requestContent is outside that using block, is because the stream gets disposed after the block closes. You could also get rid of using and call stream.Dispose() after you've done reading the content.
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);
}
I am building a HTTP proxy with netty, which supports HTTP pipelining. Therefore I receive multiple HttpRequest Objects on a single Channel and got the matching HttpResponse Objects. The order of the HttpResponse writes is the same than I got the HttpRequest. If a HttpResponse was written, the next one will be written when the HttpProxyHandler receives a writeComplete event.
The Pipeline should be convenient:
final ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("writer", new HttpResponseWriteDelayHandler());
pipeline.addLast("deflater", new HttpContentCompressor(9));
pipeline.addLast("handler", new HttpProxyHandler());
Regarding this question only the order of the write calls should be important, but to be sure I build another Handler (HttpResponseWriteDelayHandler) which suppresses the writeComplete event until the whole response was written.
To test this I enabled network.http.proxy.pipelining in Firefox and visited a page with many images and connections (a news page). The problem is, that the browser does not receive some responses in spite of the logs of the proxy consider them as sent successfully.
I have some findings:
The problem only occurs if the connection from proxy to server is faster than the connection from proxy to browser.
The problem occurs more often after sending a larger image on that connection, e.g. 20kB
The problem does not occur if only 304 - Not Modified responses were sent (refreshing the page considering browser cache)
Setting bootstrap.setOption("sendBufferSize", 1048576); or above does not help
Sleeping a timeframe dependent on the responses body size in before sending the writeComplete event in HttpResponseWriteDelayHandler solves the problem, but is a very bad solution.
I found the solution and want to share it, if anyone else has a similar problem:
The content of the HttpResponse is too big. To analyze the content the whole HTML document was in the buffer. This must be splitted in Chunks again to send it properly. If the HttpResponse is not chunked I wrote a simple solution to do it. One needs to put a ChunkedWriteHandler next to the logic handler and write this class instead of the response itself:
public class ChunkedHttpResponse implements ChunkedInput {
private final static int CHUNK_SIZE = 8196;
private final HttpResponse response;
private final Queue<HttpChunk> chunks;
private boolean isResponseWritten;
public ChunkedHttpResponse(final HttpResponse response) {
if (response.isChunked())
throw new IllegalArgumentException("response must not be chunked");
this.chunks = new LinkedList<HttpChunk>();
this.response = response;
this.isResponseWritten = false;
if (response.getContent().readableBytes() > CHUNK_SIZE) {
while (CHUNK_SIZE < response.getContent().readableBytes()) {
chunks.add(new DefaultHttpChunk(response.getContent().readSlice(CHUNK_SIZE)));
}
chunks.add(new DefaultHttpChunk(response.getContent().readSlice(response.getContent().readableBytes())));
chunks.add(HttpChunk.LAST_CHUNK);
response.setContent(ChannelBuffers.EMPTY_BUFFER);
response.setChunked(true);
response.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
}
}
#Override
public boolean hasNextChunk() throws Exception {
return !isResponseWritten || !chunks.isEmpty();
}
#Override
public Object nextChunk() throws Exception {
if (!isResponseWritten) {
isResponseWritten = true;
return response;
} else {
HttpChunk chunk = chunks.poll();
return chunk;
}
}
#Override
public boolean isEndOfInput() throws Exception {
return isResponseWritten && chunks.isEmpty();
}
#Override
public void close() {}
}
Then one can call just channel.write(new ChunkedHttpResponse(response) and the chunking is done automatically if needed.
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; }
}
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