当前位置: 移动技术网 > IT编程>开发语言>.net > ASP.NET MVC使用RazorEngine解析模板生成静态页

ASP.NET MVC使用RazorEngine解析模板生成静态页

2017年12月12日  | 移动技术网IT编程  | 我要评论

www.avbbs.tv,深夜日剧,儿女传奇侯京健

简述

      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>
}

应用还是很广泛的,而且解析代码相对于标签替换来说十分简洁、高效。有时间可以多研究研究,改天有空写一个模板替换标签的供大家参考一下。

还是那句老话,这篇文章仅仅是个人的一些理解和实现,可能中间会出现一些不合理的地方或是错误,请大家指正,我们共同学习研究。
以上就是本文的全部内容,希望大家喜欢。
原文地址:

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网