簡述
Razor是ASP.NET MVC 3中新加入的技術(shù),以作為ASPX引擎的一個新的替代項。在早期的MVC版本中默認使用的是ASPX模板引擎,Razor在語法上的確不錯,用起來非常方便,簡潔的語法與.NET Framework 結(jié)合,廣泛應(yīng)用于ASP.NET MVC 項目。
我們在很多項目開發(fā)中會常常用到頁面靜態(tài)化,頁面靜態(tài)化有許多方式,最常見的就是類似很多PHP CMS種使用的 標簽替換的方式(如:帝國CMS、EcShop等),還有很多都是偽靜態(tài),偽靜態(tài)我們就不做過多解釋,通過路由或Url重寫來實現(xiàn)就可以了。Razor為我們提供了更加方便的模板解析方式,任何東西都是兩方面的,技術(shù)也是如此,Razor解析模板雖然更加方便、簡潔,但是對于模板制作人員來說也是有一定的技術(shù)要求,或者對于開發(fā)一套模板制作功能來說,考慮的要更多一些。我們不再去探究這些問題,我們更注重哪種技術(shù)更容易、更方便、更好的滿足我們項目的需求。
如何使用RazorEngine
今天來簡單介紹一下如何使用RazorEngine解析模板生成靜態(tài)頁面,RazorEngine它是基于微軟的Razor之上,包裝而成的一個可以獨立使用的模板引擎。也就是說,保留了Razor的模板功能,但是使得Razor脫離于Asp.net MVC,能夠在其它應(yīng)用環(huán)境下使用,項目地址:https://github.com/Antaris/RazorEngine
首先我們?nèi)odeplex上下兩個需要的dll http://razorengine.codeplex.com
看到網(wǎng)上很多介紹RazorEngine的基礎(chǔ)用法的,講解的都比較詳細,對于RazorEngine運行原理很清晰,我們在這里就不重復(fù)介紹了。寫這篇文章是對于很多新手同學(xué)來說比較喜歡“拿來主義”,基本的用法原理都能看懂,但是如何應(yīng)用到項目中還是有些不是很清晰,我們只講講如何在項目中運用。
本文分為兩部分:第一個部分,基本的單數(shù)據(jù)模型模板解析;第二部分,面向接口的多數(shù)據(jù)模型模板解析
第一個部分 基本的單數(shù)據(jù)模型模板解析
一、我們創(chuàng)建一個MVC項目,并且添加上面的兩個DLL引用,然后我們新建一個簡單的文章類
public class Articles
{
/// summary>
/// 文章ID
/// /summary>
public int Id { get; set; }
/// summary>
/// 文章標題
/// /summary>
public string Title { get; set; }
/// summary>
/// 文章內(nèi)容
/// /summary>
public string Content { get; set; }
/// summary>
/// 作者
/// /summary>
public string Author { get; set; }
/// summary>
/// 發(fā)布時間
/// /summary>
public DateTime CreateDate { get; set; }
}
二、我們新建一個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 - 發(fā)布時間:@Model.CreateDate/p>
p>@Raw(Model.Content)/p>
/body>
/html>
說明:Model就是我們的文章實體類 在MVC的試圖頁cshtml中 我們一般都是在控制器里傳遞這個實體類 然后在視圖頁中 @model Models.Articles 來接收這個實體類 然后通過“@Model.”來輸出內(nèi)容,在Razor模板中是一樣的,只是不用@model Models.Articles 來接收了,其它的語法跟在.cshtml試圖頁中是一樣的,這么說多余了,因為寫法不一樣他就不是Razor了
三、我們寫一個方法來獲取模板頁的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; }
}
四、我們寫一個方法 用于生成Html靜態(tài)頁
/// summary>
/// 創(chuàng)建靜態(tài)文件
/// /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;
}
五、我們來寫個方法調(diào)用靜態(tài)模板,并且傳遞數(shù)據(jù)模型實體類 創(chuàng)建Html靜態(tài)頁
/// summary>
/// 解析模板生成靜態(tài)頁
/// /summary>
/// param name="temppath">模板地址/param>
/// param name="path">靜態(tài)頁地址/param>
/// param name="t">數(shù)據(jù)模型/param>
/// returns>/returns>
public bool CreateStaticPage(string temppath, string path, RazorEngineTemplates.Models.Articles t)
{
try
{
//獲取模板Html
string TemplateContent = GetHtml(temppath, System.Text.Encoding.UTF8);
//初始化結(jié)果
string result = string.Empty;
//解析模板生成靜態(tài)頁Html代碼
result = Razor.Parse(TemplateContent, t);
//創(chuàng)建靜態(tài)文件
return CreateFileHtmlByTemp(result, path);
}
catch (Exception e)
{
throw e;
}
}
好了,大功告成,是不是很簡單。
這里只是一個很簡單的應(yīng)用,沒有讀取數(shù)據(jù),也沒有列表,只有一個文章數(shù)據(jù)模型,下一部分我們將介紹 多模型模板解析,因為是多模型 所以 生成靜態(tài)頁面的時候 就不是傳遞一個具體模型實體類 我們會用到 反射,通過反射模型屬性 獲取數(shù)據(jù),有不熟悉反射的可以提前研究一下,也可以直接看下一部分的反射代碼也很簡單的。
第二部分 面向接口的多數(shù)據(jù)模型模板解析
這一部分,我們介紹使用接口來解析模板,包括列表等多種模型解析,用到了Spring注入和反射還有接口等,有不熟悉的可以百度搜一下或者評論留言。
我們接著上面的示例,我們新建兩個類庫 一個是存放數(shù)據(jù)模型的 我們叫Domain;另外一個是接口和實現(xiàn)類的 我們叫Service,然后我們添加他們之間的引用
一、我們在Domain下創(chuàng)建幾個測試類
Articles - 文章測試類
Company - 公司測試類
Column - 欄目測試類
TemplateView - 模型解析類(這個是不是比較弱智?我也沒深入研究多個模型怎么反射出來 所以 我加了這么個算是公用的類 沒有對應(yīng)的數(shù)據(jù)表 只是解析模板的時候 作為中間件用用)
public class Articles
{
/// summary>
/// 文章ID
/// /summary>
public int Id { get; set; }
/// summary>
/// 文章標題
/// /summary>
public string Title { get; set; }
/// summary>
/// 文章內(nèi)容
/// /summary>
public string Content { get; set; }
/// summary>
/// 作者
/// /summary>
public string Author { get; set; }
/// summary>
/// 發(fā)布時間
/// /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>
/// 聯(lián)系人
/// /summary>
public string ContectUser { get; set; }
/// summary>
/// 創(chuàng)建時間
/// /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 ICollectionArticles> Articles { get; set; }
}
public class TemplateView
{
/// summary>
/// ID
/// /summary>
public int Id { get; set; }
/// summary>
/// 標題
/// /summary>
public string Title { get; set; }
/// summary>
/// 內(nèi)容
/// /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>
/// 聯(lián)系人
/// /summary>
public string ContectUser { get; set; }
/// summary>
/// 文章列表
/// /summary>
public virtual ICollectionArticles> Articles { get; set; }
}
二、我們在Service下創(chuàng)建一個基礎(chǔ)操作接口以及其實現(xiàn)類(里面的很多方法 比如:獲取頁面的Html代碼、獲取頁面的編碼以及創(chuàng)建靜態(tài)文件等 是沒有必要寫在接口的 這個可以寫到公用的類庫里,因為這里就用到這么幾個方法 所以我沒有加公用類庫 就直接寫在這里面了)
/// summary>
/// 基礎(chǔ)操作接口
/// /summary>
/// typeparam name="T">/typeparam>
public interface IRepositoryT> where T : class
{
/// summary>
/// 解析模板生成靜態(tài)頁
/// /summary>
/// param name="temppath">模板地址/param>
/// param name="path">靜態(tài)頁地址/param>
/// param name="t">數(shù)據(jù)模型/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>
/// 創(chuàng)建靜態(tài)文件
/// /summary>
/// param name="result">Html代碼/param>
/// param name="createpath">生成路徑/param>
/// returns>/returns>
bool CreateFileHtmlByTemp(string result, string createpath);
}
/// summary>
/// 基礎(chǔ)接口實現(xiàn)類
/// /summary>
/// typeparam name="T">/typeparam>
public abstract class RepositoryBaseT> : IRepositoryT> where T : class
{
/// summary>
/// 解析模板生成靜態(tài)頁
/// /summary>
/// param name="temppath">模板地址/param>
/// param name="path">靜態(tài)頁地址/param>
/// param name="t">數(shù)據(jù)模型/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);
//初始化結(jié)果
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);
}
}
//很多時候 我們并沒有創(chuàng)建復(fù)雜的主外鍵關(guān)系 例如欄目下的文章 我們僅僅是在文章表中添加了一個所屬欄目ID的字段
//并沒有創(chuàng)建關(guān)聯(lián) 這種情況下 我們直接獲取欄目的時候 是獲取不到文章列表的
//包括很多自定義的模型和字段 比如 文章的內(nèi)容 可能不跟文章一個表 而是一個單獨的大數(shù)據(jù)字段表 這種情況下 我們的
//TemplateView.Content就需要單獨獲取一下另一個數(shù)據(jù)模型里的 這個文章的內(nèi)容 這種時候 我們可以在這里重新給他賦值
//如 傳入的模型是 文章
//if(t is Domain.Articles)
//{
// Entity.Content= 查詢大數(shù)據(jù)字段表中這篇文章的內(nèi)容;
//}
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>
/// 創(chuàng)建靜態(tài)文件
/// /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;
}
}
三、我們分別創(chuàng)建 文章管理、公司管理、欄目管理的接口和實現(xiàn)類 并且他們都集成基礎(chǔ)操作
/// summary>
/// 文章管理
/// /summary>
public interface IArticleManage:IRepositoryDomain.Articles>
{
}
public class ArticleManage:RepositoryBaseDomain.Articles>,IArticleManage
{
}
/// summary>
/// 公司管理
/// /summary>
public interface ICompanyManage:IRepositoryDomain.Company>
{
}
public class CompanyManage:RepositoryBaseDomain.Company>,ICompanyManage
{
}
//欄目管理
public interface IColumnManage:IRepositoryDomain.Column>
{
}
public class ColumnManage:RepositoryBaseDomain.Column>,IColumnManage
{
}
四、注入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>
五、我們分別初始化一個文章類、一個公司類(沒有管理數(shù)據(jù)表,它下面沒有文章列表 欄目模型我就不初始化了,怎么輸出列表 大家可以參考下 欄目模板)
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()
{
//初始化一個文章數(shù)據(jù)模型
var entityArticle = new Domain.Articles() { Id = 1, Title = "這里是文章標題", Content = "span style=\"color:red;\">這里是文章內(nèi)容/span>", Author = "張三", CreateDate = DateTime.Now };
//初始化一個公司數(shù)據(jù)模型
var entityCompany = new Domain.Company() { Id = 1, CompanyName = "這里是公司名稱", CompanyTel = "公司電話", ContectUser = "張三", CreateDate = DateTime.Now };
//調(diào)用方法生成靜態(tài)頁面
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();
}
}
六、這是測試的簡單的文章模板、公司模板和欄目模板
!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 - 發(fā)布時間:@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>聯(lián)系人:@Model.ContectUser/p>
p>創(chuàng)建時間:@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啊,這都不需要再多講了吧,簡單一說,如果你傳入數(shù)據(jù)前沒有事先排序或者獲取前幾條,這些操作要做模板里操作 那跟在.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>
}
應(yīng)用還是很廣泛的,而且解析代碼相對于標簽替換來說十分簡潔、高效。有時間可以多研究研究,改天有空寫一個模板替換標簽的供大家參考一下。
還是那句老話,這篇文章僅僅是個人的一些理解和實現(xiàn),可能中間會出現(xiàn)一些不合理的地方或是錯誤,請大家指正,我們共同學(xué)習(xí)研究。
以上就是本文的全部內(nèi)容,希望大家喜歡。
原文地址:http://yuangang.cnblogs.com
您可能感興趣的文章:- 詳解ASP.NET Razor 語法
- ASP.NET MVC重寫RazorViewEngine實現(xiàn)多主題切換
- 詳解ASP.NET MVC 利用Razor引擎生成靜態(tài)頁
- ASP.NET MVC4 Razor模板簡易分頁效果
- ASP.NET Razor模板引擎中輸出Html的兩種方式
- asp.net模板引擎Razor調(diào)用外部方法用法實例
- asp.net模板引擎Razor中cacheName的問題分析
- 使用Asp.net Mvc3 Razor視圖方式擴展JQuery UI Widgets方法介紹
- 詳細分析ASP.NET Razor之C# 變量