Asp.Net MVC页面静态化功能实现一:利用IHttpModule,摒弃ResultFilter

上一篇有提到利用IHttpModule和ResultFilter实现页面静态化功能。后来经过一些改动,将ResultFilter中要实现的功能全部转移到IHttpModule中来实现

Asp.Net MVC页面静态化功能实现一:利用IHttpModule和ResultFilter

1、改动后的自定义IHttpModule实现代码:

public class RouterHttpModule : IHttpModule
{
    public void Init(HttpApplication application)
    {
        application.BeginRequest += this.Application_BeginRequest; //注册事件
    }

    private void Application_BeginRequest(Object source, EventArgs e)
    {
        HttpApplication application = (HttpApplication)source;
        HttpContext context = application.Context;
        string filePath = context.Request.FilePath;
        string fileExtension = VirtualPathUtility.GetExtension(filePath);
        //如果当前请求的不是资源文件、不是后台管理、请求中没有表单信息、静态页面存在,则返回静态页面
        if (string.IsNullOrEmpty(fileExtension) && !filePath.ToLower().StartsWith("/admin") && context.Request.Form.Count.Equals(0))
        {
            string htmlPath = context.Request.HtmlFilePath();
            if (File.Exists(htmlPath))
            {
                context.Response.WriteFile(htmlPath);
                context.Response.End();
            }
            context.Response.Filter = new HttpResponseFilterWrapper(context.Response.Filter, context);
        }
    }

    public void Dispose() { }
}

这里只是在判断静态页面文件是否存在之后加了这样一段代码:context.Response.Filter = new HttpResponseFilterWrapper(context.Response.Filter, context);

2、HttpResponseFilterWrapper的实现代码

HttpResponseFilterWrapper的功能还跟以前一样,保存Filter中返回给客户端的html代码到服务器硬盘上

public class HttpResponseFilterWrapper : Stream
{
    private Stream inner;
    private HttpContext context;
    public HttpResponseFilterWrapper(System.IO.Stream s, HttpContext context)
    {
        this.inner = s;
        this.context = context;
    }

    public override bool CanRead
    {
        get { return inner.CanRead; }
    }

    public override bool CanSeek
    {
        get { return inner.CanSeek; }
    }

    public override bool CanWrite
    {
        get { return inner.CanWrite; }
    }

    public override void Flush()
    {
        inner.Flush();
    }

    public override long Length
    {
        get { return inner.Length; }
    }

    public override long Position
    {
        get{ return inner.Position; }
        set{ inner.Position = value; }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return inner.Read(buffer, offset, count);
    }

    public override long Seek(long offset, System.IO.SeekOrigin origin)
    {
        return inner.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        inner.SetLength(value);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        if (!context.Response.StatusCode.Equals(200))
        {
            return;
        }
        inner.Write(buffer, offset, count);
        //if (!context.HttpContext.Request.FilePath.ToLower().StartsWith("/admin"))
        //当前请求不是后台管理;并且返回客户端是html才生成静态页面
        if (!context.Request.FilePath.ToLower().StartsWith("/admin") && context.Response.ContentType.Equals("text/html"))
        {
            //静态页面保存路径信息
            string htmlPath = context.Request.HtmlFilePath();
            string direcHtmlPath = Path.GetDirectoryName(htmlPath);  
            if (!Directory.Exists(direcHtmlPath))
            {
                Directory.CreateDirectory(direcHtmlPath);
            }
            //获取返回客户端的html代码,并进行压缩处理
            string htmlCode = System.Text.Encoding.UTF8.GetString(buffer); 
            string isZipHtml = WebConfigInfo.GetConfigValueByKey("IsCompressed");
            //如果“IsCompressed”的值为空或0,则表示不压缩
            if (!string.IsNullOrEmpty(isZipHtml) && !isZipHtml.Equals(0))
            {
                htmlCode = Regex.Replace(htmlCode, "^\\s*", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
                htmlCode = Regex.Replace(htmlCode, "\\r\\n", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
                htmlCode = Regex.Replace(htmlCode, "<!--*.*?-->", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
            }
            //保存文件,这里不能用File.WriteAllText
            File.AppendAllText(htmlPath, htmlCode);
        }
    }
}

3、确保静态页面数据的及时性

当在后台管理系统中,对某个栏目或某篇文章进行过增删改的操作时,要同时保证静态页面中的数据与数据库中的数据保持一致。

最初想到的实现方法是在静态页面中设定一个过期时间,但是公司所使用的框架中内容信息是通过异步加载进来的,所以保存的静态页面中不存<html><head><body>标签,所以就放弃了这种方法。因为项目时间关系,最后偷了一点懒,在后台管理中添加一个“重新发布”的功能,直接将之前生成的静态页面全部删除掉。