开发者

ASP.net ashx handler not caching

开发者 https://www.devze.com 2023-04-04 12:26 出处:网络
We recently installed Robohash as a great fallback for Gravatar: http://static2.scirra.net/avatars/128/5df4bf5d460c9497fdb35578e923ad1f.png

We recently installed Robohash as a great fallback for Gravatar:

ASP.net ashx handler not caching

http://static2.scirra.net/avatars/128/5df4bf5d460c9497fdb35578e923ad1f.png

As you can see robohashes are hilariously brilliant and served from our static domain. The URL is actually rewritten:

<action type="Rewrite" url="gravatar.ashx?hash={R:2}&amp;size={R:1}" appendQueryString="false" />

And in the 开发者_JAVA百科same web.config file we have the cache profiles:

<staticContent>
    <clientCache httpExpires="Sun, 29 Mar 2020 00:00:00 GMT" cacheControlMode="UseExpires" />
</staticContent>
<caching>
    <profiles>
        <add extension=".ashx" policy="CacheForTimePeriod"  kernelCachePolicy="DontCache" duration="01:00:00" />
        <add extension=".png" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange" location="Any" />
        <add extension=".jpg" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange" location="Any" />
        <add extension=".gif" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange" location="Any" />
        <add extension=".ico" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange" location="Any" />
    </profiles>
</caching>

For good measure in the gravatar.ashx file we also set a cache policy:

<%@ WebHandler Language="C#" Class="GravatarImage" %>

using System;
using System.Web;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Net;

public class GravatarImage : IHttpHandler {

    public void ProcessRequest (HttpContext context) {

        // Adds document content type        
        context.Response.Cache.SetCacheability(HttpCacheability.Public);
        context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(60));
        context.Response.Cache.SetMaxAge(new TimeSpan(1, 0, 0));
        context.Response.AddHeader("Last-Modified", DateTime.Now.ToLongDateString());

        // Get params and send initial request to gravatar
        string Hash = context.Request.QueryString["hash"];
        string Size = context.Request.QueryString["size"];
        string URL = "http://www.gravatar.com/avatar/" + Hash + "?r=pg&s=" + Size + "&d=404";

        // Make request to gravatar
        bool Fetched = makeAvatarRequest(URL);

        // Backup to robo hash
        if (!Fetched)
        {
            URL = "http://robohash.org/" + Hash + ".png?size=" + Size + "x" + Size + "&bgset=bg2";
            Fetched = makeAvatarRequest(URL);
        }

        // Fallback if gravatar doesn't match and robohash is down
        if (!Fetched)
        {

        }

        // Cache this handler response for 1 hour.
        HttpCachePolicy c = context.Response.Cache;
        c.SetCacheability(HttpCacheability.Public);
        c.SetMaxAge(new TimeSpan(1, 0, 0));
    }

    // Attempt a request for avatar
    private bool makeAvatarRequest(string URL)
    {        
        try
        {
            WebRequest request = WebRequest.Create(URL);
            using (WebResponse response = request.GetResponse())
            {
                using (Stream responseStream = response.GetResponseStream())
                {
                    displayImage(responseStream);
                    return true;
                }
            }
        }
        catch (WebException ex)
        {
            return false;
        }
    }

    // Display the image from stream
    private void displayImage(Stream stream)
    {
        HttpContext.Current.Response.ContentType = "image/png";
        Image img = Image.FromStream(stream);
        MemoryStream temp = new MemoryStream();
        img.Save(temp, ImageFormat.Png);
        byte[] buffer = temp.GetBuffer();
        HttpContext.Current.Response.OutputStream.Write(buffer, 0, buffer.Length);

        img.Dispose();
        temp.Dispose();        
    }

    public bool IsReusable {
        get {
            return false;
        }
    }

}

Note we use Response.cache AND CachePolicy at the bottom

When I use YSlow, every image on the page has a future expiry date, EXCEPT these avatars which have no expiry date. Every time a page is requested they are fetched again.

The idea of the script is to fetch the avatar from the external URL and cache it for 1 hour. It's then served from our website.

Can anyone help us out here? An example page with the avatars being used without cache is: http://www.scirra.com/forum/construct-in-ludum-dare-21_topic44523.html


Because you are using a handler you need to handle the caching headers in your own code, ie returning the 304 repsonse as well. So check the dates on the headers against the files and return 304's where appropriate.

We have code similar to the following at the start of some our image handlers

var lastModified = this.LastModifiedFileTime(path);
var isNotModified = this.WriteConditional304(context, lastModified);
if (isNotModified)
    return;

the two methods used look roughly as below.

protected bool WriteConditional304(HttpContext context, DateTime lastWrite)
    {
        if (context.Request.Headers[since] != null || context.Request.Headers[eTag] != null)
        {
            try
            {
                DateTime date = context.Request.Headers[since] != null ? DateTime.Parse(context.Request.Headers[since]) : new DateTime(long.Parse(context.Request.Headers[eTag]));

                if (lastWrite <= date)
                {
                    Write304(context);
                    return true;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        return false;
    }



protected DateTime LastModifiedFileTime(string path)
    {
        FileInfo fi = new FileInfo(path);
        var modificationTime = fi.LastWriteTime;
        // negates the smaller parts of the date as the header doesnt carry them
        var date = new DateTime(modificationTime.Year, modificationTime.Month, modificationTime.Day, modificationTime.Hour,
            modificationTime.Minute, modificationTime.Second);
        return date;
    }
0

精彩评论

暂无评论...
验证码 换一张
取 消