原博地址:http://www.cnblogs.com/mysweet/archive/2012/05/26/2519686.html
今天闲下来写篇日志,页面静态化.
页面静态化是我们经常碰到的问题,在web中,要说速度,只有html静态页面最快,所以页面静态化对于web网站来说,是一个非常好的减少请求降低服务器压力的方式.
而常规的html静态页面也有很多问题,比如不能像php,aspx,jsp页面那样轻松的和数据库交互.
在以前,html静态化都是,对于一些子页面(比如新闻内容等),一旦发布基本上很少进行修改的页面,就可以通过数据库读取,然后生成html,
这种方式性能最好,而且对SEO友好,但不灵活,只对于那些很少修改的页面,如果页面发生修改又需要重新生成,这对于硬盘也是一种伤害.
于是乎,动态语言横行.
ajax技术的出现,再度让页面进入静态化.
html页面中大量的ajax,即有很高的灵活性,而且达到的降低服务器压力减少请求的目的.
但是这样也会造成SEO的问题,所以,对于一些需要SEO的页面就不要用ajax实现了.
那么哪些适合页面静态化呢?
对于那些私人的空间
比如:会员空间,大师空间,企业空间,登录,注册等等,这些地方,就不需要.
但是还有一个问题,对于这些页面静态化,我们经常性都是先做aspx页面(母板页,用户控件)等,最后做页面静态化操作,我们就需要重新把那些动态页面的链接全部改成静态,这就太痛苦了,这时候我们就需要事先写一个URL页面静态化管理,所有的链接经过这个URLManage.GetURL处理.
1: /// <summary>
2: /// 获得路径(暂时只做静态页面管理)(/*在这里可以扩展出URL重写*/)
3: /// </summary>
4: /// <param name="PageUrl">页面的URL(不包括扩展名)</param>
5: /// <param name="QueryString">页面参数</param>
6: /// <returns></returns>
7: public static string GetURL(string PageUrl,string QueryString)
8: {
9: //页面路径
10: string PagePath = "";
11:
12: //如果当前的参数不为空,则加上?
13: if (QueryString != "")
14: QueryString = "?" + QueryString;
15: //如果是静态页面(从配置文件中读取静态页面状态(是否))
16: if (ReadURLConfig(PageUrl) == true)
17: {
18: PagePath = PageUrl + ".htm";
19: }
20: //如果是动态页面
21: else
22: PagePath = PageUrl + ".aspx";
23: //把相对路径转化为绝对路径
24: return System.Web.VirtualPathUtility.ToAbsolute(PagePath)+QueryString ;
25: }
26: /// <summary>
27: /// 从配置文件中读取是否生成静态页面
28: /// </summary>
29: /// <param name="PageName">页面的名称</param>
30: /// <returns></returns>
31: public static bool ReadURLConfig(string PageURL)
32: {
33: //读取配置文件
34: string path = HttpContext.Current.Server.MapPath(@"~/Admin/ConfigManage/URLConfig.xml");
35: //XmlHelper.Read(path, "/Node/Element[@Attribute='Name']", "Attribute")
36: //是否生成HTML
37: string IsHtml="false";
38: IsHtml=XMlHelper.Read(path, "/PageSettings/Page[@PageURL='"+PageURL+"']", "IsHtml");
39: if (IsHtml.ToLower() == "true")
40: {
41: return true;
42: }
43: else return false;
44:
45: }
46:
这个类主要是帮助我们可以直接从配置文件中读取关于文件页面静态化的信息
对于我们大量的ajax页面(可能涉及到超链接),我们也需要通过这个URLManage来获取他的路径,由于js不能调用asp.net中的函数,所以我们需要在每个页面的开头定义,js中需要用到的超链接.
![image image](https://i-blog.csdnimg.cn/blog_migrate/2d9dda6d9136654a1b21985baf73bdf8.png)
看看它的ajax文件中是怎么写的.
这时候,我们的前台的工作就完成了,主要是后台的页面静态化管理的设计了,
后台的页面静态化管理,主要实现,通过树形菜单的形式,可以自由选择,那些页面生成静态页面,方便测试和维护.
不过这里,少处理了关于新闻内容的页面静态化,新闻内容的页面静态化则是采用的第一种方法,完全的页面静态化,从数据库读取出数据,全部生成的方式.
主要效果:
把需要页面静态化的页面写在配置文件中,然后通过这个页面静态化管理,进行页面静态化.
这样做的好处就是方便开发和维护,可以很轻松的管理静态页面.
主要技术:
1.XML操作以及LINQ简单应用,
2.页面静态化,只需要通过简单的WebClient下载动态页面就达到了页面静态化的目的.简单方便.
WebClient下载文件类
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Net;
6: using System.Web;
7: using System.IO;
8:
9: namespace Common
10: {
11: /// <summary>
12: /// 用webClient下载文件
13: /// </summary>
14: public partial class DownFile
15: {
16: #region 生成静态页面
17: /// <summary>
18: /// 生成静态页面
19: /// 调用实例:
20: /// Common.DownFile webclient = new Common.DownFile();
21: /// string RequestVirtualUrl= "/News/ViewNews.aspx?NewsId="+Info.Id;
22: /// string SaveVirtualPath = "~/News/" + Info.Id + ".htm";
23: /// webclient.CreateStaticByWebClient(RequestVirtualUrl, SaveVirtualPath);
24: /// </summary>
25: /// <param name="VirtualRequestUrl">要请求的虚拟路径,例如: "/News/ViewNews.aspx?NewsId="+Info.Id;</param>
26: /// <param name="SaveVirtualPath">要保存的虚拟路径,例如:"~/News/" + Info.Id + ".htm";</param>
27: public static void CreateStaticByWebClient(string VirtualRequestUrl, string SaveVirtualPath)
28: {
29: WebClient wc = new WebClient();
30: wc.Encoding = Encoding.UTF8;
31: //通过WebClient向服务器发Get请求,把服务器返回的html内容保存到磁盘上,以后用户直接请html文件请求.
32: string AppVirtualPath = HttpContext.Current.Request.ApplicationPath;
33: //由于网站应用程序虚拟目录是/czcraft,而传递过来/News是正确的,
34: //但是发布到iis上面,虚拟路径就是/,而传递过来的确实/News,路径就出错了,
35:
36: if (AppVirtualPath == "/")
37: {
38: AppVirtualPath = "";
39: }
40: string FilePath = HttpContext.Current.Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority + AppVirtualPath + VirtualRequestUrl;
41:
42: //保存路径
43: string SaveFilePath = HttpContext.Current.Server.MapPath(SaveVirtualPath);
44:
45: //下载并保存文件
46: wc.DownloadFile(FilePath, SaveFilePath);
47:
48: }
49: #endregion
50: #region 文件删除
51: /// <summary>
52: /// 文件删除
53: /// </summary>
54: /// <param name="VirtualFilePath">文件虚拟路径</param>
55: public void FileDelete(string VirtualFilePath)
56: {
57: //物理路径
58: string RealFilePath = HttpContext.Current.Server.MapPath(VirtualFilePath);
59:
60: //如果文件存在则删除
61: if (File.Exists(VirtualFilePath))
62: {
63: File.Delete(VirtualFilePath);
64: }
65: }
66: #endregion
67: }
68: }
页面静态化管理类(URLXMLInfoManage):
1: using System;
2: using System.Data;
3: using System.Configuration;
4: using System.Linq;
5: using System.Web;
6: using System.Web.Security;
7: using System.Web.UI;
8: using System.Web.UI.HtmlControls;
9: using System.Web.UI.WebControls;
10: using System.Web.UI.WebControls.WebParts;
11: using System.Xml.Linq;
12: using System.Xml;
13: using System.Text;
14: using System.Collections;
15: using System.Collections.Generic;
16: using System.IO;
17: using Newtonsoft.Json;
18:
19: /// <summary>
20: ///URLXMLInfoManage 的摘要说明
21: /// </summary>
22: public class URLXMLInfoManage
23: {
24: #region 字段
25: //单例模式
26: public static readonly URLXMLInfoManage XMLInfo = new URLXMLInfoManage();
27: /// <summary>
28: /// 路径(XML)
29: /// </summary>
30: private static readonly string XMLPath = "~/Admin/ConfigManage/URLConfig.xml";
31: #endregion
32: #region 实例化
33: /// <summary>
34: /// 私有实例化
35: /// </summary>
36: private URLXMLInfoManage()
37: {
38: }
39: /// <summary>
40: /// 实例化(静态)
41: /// </summary>
42: /// <param name="path">xml的路径</param>
43: /// <returns></returns>
44: public static URLXMLInfoManage Instance()
45: {
46: return XMLInfo;
47: }
48: #endregion
49: #region 通过页面信息(返回json)
50: /// <summary>
51: /// 通过页面信息(返回json)
52: /// </summary>
53: /// <returns></returns>
54: public string GetURLInfoForJson()
55: {
56:
57: //加载XML
58: XDocument xDoc = XDocument.Load(HttpContext.Current.Server.MapPath(XMLPath));
59:
60: IEnumerable<XElement> PageList = xDoc.Root.Descendants("Page");
61: //linq分组(根据xml中Page的Type的名称分组
62: var group = PageList.GroupBy(page => page.Attribute("Type").Value);
63: //输出json格式数据
64: StringBuilder json = new StringBuilder();
65: StringWriter sw = new StringWriter(json);
66: using (JsonWriter jsonWriter = new JsonTextWriter(sw))
67: {
68: jsonWriter.Formatting = Newtonsoft.Json.Formatting.Indented;
69: jsonWriter.WriteStartArray();
70: foreach (IGrouping<string, XElement> item in group)
71: {
72: jsonWriter.WriteStartObject();
73: //-1代表不存在的id
74: jsonWriter.WritePropertyName("id");
75: jsonWriter.WriteValue(-1);
76: jsonWriter.WritePropertyName("text");
77: jsonWriter.WriteValue(item.First().Attribute("TypeName").Value);
78: jsonWriter.WritePropertyName("expanded");
79: jsonWriter.WriteValue(false);
80: jsonWriter.WritePropertyName("children");
81:
82: jsonWriter.WriteStartArray();
83: foreach (XElement XElem in item)
84: {
85: //页面名称
86: string PageName = XElem.Attribute("PageName").Value;
87: //页面URL
88: string PageURL = XElem.Attribute("PageURL").Value;
89: //页面标识
90: string PageId = XElem.Attribute("Id").Value;
91: //是否是html页面
92: bool IsHtml = Convert.ToBoolean(XElem.Attribute("IsHtml").Value);
93: jsonWriter.WriteStartObject();
94: jsonWriter.WritePropertyName("id");
95: jsonWriter.WriteValue(PageId);
96: jsonWriter.WritePropertyName("text");
97: jsonWriter.WriteValue(PageName);
98: jsonWriter.WritePropertyName("expanded");
99: jsonWriter.WriteValue(IsHtml);
100: jsonWriter.WriteEndObject();
101: }
102: jsonWriter.WriteEndArray();
103:
104: jsonWriter.WriteEndObject();
105:
106: }
107: jsonWriter.WriteEndArray();
108:
109:
110:
111:
112: }
113: return json.ToString();
114:
115:
116: }
117:
118: #endregion
119: #region 设置页面静态化信息
120: /// <summary>
121: /// 设置页面静态化信息
122: /// </summary>
123: /// <param name="Ids"></param>
124: /// <returns></returns>
125: public bool SetURLInfo(string Ids)
126: {
127: //获取URL的Id
128: string[] IdList = Ids.Split(',');
129: //加载XML
130: XDocument xDoc = XDocument.Load(HttpContext.Current.Server.MapPath(XMLPath));
131:
132: IEnumerable<XElement> PageList = xDoc.Root.Descendants("Page");
133: foreach (XElement Page in PageList)
134: {
135: //默认不生成HTML页面
136: Page.SetAttributeValue("IsHtml", false);
137: foreach (string Id in IdList)
138: {
139:
140: if (Id == Page.Attribute("Id").Value)
141: {
142: //页面静态化
143: CreateHTML(Page.Attribute("PageURL").Value);
144: //写回XML中
145: Page.SetAttributeValue("IsHtml", true);
146: break;
147: }
148: }
149: }
150: xDoc.Save(HttpContext.Current.Server.MapPath(XMLPath));
151: return true;
152: }
153: #endregion
154: #region 页面静态化(不适合文章等纯HTML)
155: /// <summary>
156: /// 页面静态化(不适合文章等纯HTML)
157: /// </summary>
158: /// <param name="PathURL"></param>
159: /// <returns></returns>
160: public bool CreateHTML(string PathURL)
161: {
162:
163: //保存路径
164: string SavePath = PathURL + ".htm";
165: //请求路径(删除前缀的~标识)
166: string RequestPath = PathURL.TrimStart('~')+".aspx";
167: //下载文件(原路径保存)
168: Common.DownFile.CreateStaticByWebClient(RequestPath, SavePath);
169: return true;
170: }
171: #endregion
172: }
这里涉及到一个LINQ,以前没怎么用过LINQ,仅仅是会点基本语法,这次,感觉真是挺强大的,简洁清晰.
//查找所有的Page节点
IEnumerable<XElement> PageList = xDoc.Root.Descendants("Page");
//linq分组(根据xml中Page的Type的名称分组)
var group = PageList.GroupBy(page => page.Attribute("Type").Value);
不过,groupby的接口是IGrouping<string, XElement>,group的key就是Type,value就是Type相同的Page节点
以前我们也写过类似的分组的处理,可以对比一下:
下面这个是根据类别,输出前8个产品,跟上面是类似的.
可以看到LINQ的简洁,LINQ和Lambda表达式,更简洁,更清晰也更容易理解.
1: #region 根据企业id查找企业的产品信息(每种分别显示前8个)
2: /// <summary>
3: /// 根据企业id查找企业的产品信息(每种分别显示前8个)
4: /// </summary>
5: /// <param name="CompanyId"></param>
6: /// <returns></returns>
7: public string GetCompanyWorkForJson(string CompanyId)
8: {
9: //查询状态
10: bool Status = false;
11: //获取企业的产品信息(每种显示前8个)
12: DataTable dtListProduct = new VProductCraftTypeDAL().ListAllByCompanyIdToDatable(CompanyId);
13: //转化为json格式
14: StringBuilder json = new StringBuilder();
15: StringWriter sw = new StringWriter(json);
16:
17: using (JsonWriter jsonWriter = new JsonTextWriter(sw))
18: {
19:
20: jsonWriter.Formatting = Formatting.Indented;
21: //判断数据读取状态
22: if (dtListProduct.Rows.Count > 0)
23: {
24: Status = true;
25: }
26: jsonWriter.WriteStartObject();
27: jsonWriter.WritePropertyName("Status");
28: jsonWriter.WriteValue(Status);
29: jsonWriter.WritePropertyName("Data");
30:
31: jsonWriter.WriteStartArray();
32: if (Status == true)
33: {
34: //先输出第一个元素的类别信息
35: jsonWriter.WriteStartObject();
36: jsonWriter.WritePropertyName("TypeId");
37: jsonWriter.WriteValue(dtListProduct.Rows[0]["TypeId"].ToString());
38: jsonWriter.WritePropertyName("TypeName");
39: jsonWriter.WriteValue(dtListProduct.Rows[0]["TypeName"].ToString());
40: //第一个元素的开始
41: jsonWriter.WritePropertyName("Product");
42: jsonWriter.WriteStartArray();
43:
44: //按照类别分组
45: //产品计数(一个分组下的产品,从1开始算起)
46:
47: for (int num = 0, numProduct = 1; num < dtListProduct.Rows.Count; num++, numProduct++)
48: {
49:
50: //获取该类别下的分组总个数
51: int Total = Convert.ToInt32(dtListProduct.Rows[num]["total"]);
52: //如果该类别下还存在未输出的产品
53: if (numProduct <= Total)
54: {
55:
56:
57: jsonWriter.WriteStartObject();
58: jsonWriter.WritePropertyName("ProductId");
59: jsonWriter.WriteValue(dtListProduct.Rows[num]["Id"].ToString());
60: jsonWriter.WritePropertyName("Name");
61: jsonWriter.WriteValue(dtListProduct.Rows[num]["Name"].ToString());
62: jsonWriter.WritePropertyName("SimpleName");
63: jsonWriter.WriteValue(dtListProduct.Rows[num]["SimpleName"].ToString());
64: jsonWriter.WritePropertyName("Lsprice");
65: jsonWriter.WriteValue(dtListProduct.Rows[num]["Lsprice"].ToString());
66: jsonWriter.WritePropertyName("Picturepath");
67: jsonWriter.WriteValue(dtListProduct.Rows[num]["Picturepath"].ToString());
68: jsonWriter.WriteEndObject();
69:
70: }
71: else
72: {
73: //将该类别的产品计数重置为1
74: numProduct = 1;
75: //这里给上一个类别的产品结束标记
76:
77: jsonWriter.WriteEndArray();
78: jsonWriter.WriteEndObject();
79:
80: jsonWriter.WriteStartObject();
81: jsonWriter.WritePropertyName("TypeId");
82: jsonWriter.WriteValue(dtListProduct.Rows[num]["TypeId"].ToString());
83: jsonWriter.WritePropertyName("TypeName");
84: jsonWriter.WriteValue(dtListProduct.Rows[num]["TypeName"].ToString());
85: //如果还存在产品
86: if (num < dtListProduct.Rows.Count)
87: {
88: //下一个元素的开始
89: jsonWriter.WritePropertyName("Product");
90: jsonWriter.WriteStartArray();
91:
92: }
93:
94: }
95: }
96: }
97:
98:
99: jsonWriter.WriteEndArray();
100: jsonWriter.WriteEndObject();
101:
102: }
103: return json.ToString();
104: }
105: #endregion
接下来我们就可以写,UI层和业务层了,
UI层仍然是用的MUNIUI框架(以后再也不用这个了,其实,不太好用,感觉,换个其他的JQuery框架),
1:
2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3: <html xmlns="http://www.w3.org/1999/xhtml">
4: <head>
5: <title>页面管理(静态页面生成管理)</title>
6: <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
7: <link href="../css/demo.css" rel="stylesheet" type="text/css" />
8:
9: <script src="../scripts/jquery-1.6.2.min.js" type="text/javascript"></script>
10:
11: <script src="../scripts/miniui/miniui.js" type="text/javascript"></script>
12:
13: <link href="../scripts/miniui/themes/default/miniui.css" rel="stylesheet" type="text/css" />
14: <link href="../scripts/miniui/themes/icons.css" rel="stylesheet" type="text/css" />
15: </head>
16: <body>
17: <h1>
18: 页面静态化管理</h1>
19: <ul id="tree2" class="mini-tree" url="Data/UrlInfo.ashx?method=GetURLInfo" style="width: 300px;"
20: showtreeicon="true" textfield="text" idfield="id" showcheckbox="true"
21: checkrecursive="true">
22: </ul>
23: <br />
24:
25: <a class="mini-button" iconCls=" icon-new" href="javascript:ToHtml()">页面静态化</a>
26: <!-- <input type="button" value="setCheckedNodes" οnclick="setCheckedNodes()" />
27: <input type="button" value="getCheckedNodes" οnclick="getCheckedNodes()" />-->
28: <br />
29:
30: <script type="text/javascript">
31:
32:
33: function getCheckedNodes() {
34: var tree = mini.get("tree2");
35: var value = tree.getValue();
36: alert(value);
37:
38: }
39: //页面静态化
40: function ToHtml(){
41: var tree = mini.get("tree2");
42: var value = tree.getValue();
43: if(value==null){
44: alert("请选择要生成的静态页面的页面名称");
45: return;
46: }
47: else{
48: //alert(value);
49: $.ajax({
50: url: "Data/UrlInfo.ashx?method=ToHtml",
51: data:{Id:value},
52: cache: false,
53: success: function (text){
54: if(text)
55: {
56: alert("页面HTML生成成功!");
57: }
58: }
59: });
60:
61: }
62:
63: }
64: //--------------------------------
65: function onBeforeCheckNode(e) {
66: var tree = e.sender;
67: var node = e.node;
68: if (tree.hasChildren(node)) {
69: //e.cancel = true;
70: }
71: }
72:
73: </script>
74:
75:
76: </body>
77: </html>
78:
后台Ashx页面:
1: <%@ WebHandler Language="C#" Class="UrlInfo" %>
2:
3: using System;
4: using System.Web;
5: using czcraft.BLL;
6: using czcraft.Model;
7: using Common;
8: using System.Collections.Generic;
9: using Newtonsoft.Json.Linq;
10: public class UrlInfo : IHttpHandler {
11:
12:
13: public void ProcessRequest(HttpContext context)
14: {
15: String methodName = context.Request["method"];
16: if (!string.IsNullOrEmpty(methodName))
17: CallMethod(methodName, context);
18: }
19: /// <summary>
20: /// 根据业务需求调用不同的方法
21: /// </summary>
22: /// <param name="Method">方法</param>
23: /// <param name="context">上下文</param>
24: public void CallMethod(string Method, HttpContext context)
25: {
26: switch (Method)
27: {
28: case "GetURLInfo":
29: GetURLInfo(context);
30: break;
31: case "ToHtml":
32: ToHtml(context);
33: break;
34: default:
35: return;
36:
37:
38: }
39: }
40: /// <summary>
41: /// 页面html
42: /// </summary>
43: /// <param name="context"></param>
44: public void ToHtml(HttpContext context)
45: {
46: string Ids= context.Request["Id"];
47:
48: URLXMLInfoManage xmlManage = URLXMLInfoManage.Instance();
49: //写回XML中
50: context.Response.Write(xmlManage.SetURLInfo(Ids));
51: }
52: /// <summary>
53: /// 获取URL信息(生成html的)
54: /// </summary>
55: /// <param name="context"></param>
56: public void GetURLInfo(HttpContext context)
57: {
58: URLXMLInfoManage xmlManage = URLXMLInfoManage.Instance();
59:
60: context.Response.Write(xmlManage.GetURLInfoForJson());
61: }
62: public bool IsReusable {
63: get {
64: return false;
65: }
66: }
67:
68: }
页面静态化效果:
可以看到首页和右下角的超链接,后缀都变成了htm,而且,项目中可以看到都生成了html页面
最近再看3本好书,一本是.net设计规范
极品,看了之后才知道代码命名等等问题.
还有一本是博客园的. 小洋(燕洋天)写的,蛮不错的,叫做.net应用架构设计原则,模式与实践
比较适合我这种,web对于架构方面有点感觉,但是不知道怎么样设计的好,扩展性好的孩纸.
还有一本就是C# in Depth
买了本英文版,慢慢品读,学英语,英语太差真吃亏呀!
接下来就是写一个缓存管理,
以前设计的缓存,只适合asp.net的Cache,但是asp.net的缓存,不适合大型构架,大型构架都会把缓存单独放在缓存服务器中,经常性,别人扩展到大型应用就麻烦了,我们需要考虑周全就需要写一个可扩展性的缓存结构,
MemCache是一个很强力的高性能分布式缓存系统.
接下来就准备设计一个缓存管理组件,用来管理缓存,如果网站扩大可以轻松的把缓存往MemCache上移植.