Razor是ASP.NET MVC 3中新加入的技術,以做爲ASPX引擎的一個新的替代項。在早期的MVC版本中默認使用的是ASPX模板引擎,Razor在語法上的確不錯,用起來很是方便,簡潔的語法與.NET Framework 結合,普遍應用於ASP.NET MVC 項目。html
咱們在不少項目開發中會經常用到頁面靜態化,頁面靜態化有許多方式,最多見的就是相似不少PHP CMS種使用的 標籤替換的方式(如:帝國CMS、EcShop等),還有不少都是僞靜態,僞靜態咱們就不作過多解釋,經過路由或Url重寫來實現就能夠了。Razor爲咱們提供了更加方便的模板解析方式,任何東西都是兩方面的,技術也是如此,Razor解析模板雖然更加方便、簡潔,可是對於模板製做人員來講也是有必定的技術要求,或者對於開發一套模板製做功能來講,考慮的要更多一些。咱們再也不去探究這些問題,咱們更注重哪一種技術更容易、更方便、更好的知足咱們項目的需求。git
今天來簡單介紹一下如何使用RazorEngine解析模板生成靜態頁面,RazorEngine它是基於微軟的Razor之上,包裝而成的一個能夠獨立使用的模板引擎。也就是說,保留了Razor的模板功能,可是使得Razor脫離於Asp.net MVC,可以在其它應用環境下使用,項目地址:https://github.com/Antaris/RazorEngine程序員
首先咱們去codeplex上下兩個須要的dll http://razorengine.codeplex.comgithub
看到網上不少介紹RazorEngine的基礎用法的,講解的都比較詳細,對於RazorEngine運行原理很清晰,咱們在這裏就不重複介紹了。寫這篇文章是對於不少新手同窗來講比較喜歡「拿來主義」,基本的用法原理都能看懂,可是如何應用到項目中仍是有些不是很清晰,咱們只講講如何在項目中運用。spring
本文分爲兩部分:第一個部分,基本的單數據模型模板解析;第二部分,面向接口的多數據模型模板解析app
第一個部分 基本的單數據模型模板解析學習
1、咱們建立一個MVC項目,而且添加上面的兩個DLL引用,而後咱們新建一個簡單的文章類測試
public class Articles { /// <summary> /// 文章ID /// </summary> public int Id { get; set; } /// <summary> /// 文章標題 /// </summary> public string Title { get; set; } /// <summary> /// 文章內容 /// </summary> public string Content { get; set; } /// <summary> /// 做者 /// </summary> public string Author { get; set; } /// <summary> /// 發佈時間 /// </summary> public DateTime CreateDate { get; set; } }
2、咱們新建一個Razor的Html模板大數據
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>@Model.Title</title> </head> <body> <h1>@Model.Title</h1> <p>做者:@Model.Author - 發佈時間:@Model.CreateDate</p> <p>@Raw(Model.Content)</p> </body> </html>
說明:Model就是咱們的文章實體類 在MVC的試圖頁cshtml中 咱們通常都是在控制器裏傳遞這個實體類 而後在視圖頁中 @model Models.Articles 來接收這個實體類 而後經過「@Model.」來輸出內容,在Razor模板中是同樣的,只是不用@model Models.Articles 來接收了,其它的語法跟在.cshtml試圖頁中是同樣的,這麼說多餘了,由於寫法不同他就不是Razor了ui
3、咱們寫一個方法來獲取模板頁的Html代碼
/// <summary> /// 獲取頁面的Html代碼 /// </summary> /// <param name="url">模板頁面路徑</param> /// <param name="encoding">頁面編碼</param> /// <returns></returns> public string GetHtml(string url, System.Text.Encoding encoding) { byte[] buf = new WebClient().DownloadData(url); if (encoding != null) return encoding.GetString(buf); string html = System.Text.Encoding.UTF8.GetString(buf); encoding = GetEncoding(html); if (encoding == null || encoding == System.Text.Encoding.UTF8) return html; return encoding.GetString(buf); } /// <summary> /// 獲取頁面的編碼 /// </summary> /// <param name="html">Html源碼</param> /// <returns></returns> public System.Text.Encoding GetEncoding(string html) { string pattern = @"(?i)\bcharset=(?<charset>[-a-zA-Z_0-9]+)"; string charset = Regex.Match(html, pattern).Groups["charset"].Value; try { return System.Text.Encoding.GetEncoding(charset); } catch (ArgumentException) { return null; } }
4、咱們寫一個方法 用於生成Html靜態頁
/// <summary> /// 建立靜態文件 /// </summary> /// <param name="result">Html代碼</param> /// <param name="createpath">生成路徑</param> /// <returns></returns> public bool CreateFileHtmlByTemp(string result, string createpath) { if (!string.IsNullOrEmpty(result)) { if (string.IsNullOrEmpty(createpath)) { createpath = "/default.html"; } string filepath = createpath.Substring(createpath.LastIndexOf(@"\")); createpath = createpath.Substring(0, createpath.LastIndexOf(@"\")); if (!Directory.Exists(createpath)) { Directory.CreateDirectory(createpath); } createpath = createpath + filepath; try { FileStream fs2 = new FileStream(createpath, FileMode.Create); StreamWriter sw = new StreamWriter(fs2, new System.Text.UTF8Encoding(false));//去除UTF-8 BOM sw.Write(result); sw.Close(); fs2.Close(); fs2.Dispose(); return true; } catch { return false; } } return false; }
5、咱們來寫個方法調用靜態模板,而且傳遞數據模型實體類 建立Html靜態頁
/// <summary> /// 解析模板生成靜態頁 /// </summary> /// <param name="temppath">模板地址</param> /// <param name="path">靜態頁地址</param> /// <param name="t">數據模型</param> /// <returns></returns> public bool CreateStaticPage(string temppath, string path, RazorEngineTemplates.Models.Articles t) { try { //獲取模板Html string TemplateContent = GetHtml(temppath, System.Text.Encoding.UTF8); //初始化結果 string result = string.Empty; //解析模板生成靜態頁Html代碼 result = Razor.Parse(TemplateContent, t); //建立靜態文件 return CreateFileHtmlByTemp(result, path); } catch (Exception e) { throw e; } }
好了,大功告成,是否是很簡單。
這裏只是一個很簡單的應用,沒有讀取數據,也沒有列表,只有一個文章數據模型,下一部分咱們將介紹 多模型模板解析,由於是多模型 因此 生成靜態頁面的時候 就不是傳遞一個具體模型實體類 咱們會用到 反射,經過反射模型屬性 獲取數據,有不熟悉反射的能夠提早研究一下,也能夠直接看下一部分的反射代碼也很簡單的。
第二部分 面向接口的多數據模型模板解析
這一部分,咱們介紹使用接口來解析模板,包括列表等多種模型解析,用到了Spring注入和反射還有接口等,有不熟悉的能夠百度搜一下或者評論留言。
咱們接着上面的示例,咱們新建兩個類庫 一個是存放數據模型的 咱們叫Domain;另一個是接口和實現類的 咱們叫Service,而後咱們添加他們之間的引用
1、咱們在Domain下建立幾個測試類
Articles - 文章測試類
Company - 公司測試類
Column - 欄目測試類
TemplateView - 模型解析類(這個是否是比較弱智?我也沒深刻研究多個模型怎麼反射出來 因此 我加了這麼個算是公用的類 沒有對應的數據表 只是解析模板的時候 做爲中間件用用)
public class Articles { /// <summary> /// 文章ID /// </summary> public int Id { get; set; } /// <summary> /// 文章標題 /// </summary> public string Title { get; set; } /// <summary> /// 文章內容 /// </summary> public string Content { get; set; } /// <summary> /// 做者 /// </summary> public string Author { get; set; } /// <summary> /// 發佈時間 /// </summary> public DateTime CreateDate { get; set; } } public class Company { /// <summary> /// 公司Id /// </summary> public int Id { get; set; } /// <summary> /// 公司名稱 /// </summary> public string CompanyName { get; set; } /// <summary> /// 公司電話 /// </summary> public string CompanyTel { get; set; } /// <summary> /// 聯繫人 /// </summary> public string ContectUser { get; set; } /// <summary> /// 建立時間 /// </summary> public DateTime CreateDate { get; set; } } public class Column { /// <summary> /// 欄目ID /// </summary> public int Id { get; set; } /// <summary> /// 欄目名稱 /// </summary> public string Title { get; set; } /// <summary> /// 文章列表 /// </summary> public virtual ICollection<Articles> Articles { get; set; } } public class TemplateView { /// <summary> /// ID /// </summary> public int Id { get; set; } /// <summary> /// 標題 /// </summary> public string Title { get; set; } /// <summary> /// 內容 /// </summary> public string Content { get; set; } /// <summary> /// 做者 /// </summary> public string Author { get; set; } /// <summary> /// 時間 /// </summary> public DateTime CreateDate { get; set; } /// <summary> /// 公司名稱 /// </summary> public string CompanyName { get; set; } /// <summary> /// 公司電話 /// </summary> public string CompanyTel { get; set; } /// <summary> /// 聯繫人 /// </summary> public string ContectUser { get; set; } /// <summary> /// 文章列表 /// </summary> public virtual ICollection<Articles> Articles { get; set; } }
2、咱們在Service下建立一個基礎操做接口以及其實現類(裏面的不少方法 好比:獲取頁面的Html代碼、獲取頁面的編碼以及建立靜態文件等 是沒有必要寫在接口的 這個能夠寫到公用的類庫裏,由於這裏就用到這麼幾個方法 因此我沒有加公用類庫 就直接寫在這裏面了)
/// <summary> /// 基礎操做接口 /// </summary> /// <typeparam name="T"></typeparam> public interface IRepository<T> where T : class { /// <summary> /// 解析模板生成靜態頁 /// </summary> /// <param name="temppath">模板地址</param> /// <param name="path">靜態頁地址</param> /// <param name="t">數據模型</param> /// <returns></returns> bool CreateStaticPage(string temppath, string path, T t); /// <summary> /// 獲取頁面的Html代碼 /// </summary> /// <param name="url">模板頁面路徑</param> /// <param name="encoding">頁面編碼</param> /// <returns></returns> string GetHtml(string url, System.Text.Encoding encoding); /// <summary> /// 獲取頁面的編碼 /// </summary> /// <param name="html">Html源碼</param> /// <returns></returns> System.Text.Encoding GetEncoding(string html); /// <summary> /// 建立靜態文件 /// </summary> /// <param name="result">Html代碼</param> /// <param name="createpath">生成路徑</param> /// <returns></returns> bool CreateFileHtmlByTemp(string result, string createpath); } /// <summary> /// 基礎接口實現類 /// </summary> /// <typeparam name="T"></typeparam> public abstract class RepositoryBase<T> : IRepository<T> where T : class { /// <summary> /// 解析模板生成靜態頁 /// </summary> /// <param name="temppath">模板地址</param> /// <param name="path">靜態頁地址</param> /// <param name="t">數據模型</param> /// <returns></returns> public bool CreateStaticPage(string temppath, string path, T t) { try { //實例化模型 var Entity = new Domain.TemplateView(); //獲取模板Html string TemplateContent = GetHtml(temppath, System.Text.Encoding.UTF8); //初始化結果 string result = ""; //反射賦值 Type typeT = t.GetType(); Type typeEn = Entity.GetType(); System.Reflection.PropertyInfo[] propertyinfosT = typeT.GetProperties(); foreach (System.Reflection.PropertyInfo propertyinfoT in propertyinfosT) { System.Reflection.PropertyInfo propertyinfoEn = typeEn.GetProperty(propertyinfoT.Name); if (propertyinfoEn != null && propertyinfoT.GetValue(t, null) != null) { propertyinfoEn.SetValue(Entity, propertyinfoT.GetValue(t, null), null); } } //不少時候 咱們並無建立複雜的主外鍵關係 例如欄目下的文章 咱們僅僅是在文章表中添加了一個所屬欄目ID的字段 //並無建立關聯 這種狀況下 咱們直接獲取欄目的時候 是獲取不到文章列表的 //包括不少自定義的模型和字段 好比 文章的內容 可能不跟文章一個表 而是一個單獨的大數據字段表 這種狀況下 咱們的 //TemplateView.Content就須要單獨獲取一下另外一個數據模型裏的 這個文章的內容 這種時候 咱們能夠在這裏從新給他賦值 //如 傳入的模型是 文章 //if(t is Domain.Articles) //{ // Entity.Content= 查詢大數據字段表中這篇文章的內容; //} result = Razor.Parse(TemplateContent, Entity); return CreateFileHtmlByTemp(result, path); } catch (Exception e) { throw e; } } /// <summary> /// 獲取頁面的Html代碼 /// </summary> /// <param name="url">模板頁面路徑</param> /// <param name="encoding">頁面編碼</param> /// <returns></returns> public string GetHtml(string url, System.Text.Encoding encoding) { byte[] buf = new WebClient().DownloadData(url); if (encoding != null) return encoding.GetString(buf); string html = System.Text.Encoding.UTF8.GetString(buf); encoding = GetEncoding(html); if (encoding == null || encoding == System.Text.Encoding.UTF8) return html; return encoding.GetString(buf); } /// <summary> /// 獲取頁面的編碼 /// </summary> /// <param name="html">Html源碼</param> /// <returns></returns> public System.Text.Encoding GetEncoding(string html) { string pattern = @"(?i)\bcharset=(?<charset>[-a-zA-Z_0-9]+)"; string charset = Regex.Match(html, pattern).Groups["charset"].Value; try { return System.Text.Encoding.GetEncoding(charset); } catch (ArgumentException) { return null; } } /// <summary> /// 建立靜態文件 /// </summary> /// <param name="result">Html代碼</param> /// <param name="createpath">生成路徑</param> /// <returns></returns> public bool CreateFileHtmlByTemp(string result, string createpath) { if (!string.IsNullOrEmpty(result)) { if (string.IsNullOrEmpty(createpath)) { createpath = "/default.html"; } string filepath = createpath.Substring(createpath.LastIndexOf(@"\")); createpath = createpath.Substring(0, createpath.LastIndexOf(@"\")); if (!Directory.Exists(createpath)) { Directory.CreateDirectory(createpath); } createpath = createpath + filepath; try { FileStream fs2 = new FileStream(createpath, FileMode.Create); StreamWriter sw = new StreamWriter(fs2, new System.Text.UTF8Encoding(false));//去除UTF-8 BOM sw.Write(result); sw.Close(); fs2.Close(); fs2.Dispose(); return true; } catch { return false; } } return false; } }
3、咱們分別建立 文章管理、公司管理、欄目管理的接口和實現類 而且他們都集成基礎操做
/// <summary> /// 文章管理 /// </summary> public interface IArticleManage:IRepository<Domain.Articles> { } public class ArticleManage:RepositoryBase<Domain.Articles>,IArticleManage { } /// <summary> /// 公司管理 /// </summary> public interface ICompanyManage:IRepository<Domain.Company> { } public class CompanyManage:RepositoryBase<Domain.Company>,ICompanyManage { } //欄目管理 public interface IColumnManage:IRepository<Domain.Column> { } public class ColumnManage:RepositoryBase<Domain.Column>,IColumnManage { }
4、注入Xml
<?xml version="1.0" encoding="utf-8" ?> <objects xmlns="http://www.springframework.net"> <description>Spring注入Service,容器指向本層層封裝的接口</description> <object id="Service.ArticleManage" type="Service.ArticleManage,Service" singleton="false"> </object> <object id="Service.ColumnManage" type="Service.ColumnManage,Service" singleton="false"> </object> <object id="Service.CompanyManage" type="Service.CompanyManage,Service" singleton="false"> </object> </objects>
5、咱們分別初始化一個文章類、一個公司類(沒有管理數據表,它下面沒有文章列表 欄目模型我就不初始化了,怎麼輸出列表 你們能夠參考下 欄目模板)
public class HomeController : Controller { /// <summary> /// 聲明一下注入接口 /// </summary> public IArticleManage ArticleManage = Spring.Context.Support.ContextRegistry.GetContext().GetObject("Service.ArticleManage") as IArticleManage; public ICompanyManage CompanyManage = Spring.Context.Support.ContextRegistry.GetContext().GetObject("Service.CompanyManage") as ICompanyManage; public IColumnManage ColumnManage = Spring.Context.Support.ContextRegistry.GetContext().GetObject("Service.ColumnManage") as IColumnManage; public ActionResult Index() { //初始化一個文章數據模型 var entityArticle = new Domain.Articles() { Id = 1, Title = "這裏是文章標題", Content = "<span style=\"color:red;\">這裏是文章內容</span>", Author = "張三", CreateDate = DateTime.Now }; //初始化一個公司數據模型 var entityCompany = new Domain.Company() { Id = 1, CompanyName = "這裏是公司名稱", CompanyTel = "公司電話", ContectUser = "張三", CreateDate = DateTime.Now }; //調用方法生成靜態頁面 ArticleManage.CreateStaticPage(Server.MapPath("/Templates/Temp_article.html"), Server.MapPath("/Pages/news/" + DateTime.Now.ToString("yyyyMMddHHmmss") + "1.html"), entityArticle); CompanyManage.CreateStaticPage(Server.MapPath("/Templates/Temp_company.html"), Server.MapPath("/Pages/news/" + DateTime.Now.ToString("yyyyMMddHHmmss") + "2.html"), entityCompany); return View(); } public ActionResult About() { ViewBag.Message = "Your application description page."; return View(); } public ActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } }
6、這是測試的簡單的文章模板、公司模板和欄目模板
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>@Model.Title</title> </head> <body> <h1>@Model.Title</h1> <p>做者:@Model.Author - 發佈時間:@Model.CreateDate</p> <p>@Raw(Model.Content)</p> </body> </html>
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <p>公司名稱:@Model.CompanyName</p> <p>公司電話:@Model.CompanyTel</p> <p>聯繫人:@Model.ContectUser</p> <p>建立時間:@Model.CreateDate</p> </body> </html>
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <p>欄目標題: @Model.Title</p> <p> 文章列表 <ul> @foreach(var item in @Model.Articles) { <li> <a href=""> <span>@item.Title</span> <span>@item.Author</span> <span>@item.CreateDate</span> </a> </li> } </ul> </p> </body> </html>
咱們運行一下,大功告成~~~
怎麼排序?怎麼獲取前幾條?怎麼格式化日期時間?怎麼分頁?
這但是Razor啊,這都不須要再多講了吧,簡單一說,若是你傳入數據前沒有事先排序或者獲取前幾條,這些操做要作模板裏操做 那跟在.cshtml裏基本是同樣的
@foreach(var item in @Model.ListColumn) { <div > @if (@item.LinkUrl==null) { <ul> @foreach(var article in @item.COM_ARTICLE.Take(15).OrderByDescending(p=>p.UpDateDate)) { <li> <a href="@article.LinkUrl" class="gd-a"> <div>@article.Title</div></a> </li> } </ul> } else { } </div> }
應用仍是很普遍的,並且解析代碼相對於標籤替換來講十分簡潔、高效。有時間能夠多研究研究,改天有空寫一個模板替換標籤的供你們參考一下。有人會說那我還得教前臺製做Razor語法,這種說法咱們無法去置評,標籤替換你仍然要教他如何使用標籤啊,因此是否是複雜並非探究的主題,想要前臺製做人員更方便的製做一套模板語法並非主要因素,好比咱們能夠作一套方便的模板製做,用戶點擊一下就生成代碼,或者直接作成可視化的,這可能讓咱們的程序員要耗費更多的精力,可是一勞永逸,標籤替換方式你仍然要給前臺製做人員一套標籤規範和語法,何況後臺解析異常的龐大和複雜。
仍是那句老話,這篇文章僅僅是我的的一些理解和實現,可能中間會出現一些不合理的地方或是錯誤,請你們指正,咱們共同窗習研究。
Demo是用VS 2013寫的
原創文章 轉載請尊重勞動成果 http://yuangang.cnblogs.com