ASP.NET MVC使用RazorEngine解析模板生成静态页
简述
razor是asp.net mvc 3中新加入的技术,以作为aspx引擎的一个新的替代项。在早期的mvc版本中默认使用的是aspx模板引擎,razor在语法上的确不错,用起来非常方便,简洁的语法与.net framework 结合,广泛应用于asp.net mvc 项目。
我们在很多项目开发中会常常用到页面静态化,页面静态化有许多方式,最常见的就是类似很多php cms种使用的 标签替换的方式(如:帝国cms、ecshop等),还有很多都是伪静态,伪静态我们就不做过多解释,通过路由或url重写来实现就可以了。razor为我们提供了更加方便的模板解析方式,任何东西都是两方面的,技术也是如此,razor解析模板虽然更加方便、简洁,但是对于模板制作人员来说也是有一定的技术要求,或者对于开发一套模板制作功能来说,考虑的要更多一些。我们不再去探究这些问题,我们更注重哪种技术更容易、更方便、更好的满足我们项目的需求。
如何使用razorengine
今天来简单介绍一下如何使用razorengine解析模板生成静态页面,razorengine它是基于微软的razor之上,包装而成的一个可以独立使用的模板引擎。也就是说,保留了razor的模板功能,但是使得razor脱离于asp.net mvc,能够在其它应用环境下使用,项目地址:https://github.com/antaris/razorengine
首先我们去codeplex上下两个需要的dll
看到网上很多介绍razorengine的基础用法的,讲解的都比较详细,对于razorengine运行原理很清晰,我们在这里就不重复介绍了。写这篇文章是对于很多新手同学来说比较喜欢“拿来主义”,基本的用法原理都能看懂,但是如何应用到项目中还是有些不是很清晰,我们只讲讲如何在项目中运用。
本文分为两部分:第一个部分,基本的单数据模型模板解析;第二部分,面向接口的多数据模型模板解析
第一个部分 基本的单数据模型模板解析
一、我们创建一个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; } }
二、我们新建一个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了
三、我们写一个方法来获取模板页的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静态页
/// <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; }
五、我们来写个方法调用静态模板,并且传递数据模型实体类 创建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,然后我们添加他们之间的引用
一、我们在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; } }
二、我们在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; } }
三、我们分别创建 文章管理、公司管理、栏目管理的接口和实现类 并且他们都集成基础操作
/// <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 { }
四、注入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>
五、我们分别初始化一个文章类、一个公司类(没有管理数据表,它下面没有文章列表 栏目模型我就不初始化了,怎么输出列表 大家可以参考下 栏目模板)
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(); } }
六、这是测试的简单的文章模板、公司模板和栏目模板
<!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> }
应用还是很广泛的,而且解析代码相对于标签替换来说十分简洁、高效。有时间可以多研究研究,改天有空写一个模板替换标签的供大家参考一下。
还是那句老话,这篇文章仅仅是个人的一些理解和实现,可能中间会出现一些不合理的地方或是错误,请大家指正,我们共同学习研究。
以上就是本文的全部内容,希望大家喜欢。
原文地址:
推荐阅读
-
ASP.NET MVC使用RazorEngine解析模板生成静态页
-
ASP.NET MVC使用RazorEngine解析模板生成静态页
-
详解ASP.NET MVC 解析模板生成静态页(RazorEngine)
-
详解ASP.NET MVC 利用Razor引擎生成静态页
-
详解ASP.NET MVC 解析模板生成静态页(RazorEngine)
-
详解ASP.NET MVC 利用Razor引擎生成静态页
-
ASP.Net MVC 布局页、模板页使用方法详细介绍
-
利用Razor引擎生成静态页(ASP.NET MVC)实例详解
-
ASP.Net MVC 布局页、模板页使用方法详细介绍
-
利用Razor引擎生成静态页(ASP.NET MVC)实例详解