Embedding ASP.NET Resources (ASP.NET 2.0 and Our Way Of Doing Things)

I've seen lots of examples for embedding resources in ASP.NET assemblies (which you can do in v2.0 without much trouble). However, they all seem to get it wrong (they send the bits, but forget the ContentType, which is a very important part of sending back requests). Microsoft's APS.NET 2 implementation gets it right, but requires a bunch of WebResourceAttributes to be appended to an assembly, which basically have a resource name and then a content type... this seems kind of strange to me, as IIS should already have the ContentType for the data you are adding.

It is even more strange to me that they chose to do it this way, since the framework already has a class called MimeMapping which allows for a lookup of a MimeType given a filename (of course, this is internal for some reason, is only used by the StaticFileHandler class, and ignores any IIS mappings you might have... but that is a debate for another day).

As this is one of the things our upcoming CMS product's assemblies do to store various web resources, I figured I'd chime in on another way to do this that provides a lot more flexibility at runtime. Of course, it requires some Win32 API calls, but it does the trick without the extra attribute lookups or a hard coded mapping of content types.

public class MimeLookup
 {
  public MimeLookup()
  {
  }
  [DllImport("urlmon.dll", CharSet=CharSet.Auto)]
  internal static extern int FindMimeFromData (
   IntPtr pBC,
   [MarshalAs(UnmanagedType.LPWStr)] string pwzUrl,
   IntPtr pBuffer,
   int cbSize,
   [MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed,
   int dwMimeFlags,
   [MarshalAs(UnmanagedType.LPWStr)] out string ppwzMimeOut,
   int dwReserved );

  static public string GetMimeType(string filename)
  {
   string mimeType = null;
   FindMimeFromData(IntPtr.Zero, filename, IntPtr.Zero, 0, null, 0, out mimeType, 0);
   return mimeType;
  }
 }
public class ResourceRequestHandler : IHttpHandler
 {
  public ResourceRequestHandler()
  {
   
  }
  #region IHttpHandler Members
  public void ProcessRequest(HttpContext context)
  {
   string resourceName = context.Request["ResourceName"];
   if(resourceName == null)
   {
    throw new HttpException(404, "No resource was specified");
   }
   string typeName = context.Request["TypeName"];
   if(typeName == null)
   {
    throw new HttpException(404, "No type was specified");
   }
   Type t = Type.GetType(HttpUtility.UrlDecode(typeName));
   if(t == null)
   {
    throw new HttpException(404, "Invalid type was specified");
   }
   System.Reflection.Assembly a = System.Reflection.Assembly.GetAssembly(t);
   using(Stream s = a.GetManifestResourceStream(HttpUtility.UrlDecode(resourceName)))
   {   
    byte[] buffer = new byte[1024];
    int read = 0;
    while( (read = s.Read(buffer, 0, buffer.Length)) != 0 )
    {
     
     System.Reflection.ManifestResourceInfo ri = a.GetManifestResourceInfo(resourceName);
     
     string filename = resourceName;
     if(ri.FileName != null)
     {
      context.Response.AddHeader("content-disposition", "inline; filename="+filename);
     }
     context.Response.ContentType = MimeTypes.MimeLookup.GetMimeType(filename);
     
     context.Response.OutputStream.Write(buffer, 0, read);     
    }
   }
  }
  public bool IsReusable
  {
   get
   {
    return true;
   }
  }
  public static string GetUrl(Type t, string resourceName)
  {
   return "GetResource.ashx?TypeName="+HttpUtility.UrlEncode(t.AssemblyQualifiedName)+"&ResourceName="+HttpUtility.UrlEncode(resourceName);
  }
  #endregion
 }

5 Comments

  • Hi Jesse,



    WebResourceAttribute is used mainly for securiry reasons to limit what embedded resources should be served.



    -Vic.

  • Interesting, though IMO it still doesn't explain why the contenttype is manually embedded and is a lame approach to security. If these resources are truly in need of security, why not protect access to the resources themselves, not just access to them through the provider?

  • >>>why the contenttype is manually embedded

    I would say extensibility. Personally I don't want to depend on a hardcoded & fixed list (MimeMapping) or the need to call a native method and having the MIME media type registered on the server (urlmon.dll!FindMimeFromData). Currently WebResourceAttribute saves you from this.



    >>>and is a lame approach to security

    Not it isn't. WebResourceAttribute is there specifically to cover the "hole" introduced by the WebResource handler, no more is necessary for this but you're free to add your own security at any level you may need. (There is *another* security "issue" but its not this one...)





    I wrote 4769 words :-) about the WebResource handler that will be soon published on MSDN. I'm detailing lots of useful info on how this handler works internally (clever caching, url optimizations, substitutions, security, etc) that will hopefully shed some light on this topic.

  • "Personally I don't want to depend on a hardcoded & fixed list (MimeMapping) or the need to call a native method and having the MIME media type registered on the server"



    Somehow, you have to get that content type from an API call or some docs you have lying around, no matter when it happens. I can see some theoretical advantages to embedding the content type, though it is probably a moot point, since all web servers have the ability to map common extensions to MIME types anyway (and come with like 200 mappings out of the box).



    "WebResourceAttribute is there specifically to cover the "hole" introduced by the WebResource handler..."



    This is where we would disagree. If it really is a security hole, then is introduced not by the WebResourceHandler but by the CLR itself. What I am suggesting is something like CAS for resources built into the CLR to address the issue of people randomly requesting "private" resources (regardless of if the request is directly from a method like GetManifestResourceStream or from some web resource handler).



    In any case, I definately look forward to your article. Maybe it will help explain why this method is so cool.

  • Do you know your blog doesn't display properly - think you've got your overflow on the main text set to hidden. Not very helpful.

Comments have been disabled for this content.