Dynamically Rendering asp:Image from BLOB entry in ASP.NET - asp.net

What I want to achieve is this. I want to give the user the ability to upload an image file, store the image in BLOB in SQL Server, and then use this image as a logo in other pages of the site.
I have done this by using
Response.Clear();
Response.ContentType = "image/pjpeg";
Response.BinaryWrite(imageConents);
Response.End();
but to do this, I use a User control in the place where I want to show the image. I want to do it if possible using an asp:Image control, or even a pure old html image control. Is this possible?

Add a 'Generic Handler' to your web project, name it something like Image.ashx. Implement it like this:
public class ImageHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
using(Image image = GetImage(context.Request.QueryString["ID"]))
{
context.Response.ContentType = "image/jpeg";
image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
}
public bool IsReusable
{
get
{
return true;
}
}
}
Now just implement the GetImage method to load the image with the given ID, and you can use
<asp:Image runat="server" ImageUrl="~/Image.ashx?ID=myImageId" />
to display it. You might want to think about implementing some form of caching in the handler too. And remember if you want to change the image format to PNG, you need to use an intermediate MemoryStream (because PNGs require a seekable stream to be saved).

You can BASE64 encode the content of the image directly into the SRC attribute, however, I believe only Firefox will parse this back into an image.
What I typically do is a create a very lightweight HTTPHandler to serve the images:
using System;
using System.Web;
namespace Example
{
public class GetImage : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.QueryString("id") != null)
{
Blob = GetBlobFromDataBase(id);
context.Response.Clear();
context.Response.ContentType = "image/pjpeg";
context.Response.BinaryWrite(Blob);
context.Response.End();
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
You can reference this directly in your img tag:
<img src="GetImage.ashx?id=111"/>
Or, you could even create a server control that does it for you:
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Example.WebControl
{
[ToolboxData("<{0}:DatabaseImage runat=server></{0}:DatabaseImage>")]
public class DatabaseImage : Control
{
public int DatabaseId
{
get
{
if (ViewState["DatabaseId" + this.ID] == null)
return 0;
else
return ViewState["DataBaseId"];
}
set
{
ViewState["DatabaseId" + this.ID] = value;
}
}
protected override void RenderContents(HtmlTextWriter output)
{
output.Write("<img src='getImage.ashx?id=" + this.DatabaseId + "'/>");
base.RenderContents(output);
}
}
}
This could be used like
<cc:DatabaseImage id="db1" DatabaseId="123" runat="server/>
And of course, you could set the databaseId in the codebehind as needed.

You don't want to be serving blobs from a database without implementing client side caching.
You will need to handle the following headers to support client side caching:
ETag
Expires
Last-Modified
If-Match
If-None-Match
If-Modified-Since
If-Unmodified-Since
Unless-Modified-Since
For an http handler that does this, check out:
http://code.google.com/p/talifun-web/wiki/StaticFileHandler
It has a nice helper to serve the content. It should be easy to pass in database stream to it. It also does server side caching which should help alleviate some of the pressure on the database.
If you ever decide to serve streaming content from the database, pdfs or large files the handler also supports 206 partial requests.
It also supports gzip and deflate compression.
These file types will benefit from further compression:
css, js, htm, html, swf, xml, xslt, txt
doc, xls, ppt
There are some file types that will not benefit from further compression:
pdf (causes problems with certain versions in IE and it is usually well compressed)
png, jpg, jpeg, gif, ico
wav, mp3, m4a, aac (wav is often compressed)
3gp, 3g2, asf, avi, dv, flv, mov, mp4, mpg, mpeg, wmv
zip, rar, 7z, arj

Using ASP.Net with MVC this is pretty forward easy. You code a controller with a method like this:
public FileContentResult Image(int id)
{
//Get data from database. The Image BLOB is return like byte[]
SomeLogic ItemsDB= new SomeLogic("[ImageId]=" + id.ToString());
FileContentResult MyImage = null;
if (ItemsDB.Count > 0)
{
MyImage= new FileContentResult(ItemsDB.Image, "image/jpg");
}
return MyImage;
}
In your ASP.NET Web View or in this example, in your ASP.NET Web Form you can fill an Image Control with the URL to your method like this:
this.imgExample.ImageUrl = "~/Items/Image/" + MyItem.Id.ToString();
this.imgExample.Height = new Unit(120);
this.imgExample.Width = new Unit(120);
Voilá. Not HttpModules hassle was needed.

We actually just released some classes that help with exactly this kind of thing:
http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=16449
Specifically, check out the DatabaseImage sample.

Add the code to a handler to return the image bytes with the appropriate mime-type. Then you can just add the url to your handler like it is an image. For example:
<img src="myhandler.ashx?imageid=5">
Make sense?

Related

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; }
}

How to use output caching on .ashx handler

How can I use output caching with a .ashx handler? In this case I'm doing some heavy image processing and would like the handler to be cached for a minute or so.
Also, does anyone have any recommendations on how to prevent dogpiling?
There are some good sources but you want to cache you processing server side and client-side.
Adding HTTP headers should help in the client side caching
here are some Response headers to get started on..
You can spend hours tweaking them until you get the desired performance
//Adds document content type
context.Response.ContentType = currentDocument.MimeType;
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(10));
context.Response.Cache.SetMaxAge(new TimeSpan(0,10,0));
context.Response.AddHeader("Last-Modified", currentDocument.LastUpdated.ToLongDateString());
// Send back the file content
context.Response.BinaryWrite(currentDocument.Document);
As for server side caching that is a different monster... and there are plenty of caching resources out there...
you can use like this
public class CacheHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
OutputCachedPage page = new OutputCachedPage(new OutputCacheParameters
{
Duration = 60,
Location = OutputCacheLocation.Server,
VaryByParam = "v"
});
page.ProcessRequest(HttpContext.Current);
context.Response.Write(DateTime.Now);
}
public bool IsReusable
{
get
{
return false;
}
}
private sealed class OutputCachedPage : Page
{
private OutputCacheParameters _cacheSettings;
public OutputCachedPage(OutputCacheParameters cacheSettings)
{
// Tracing requires Page IDs to be unique.
ID = Guid.NewGuid().ToString();
_cacheSettings = cacheSettings;
}
protected override void FrameworkInitialize()
{
// when you put the <%# OutputCache %> directive on a page, the generated code calls InitOutputCache() from here
base.FrameworkInitialize();
InitOutputCache(_cacheSettings);
}
}
}
Old, question but the answer didn't really mentioned the server-side handling.
As in the winning answer, I would use this for the client side:
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(10));
context.Response.Cache.SetMaxAge(TimeSpan.FromMinutes(10));
and for the server side, since you are using a ashx instead of a web page, I'm assuming that you are directly writing the output to the Context.Response.
In that case you could use something like this (in this case I want to save the response based on parameter "q", and Im using a sliding window expiration)
using System.Web.Caching;
public void ProcessRequest(HttpContext context)
{
string query = context.Request["q"];
if (context.Cache[query] != null)
{
//server side caching using asp.net caching
context.Response.Write(context.Cache[query]);
return;
}
string response = GetResponse(query);
context.Cache.Insert(query, response, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(10));
context.Response.Write(response);
}
I used the following with success and thought it worthwhile to post here .
Manually controlling the ASP.NET page output cache
From http://dotnetperls.com/cache-examples-aspnet
Setting cache options in Handler.ashx files
First, you can use HTTP handlers
in ASP.NET for a faster way to server
dynamic content than Web Form pages.
Handler.ashx is the default name for
an ASP.NET generic handler. You need
to use the HttpContext parameter and
access the Response that way.
Sample code excerpted:
<%# WebHandler Language="C#" Class="Handler" %>
C# to cache response for 1 hour
using System;
using System.Web;
public class Handler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
// Cache this handler response for 1 hour.
HttpCachePolicy c = context.Response.Cache;
c.SetCacheability(HttpCacheability.Public);
c.SetMaxAge(new TimeSpan(1, 0, 0));
}
public bool IsReusable {
get {
return false;
}
}
}
The solution with the OutputCachedPage works fine, however at a price of the performance, since you need to instantiate an object derived from the System.Web.UI.Page base class.
A simple solution would be to use the Response.Cache.SetCacheability, as suggested by some of the above answers. However for the response to be cached at the server (inside Output Cache) one needs to use HttpCacheability.Server, and set a VaryByParams or VaryByHeaders (note that when using VaryByHeaders URL can't contain a query string, since the cache will be skipped).
Here's a simple example (based on https://support.microsoft.com/en-us/kb/323290):
<%# WebHandler Language="C#" Class="cacheTest" %>
using System;
using System.Web;
using System.Web.UI;
public class cacheTest : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
TimeSpan freshness = new TimeSpan(0, 0, 0, 10);
DateTime now = DateTime.Now;
HttpCachePolicy cachePolicy = context.Response.Cache;
cachePolicy.SetCacheability(HttpCacheability.Public);
cachePolicy.SetExpires(now.Add(freshness));
cachePolicy.SetMaxAge(freshness);
cachePolicy.SetValidUntilExpires(true);
cachePolicy.VaryByParams["id"] = true;
context.Response.ContentType = "application/json";
context.Response.BufferOutput = true;
context.Response.Write(context.Request.QueryString["id"]+"\n");
context.Response.Write(DateTime.Now.ToString("s"));
}
public bool IsReusable
{
get
{
return false;
}
}
}
Hint: you monitor the caching in the Performance Counters "ASP.NET Applications__Total__\Output Cache Total".

Can I use HttpHandler to fake the existence of aspx pages?

I am building a web site with ASP.NET 3.5, and most of the site structure is static enough to create a folder structure and aspx pages. However, the site administrators want the ability to add new pages to different sections of the site through a web interface and using a WYSIWYG editor. I am using nested master pages to give the different sections of the site their own menus. What I would like to do is have a generic page under each section of the site that uses the appropriate master page and has a place holder for content that could be loaded from a database. I would also like these "fake" pages to have a url like any other aspx page, as if they had corresponding files on the server. So rather than have my url be:
http://mysite.com/subsection/gerenicconent.aspx?contentid=1234
it would be something like:
http://mysite.com/subsection/somethingmeaningful.aspx
The problem is that somethingmeaningful.aspx does not exist, because the administrator created it through the web UI, and the content is stored in the database. What I'm thinking is that I'll implement an HTTP handler that handles requests for aspx files. In that handler, I'll check to see if the URL that was requested is an actual file or one of my "fake pages". If it is a request for a fake page, I'll re-route the request to the generic content page for the appropriate section, change the query string to request the appropriate data from the database, and rewrite the URL so that it looks to the user as if the fake page really exists. The problem I'm having right now is that I can't figure out how to route the request to the default handler for aspx pages. I tried to instantiate a PageHandlerFactory, but the constuctor is protected internal. Is there any way for me to tell my HttpHandler to call the HttpHandler that would normal be used to process a request? My handler code currently looks like this:
using System.Web;
using System.Web.UI;
namespace HandlerTest
{
public class FakePageHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
if(RequestIsForFakedPage(context))
{
// reroute the request to the generic page and rewrite the URL
PageHandlerFactory factory = new PageHandlerFactory(); // this won't compile because the constructor is protected internal
factory.GetHandler(context, context.Request.RequestType, GetGenericContentPath(context), GetPhysicalApplicationPath(context)).ProcessRequest(context);
}
else
{
// route the request to the default handler for aspx pages
PageHandlerFactory factory = new PageHandlerFactory();
factory.GetHandler(context, context.Request.RequestType, context.Request.Path, context.Request.PhysicalPath).ProcessRequest(context);
}
}
public string RequestForPageIsFaked(HttpContext context)
{
// TODO
}
public string GetGenericContentPath(HttpContext context)
{
// TODO
}
public string GetPhysicalApplicationPath(HttpContext context)
{
// TODO
}
}
}
I still have some work to do to determine if the request is for a real page, and I haven't rewritten any URLs yet, but is something like this possible? Is there another way to create a PageHandlerFactory other than calling its constructor? Is there any way I can route the request up to the "normal" HttpHandler for an aspx page? I'd basically be saying "process this ASPX request as you normally would."
If you are using 3.5, look into using asp.net routing.
http://msdn.microsoft.com/en-us/library/cc668201.aspx
You would be better off using an http module for this, as in this case you can use the RewritePath method to route the request for fake pages, and do nothing for actual pages which will allow them to be processed as normal.
There is a good explanation of this here which also covers the benefits of using IIS 7.0 if that is an option for you.
I've just pulled this off a similar system we've just written.
This method takes care of physical pages and "fake" pages. You'll be able to ascertan how this fits with your fake page schema, I'm sure.
public class AspxHttpHandler : IHttpHandlerFactory
{
#region ~ from IHttpHandlerFactory ~
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
string url=context.Request.Url.AbsolutePath;
string[] portions = url.Split(new char[] { '/', '\\' });
// gives you the path, i presume this will help you identify the section and page
string serverSidePage=Path.Combine(context.Server.MapPath("~"),url);
if (File.Exists(serverSidePage))
{
// page is real
string virtualPath = context.Request.Url.AbsolutePath;
string inputFile = context.Server.MapPath(virtualPath);
try
{
// if it's real, send in the details to the ASPX compiler
return PageParser.GetCompiledPageInstance(virtualPath, inputFile, context);
}
catch (Exception ex)
{
throw new ApplicationException("Failed to render physical page", ex);
}
}
else
{
// page is fake
// need to identify a page that exists which you can use to compile against
// here, it is CMSTaregtPage - it can use a Master
string inputFile = context.Server.MapPath("~/CMSTargetPage.aspx");
string virtualPath = "~/CMSTargetPage.aspx";
// you can also add things that the page can access vai the Context.Items collection
context.Items.Add("DataItem","123");
return PageParser.GetCompiledPageInstance(virtualPath, inputFile, context);
}
public void ReleaseHandler(IHttpHandler handler)
{
}

ASP.NET AJAX without update panel

What is the best practice to support data for asp.net 2.0-3.5 ajax web application?
I don't want to use update panels, just plain text data (JSON).
Should I use web services? Or is there another way.
Errrr... Use an .aspx page ? What are handlers for ?
You just have to create a generic base handler that will take care of json (de)serialization (e.g. using Json.net) and then implement handlers for your ajax calls.
public abstract class JsonHandlerBase<TInput, TOutput> : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json";
TInput input = (TInput)context.Request; // Desesialize input
TOutput output = ProcessRequest(context, parameter);
string json = (string)output; // Serialize output
context.Response.Write(json);
}
public abstract TOutput ProcessRequest(HttpContext context, TInput input);
public bool IsReusable { get { return false; } }
}
This is just an example, it's up to you to decide want you need in your base handler.
You can use plain aspx pages or handlers and just output JSON. You do this by erasing all the Html in the aspx and then using Response.Write() in the code.
Then for the front end JS you can use JQuery or any other Ajax framework.
You may also want to check out Asp.Net MVC. MVC has a JsonResult resonse type and is very easy to use together with JQuery to get very good Ajax functionality.

ASP.NET MVC Relative Paths

In my applications, I often have to use relative paths. For example, when I reference JQuery, I usually do so like this:
<script type="text/javascript" src="../Scripts/jquery-1.2.6.js"></script>
Now that I'm making the transition to MVC, I need to account for the different paths a page might have, relative to the root. This was of course an issue with URL rewriting in the past, but I managed to work around it by using consistent paths.
I'm aware that the standard solution is to use absolute paths such as:
<script type="text/javascript" src="/Scripts/jquery-1.2.6.js"></script>
but this will not work for me as during the development cycle, I have to deploy to a test machine on which the app will run in a virtual directory. Root relative paths don't work when the root changes. Also, for maintenance reasons, I cannot simply change out all the paths for the duration of deploying the test - that would be a nightmare in itself.
So what's the best solution?
Edit:
Since this question is still receiving views and answers, I thought it might be prudent to update it to note that as of Razor V2, support for root-relative urls is baked in, so you can use
<img src="~/Content/MyImage.jpg">
without any server-side syntax, and the view engine automatically replaces ~/ with whatever the current site root is.
Try this:
<script type="text/javascript" src="<%=Url.Content("~/Scripts/jquery-1.2.6.js")%>"></script>
Or use MvcContrib and do this:
<%=Html.ScriptInclude("~/Content/Script/jquery.1.2.6.js")%>
While an old post, new readers should know that Razor 2 and later (default in MVC4+) completely resolves this problem.
Old MVC3 with Razor 1:
Application home page
New MVC4 with Razor 2 and later:
Application home page
No awkward Razor function-like syntax.
No non-standard markup tags.
Prefixing a path in any HTML attributes with a tilde ('~') tells Razor 2 to "just make it work" by substituting the correct path. It's great.
Breaking change - MVC 5
Watch out for a breaking change change in MVC 5 (from the MVC 5 release notes)
Url Rewrite and Tilde(~)
After upgrading to ASP.NET Razor 3 or ASP.NET MVC 5, the tilde(~)
notation may no longer work correctly if you are using URL rewrites.
The URL rewrite affects the tilde(~) notation in HTML elements such as
<A/>, <SCRIPT/>, <LINK/>, and as a result the tilde no longer maps to
the root directory.
For example, if you rewrite requests for asp.net/content to asp.net,
the href attribute in
<A href="~/content/"/> resolves to
/content/content/ instead of /. To suppress this change, you can set
the IIS_WasUrlRewritten context to false in each Web Page or in
Application_BeginRequest in Global.asax.
They don't actually explain how to do it, but then I found this answer:
If you are running in IIS 7 Integrated Pipeline mode try putting the
following in your Global.asax:
protected void Application_BeginRequest(object sender, EventArgs e)
{
Request.ServerVariables.Remove("IIS_WasUrlRewritten");
}
Note: You may want to check Request.ServerVariables actually contains IIS_WasUrlRewritten first to be sure this is what your problem is.
PS. I thought I had a situation where this was happening to me and I was getting src="~/content/..." URLS generated into my HTML - but it turned out something just wasn't refreshing when my code was being compiled. Editing and resaving the Layout and page cshtml files somehow triggered something to work.
In ASP.NET I usually use <img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/>.
I don't see why a similar solution shouldn't work in ASP.NET MVC.
<script src="<%=ResolveUrl("~/Scripts/jquery-1.2.6.min.js") %>" type="text/javascript"></script>
Is what I used. Change path to match your example.
For what it's worth, I really hate the idea of littering my app with server tags just to resolve paths, so I did a bit more research and opted to use something I'd tried before for rewriting links - a response filter. In this way, I can prefix all absolute paths with a known prefix and replace it at runtime using the Response.Filter object and not have to worry about unnecessary server tags. The code is posted below in case it will help anyone else.
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
namespace Demo
{
public class PathRewriter : Stream
{
Stream filter;
HttpContext context;
object writeLock = new object();
StringBuilder sb = new StringBuilder();
Regex eofTag = new Regex("</html>", RegexOptions.IgnoreCase | RegexOptions.Compiled);
Regex rootTag = new Regex("/_AppRoot_", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public PathRewriter(Stream filter, HttpContext context)
{
this.filter = filter;
this.context = context;
}
public override void Write(byte[] buffer, int offset, int count)
{
string temp;
lock (writeLock)
{
temp = Encoding.UTF8.GetString(buffer, offset, count);
sb.Append(temp);
if (eofTag.IsMatch(temp))
RewritePaths();
}
}
public void RewritePaths()
{
byte[] buffer;
string temp;
string root;
temp = sb.ToString();
root = context.Request.ApplicationPath;
if (root == "/") root = "";
temp = rootTag.Replace(temp, root);
buffer = Encoding.UTF8.GetBytes(temp);
filter.Write(buffer, 0, buffer.Length);
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return filter.CanSeek; }
}
public override bool CanWrite
{
get { return true; }
}
public override void Flush()
{
return;
}
public override long Length
{
get { return Encoding.UTF8.GetBytes(sb.ToString()).Length; }
}
public override long Position
{
get { return filter.Position; }
set { filter.Position = value; }
}
public override int Read(byte[] buffer, int offset, int count)
{
return filter.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return filter.Seek(offset, origin);
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
}
public class PathFilterModule : IHttpModule
{
public void Dispose()
{
return;
}
public void Init(HttpApplication context)
{
context.ReleaseRequestState += new EventHandler(context_ReleaseRequestState);
}
void context_ReleaseRequestState(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
if (app.Response.ContentType == "text/html")
app.Response.Filter = new PathRewriter(app.Response.Filter, app.Context);
}
}
}
The Razor view engine for MVC 3 makes it even easier and cleaner to use virtual-root relative paths that are properly resolved at run-time. Just drop the Url.Content() method into the href attribute value and it will resolve properly.
Application home page
Like Chris, I really can't stand having to put bloated server-side tags inside my clean markup just purely to tell the stupid thing to look from the root upwards. That should be a very simple, reasonable thing to ask for. But I also hate the idea of having to go to the effort of writing any custom C# classes to do such a simple thing, why should I have to? What a waste of time.
For me, I simply compromised on "perfection" and hardcoded the virtual directory's root path name inside my path references. So like this:
<script type="text/javascript" src="/MyProject/Scripts/jquery-1.2.6.js"></script>
No server-side processing or C# code required to resolve the URL, which is best for performance although I know it would be negligible regardless. And no bloated ugly server-side chaos in my nice clean markup.
I'll just have to live with knowing that this is hardcoded and will need to be removed when the thing migrates to a proper domain instead of http://MyDevServer/MyProject/
Cheers
I use a simple helper method. You can easily use it in the Views and Controllers.
Markup:
About Us
Helper method:
public static string Root()
{
if (HttpContext.Current.Request.Url.Host == "localhost")
{
return "";
}
else
{
return "/productionroot";
}
}

Resources