ASP.net обработчик Ashx не кэшируется - PullRequest
1 голос
/ 14 сентября 2011

Мы недавно установили Robohash как отличный запасной вариант для Gravatar:

enter image description here

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

Как вы можете видеть, робоша блестяще выглядят и обслуживаются из нашего статического домена. URL фактически переписан:

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

И в том же файле web.config у нас есть профили кеша:

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

Для хорошей меры в файле gravatar.ashx мы также установили политику кэширования:

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

}

Обратите внимание, что мы используем Response.cache AND CachePolicy внизу

Когда я использую YSlow, каждое изображение на странице имеет будущую дату окончания срока действия, ЗА ИСКЛЮЧЕНИЕМ этих аватаров, у которых нет даты окончания срока действия. Каждый раз, когда запрашивается страница, они загружаются снова.

Идея скрипта состоит в том, чтобы получить аватар с внешнего URL и кэшировать его в течение 1 часа. Затем он подается с нашего сайта.

Может кто-нибудь помочь нам здесь? Пример страницы с аватарами, которые используются без кеша: http://www.scirra.com/forum/construct-in-ludum-dare-21_topic44523.html

1 Ответ

1 голос
/ 14 сентября 2011

Поскольку вы используете обработчик, вам нужно обрабатывать заголовки кэширования в своем собственном коде, то есть возвращать ответ 304 также. Так что сверьтесь с датами в заголовках для файлов и верните 304, где это уместно.

У нас есть код, подобный следующему в начале некоторых наших обработчиков изображений

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

два используемых метода выглядят примерно так, как показано ниже.

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