首先解释一下什么是动态处理页面静态化

  对于需要静态化的页面,第一次访问某个Action时,会先执行Action,并在页面渲染后Response和服务器中网站的目录下都写入需要返回的html,而第二次访问此页时,在执行Action前,程序会先在指定目录下寻找是否存在当前请求对应的静态页面,如果有,则直接返回静态页面,如果没有,则按第一次访问此请求进行处理,即执行Action,并向Response和服务器中网站的目录下都写入需要返回的html。利用这种方式,可以在网站在请求的过程中,会动态的生成静态页面,而无需人工干预,方便快捷。

接下来直接上代码

    /// <summary>
    /// 页面静态化过滤器
    /// 思路:在执行Action前,先判断此Action返回的View()的静态文件是否存在
    ///       如果存在,则直接返回静态文件。
    ///       如果不存在,则利用OnResultExecuting,替换Response中的输出流,让渲染后的html写入到
    ///       本过滤器定义的StringBuilder中,然后在OnResultExecuted中(页面渲染后),从StringBuilder
    ///       中获取html,并同时写入到静态文件和Response的内置输出流中
    ///       
    /// 注:由于我没找到直接从Response中获取输出流的html的方法,因此我这里是替换了Response中的output属性,
    /// 替换后,渲染后的结果就会输出到我们自己写的StringBuilder中(此时Response中并没有html),此时在渲染后,
    /// 再把StringBuilder中的html,分别写入静态文件和Response中(tw.write方法),完成此次请求。
    /// </summary>
    public class StaticHtmlFilter : ActionFilterAttribute
    {
        //用于保存渲染后的html文本
        static StringBuilder sb;
        //这几个Writer照着写就行了
        static StringWriter sw;
        static HtmlTextWriter hw;
        static TextWriter tw;
        //自定义的静态页面的后缀名
        static string ext = ".html";
        //静态页面的绝对路径(包括后缀名)
        string fileName = null;
        ///静态页面的绝对路径(不包括后缀名)
        static string path = null;
        //静态文件是否存在
        bool FileExists = false;

        /// <summary>
        /// Action执行前,判断当前页面是否已经被静态化(Views路径下是否存在html文件)
        /// 如果存在静态文件则直接设置filterContext的result,即返回html作为结果,而不执行Action中代码
        /// 如果不存在静态页面文件,则不设置filterContext的result,此时将会执行Action中的代码
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //根据controller和action信息
            string controller = filterContext.RouteData.Values["controller"].ToString();
            string action = filterContext.RouteData.Values["action"].ToString();
            object id=null;
            //路由中是否包含可选参数id,如果有,则在文件名也要体现
            if  (!filterContext.RouteData.Values.TryGetValue("id", out id))
            {
                path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Views", controller, action);
                fileName = string.Format("{0}{1}", path, ext);
            }
            else
            {
                path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Views", controller, action);
                fileName = string.Format("{0}{1}{2}", path, id.ToString(),ext);
            }
            //拼装后缀名
           
            FileExists = File.Exists(fileName);
            //如果文件存在,直接返回结果
            if (FileExists)
            {
                filterContext.Result = new FileContentResult(File.ReadAllBytes(fileName), "text/html; charset=utf-8");
            }
        }
        /// <summary>
        /// 执行完Action后,但渲染页面前执行此处
        /// 渲染页面的意思是将cshtml中的后台代码,翻译为前台代码
        /// 
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
       
if (!FileExists) { //保存html sb = new StringBuilder(); //两个writer sw = new StringWriter(sb); hw = new HtmlTextWriter(sw); //记住Response中原本输出流,用于返回本次请求的html,与下一句配合使用 //在渲染结束后,向tw内写入html内容 tw = filterContext.RequestContext.HttpContext.Response.Output; //过滤器自己输出流,用于获取渲染后的html内容 filterContext.RequestContext.HttpContext.Response.Output = hw; } } public override void OnResultExecuted(ResultExecutedContext filterContext) { //如果是静态文件不存在 if (!FileExists) { //获取渲染后的html文本 string res = sb.ToString(); //将文本写入到静态文件中 new Action(() => File.WriteAllText(fileName, res)).BeginInvoke(null, null); //向Response的输出流中写入本次请求的html tw.Write(sb.ToString()); } } }

什么样的Action适合静态化?

我认为有两种Action需要使用静态化

1.登录页面等无需向Action中传入参数而直接返回View的Action需要静态处理。

 /// <summary>
        /// 登录
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        [StaticHtmlFilter]
        public ActionResult Login()
        {

            var model = new LoginDto
            {
                ReturnUrl = Request.QueryString["ReturnUrl"],
                LoginName = "admin",
                Password = "qwaszx"
            };
            if (User.Identity.IsAuthenticated)
            {
                if (model.ReturnUrl.IsNotBlank())
                    return Redirect(model.ReturnUrl);
                return RedirectToAction("Index");
            }
            return View(model);
        }
View Code

 

2.通过一个参数进行查询的Action(注意是查询,非编辑)

        /// <summary>
        /// 编辑
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [StaticHtmlFilter]
        public async Task<ActionResult> Edit(string id)
        {
            var model = await _menuService.Find(id);
            return View(model);
        }
View Code

 

注意事项

  对于经常需要编辑的内容的查询页面,如商品列表如使用动态处理静态化页面,则应在编辑商品信息后,删除服务器指定目录下的静态页面,以便于页面更新。

  当然我们也可以在上面的过滤器中的 OnActionExecuting 方法在判断文件是否存在时,先判断本次请求是否是编辑操作,如果是则删除相应的静态文件重新生成即可。

注:有些地方我想得还不够充分,希望大家多提意见。