Detailed explanation of asp.net MVC parsing template to generate static pages (razorengine)

Time:2021-12-3

sketch

Razor is a new technology added to asp.net MVC 3 as a new alternative to ASPX engine. In the early MVC versions, the ASPX template engine is used by default. Razor is really good in syntax and is very convenient to use. The concise syntax is combined with the. Net framework and is widely used in asp.net MVC projects.

We often use page staticization in many project development. There are many ways of page staticization. The most common way is label replacement similar to many PHP CMS (such as imperial CMS, ECSHOP, etc.), and many others are pseudo statics. We don’t explain too much about pseudo statics, but we can implement it through routing or URL rewriting. Razor provides us with a more convenient template parsing method. Everything is in two aspects, and so is the technology. Although razor parsing template is more convenient and concise, it also has certain technical requirements for template makers, or more consideration should be given to developing a set of template making functions. We no longer explore these problems. We pay more attention to which technology is easier, more convenient and better to meet the needs of our project.

How to use razorengine

Today, let’s briefly introduce how to use razorengine to parse templates to generate static pages. Razorengine is an independent template engine packaged based on Microsoft’s razor. In other words, the template function of razor is retained, but razor is separated from asp.net MVC and can be used in other application environments. Project address:RazorEngine_jb51.rar

First, let’s go to the upper and lower DLLs of codeplexhttp://razorengine.codeplex.com

I’ve seen a lot of online introductions to the basic usage of razorengine. The explanations are quite detailed. The operating principle of razorengine is very clear. We won’t repeat the introduction here. This article is written for many novice students who prefer “taking doctrine”. They can understand the basic usage principles, but how to apply it to the project is still not very clear. We only talk about how to apply it to the project.

This paper is divided into two parts: the first part, the basic single data model template analysis; The second part is interface oriented multi data model template parsing

The first part is the basic single data model template analysis

1、 We create an MVC project and add the above two DLL references, and then we create a new simple article class

public class Articles
  {
    /// <summary>
    ///Article ID
    /// </summary>
    public int Id { get; set; }
    /// <summary>
    ///Article title
    /// </summary>
    public string Title { get; set; }
    /// <summary>
    ///Article content
    /// </summary>
    public string Content { get; set; }
    /// <summary>
    ///Author
    /// </summary>
    public string Author { get; set; }
    /// <summary>
    ///Release time
    /// </summary>
    public DateTime CreateDate { get; set; }
  }

2、 Let’s create a razor HTML template

<!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>Author: @model.author - published on: @model.createdate</p>
  <p>@Raw(Model.Content)</p>
</body>
</html>

Note: model is our article entity class   In the cshtml attempt page of MVC, we generally pass this entity class in the controller, then receive this entity class in the view page @ model models.articles, and then output the content through “@ model.” in the razor template, it is the same, but we don’t need to receive @ model models.articles. Other syntax is the same as in the. Cshtml attempt page, That’s superfluous, because the writing is different, he’s not razor

3、 We write a method to get the HTML code of the template page

/// <summary>
    ///Get HTML code of the page
    /// </summary>
    ///< param name = "URL" > template page path < / param >
    ///< param name = "encoding" > page code < / 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>
    ///Get the encoding of the page
    /// </summary>
    ///< param name = "HTML" > HTML source code < / 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、 We write a method to generate HTML static pages

/// <summary>
    ///Create static file
    /// </summary>
    ///< param name = "result" > HTML code < / param >
    ///< param name = "createpath" > generate path < / 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));// Remove UTF-8 BOM
          sw.Write(result);
          sw.Close();
          fs2.Close();
          fs2.Dispose();
          return true;
        }
        catch { return false; }
      }
      return false;
    }

5、 Let’s write a method call static template and pass the data model entity class to create an HTML static page

/// <summary>
    ///Generate static page by parsing template
    /// </summary>
    ///< param name = "temppath" > template address < / param >
    ///< param name = "path" > static page address < / param >
    ///< param name = "t" > data model < / param >
    /// <returns></returns>
    public bool CreateStaticPage(string temppath, string path, RazorEngineTemplates.Models.Articles t)
    {
      try
      {
        //Get template HTML
        string TemplateContent = GetHtml(temppath, System.Text.Encoding.UTF8);

        //Initialization result
        string result = string.Empty;

        //Parsing templates to generate static page HTML code
        result = Razor.Parse(TemplateContent, t);

        //Create static file
        return CreateFileHtmlByTemp(result, path);
      }
      catch (Exception e)
      {
        throw e;
      }
    }

Well, it’s done. Isn’t it easy.

This is just a very simple application. There is no data read or list. There is only one article data model. In the next part, we will introduce multi model template parsing. Because it is multi model, it is not necessary to pass a specific model entity class when generating a static page. We will use reflection to obtain data through reflection model attributes, If you are not familiar with reflection, you can study it in advance, or you can directly look at the next part of the reflection code. It is also very simple.

The second part is the analysis of interface oriented multi data model template

In this part, we introduce the use of interfaces to parse templates, including list and other model parsing. Spring injection, reflection and interfaces are used. If you are unfamiliar, you can search Baidu or leave comments.

Following the above example, we create two new class libraries. One is the domain where the data model is stored; The other is the interface and implementation class, which we call service, and then we add references between them

1、 We create several test classes under domain

Articles – article test class

Company – company testing

Column – column test class

Templateview – model parsing class (is this retarded? I haven’t studied how multiple models are reflected, so I added such a public class. There is no corresponding data table, but it is used as middleware when parsing templates)

public class Articles
  {
    /// <summary>
    ///Article ID
    /// </summary>
    public int Id { get; set; }
    /// <summary>
    ///Article title
    /// </summary>
    public string Title { get; set; }
    /// <summary>
    ///Article content
    /// </summary>
    public string Content { get; set; }
    /// <summary>
    ///Author
    /// </summary>
    public string Author { get; set; }
    /// <summary>
    ///Release time
    /// </summary>
    public DateTime CreateDate { get; set; }
  }
  public class Company
  {
    /// <summary>
    ///Company ID
    /// </summary>
    public int Id { get; set; }
    /// <summary>
    ///Company name
    /// </summary>
    public string CompanyName { get; set; }
    /// <summary>
    ///Company telephone
    /// </summary>
    public string CompanyTel { get; set; }
    /// <summary>
    ///Contact person
    /// </summary>
    public string ContectUser { get; set; }
    /// <summary>
    ///Creation time
    /// </summary>
    public DateTime CreateDate { get; set; }
  }
   public class Column
  {
    /// <summary>
    ///Column ID
    /// </summary>
    public int Id { get; set; }
    /// <summary>
    ///Column name
    /// </summary>
    public string Title { get; set; }
    /// <summary>
    ///Article list
    /// </summary>

    public virtual ICollection<Articles> Articles { get; set; }
  }
   public class TemplateView
  {
    /// <summary>
    /// ID
    /// </summary>
    public int Id { get; set; }
    /// <summary>
    ///Title
    /// </summary>
    public string Title { get; set; }
    /// <summary>
    ///Content
    /// </summary>
    public string Content { get; set; }
    /// <summary>
    ///Author
    /// </summary>
    public string Author { get; set; }
    /// <summary>
    ///Time
    /// </summary>
    public DateTime CreateDate { get; set; }    
    /// <summary>
    ///Company name
    /// </summary>
    public string CompanyName { get; set; }
    /// <summary>
    ///Company telephone
    /// </summary>
    public string CompanyTel { get; set; }
    /// <summary>
    ///Contact person
    /// </summary>
    public string ContectUser { get; set; }
    /// <summary>
    ///Article list
    /// </summary>
    public virtual ICollection<Articles> Articles { get; set; }
  }

2、 We create a basic operation interface and its implementation class under service (there are many methods, such as obtaining HTML code of the page, obtaining page code and creating static files. It is not necessary to write them in the interface. They can be written in the public class library. Because these methods are used here, I directly write them here without adding a public class library.)

/// <summary>
  ///Basic operation interface
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public interface IRepository<T> where T : class
  {
    /// <summary>
    ///Generate static page by parsing template
    /// </summary>
    ///< param name = "temppath" > template address < / param >
    ///< param name = "path" > static page address < / param >
    ///< param name = "t" > data model < / param >
    /// <returns></returns>
    bool CreateStaticPage(string temppath, string path, T t);    

    /// <summary>
    ///Get HTML code of the page
    /// </summary>
    ///< param name = "URL" > template page path < / param >
    ///< param name = "encoding" > page code < / param >
    /// <returns></returns>
    string GetHtml(string url, System.Text.Encoding encoding);

    /// <summary>
    ///Get the encoding of the page
    /// </summary>
    ///< param name = "HTML" > HTML source code < / param >
    /// <returns></returns>
    System.Text.Encoding GetEncoding(string html);

    /// <summary>
    ///Create static file
    /// </summary>
    ///< param name = "result" > HTML code < / param >
    ///< param name = "createpath" > generate path < / param >
    /// <returns></returns>
    bool CreateFileHtmlByTemp(string result, string createpath);
  }
/// <summary>
  ///Basic interface implementation class
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public abstract class RepositoryBase<T> : IRepository<T> where T : class
  {
    /// <summary>
    ///Generate static page by parsing template
    /// </summary>
    ///< param name = "temppath" > template address < / param >
    ///< param name = "path" > static page address < / param >
    ///< param name = "t" > data model < / param >
    /// <returns></returns>
    public bool CreateStaticPage(string temppath, string path, T t)
    {
      try
      {
        //Instantiation model
        var Entity = new Domain.TemplateView();

        //Get template HTML
        string TemplateContent = GetHtml(temppath, System.Text.Encoding.UTF8);
        //Initialization result
        string result = "";

        //Reflection assignment
        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);
          }
        }

        //Most of the time, we do not create complex primary and foreign key relationships, such as articles under columns. We just add a column ID field to the article table
        //No association is created. In this case, we can't get the article list when we get the column directly
        //It includes many custom models and fields. For example, the content of the article may not be a table with the article, but a separate big data field table. In this case, our
        //Templateview.content needs to separately obtain the content of this article in another data model. In this case, we can re assign it here

        //If the incoming model is an article
        //if(t is Domain.Articles)
        //{
        //Entity. Content = query the content of this article in the big data field table;
          
        //}

        result = Razor.Parse(TemplateContent, Entity);

        return CreateFileHtmlByTemp(result, path);
      }
      catch (Exception e)
      {
        throw e;
      }
    }

    /// <summary>
    ///Get HTML code of the page
    /// </summary>
    ///< param name = "URL" > template page path < / param >
    ///< param name = "encoding" > page code < / 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>
    ///Get the encoding of the page
    /// </summary>
    ///< param name = "HTML" > HTML source code < / 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>
    ///Create static file
    /// </summary>
    ///< param name = "result" > HTML code < / param >
    ///< param name = "createpath" > generate path < / 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));// Remove UTF-8 BOM
          sw.Write(result);
          sw.Close();
          fs2.Close();
          fs2.Dispose();
          return true;
        }
        catch { return false; }
      }
      return false;
    }
  }

3、 We create interfaces and implementation classes for article management, company management and column management, and they all integrate basic operations

/// <summary>
  ///Article management
  /// </summary>
   public interface IArticleManage:IRepository<Domain.Articles>
  {
  }
  public class ArticleManage:RepositoryBase<Domain.Articles>,IArticleManage
  {
  }

  /// <summary>
  ///Company management
  /// </summary>
  public interface ICompanyManage:IRepository<Domain.Company>
  {
  }
  public class CompanyManage:RepositoryBase<Domain.Company>,ICompanyManage
  {
  }

  //Column management
  public interface IColumnManage:IRepository<Domain.Column>
  {
  }
  public class ColumnManage:RepositoryBase<Domain.Column>,IColumnManage
  {
  }

4、 Inject XML

<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">
 < description > spring injects service, and the container points to the interface encapsulated in this layer < / description >
 <object type="Service.ArticleManage,Service" singleton="false">
 </object>
 <object type="Service.ColumnManage,Service" singleton="false">
 </object>
 <object type="Service.CompanyManage,Service" singleton="false">
 </object>
</objects>

5、 We initialize an article class and a company class respectively (if there is no management data table and there is no article list column model under it, I won’t initialize it. How to output the list, you can refer to the column template below)

public class HomeController : Controller
  {
    /// <summary>
    ///Declare the injection interface
    /// </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()
    {
      //Initialize an article data model
      Var entityarticle = new domain. Articles() {id = 1, title = "here is the article title", content = "< span style =" color: red; \ "> here is the article content < / span >", author = "Zhang San", createDate = datetime. Now};

      //Initialize a company data model
      Var entitycompany = new domain. Company() {id = 1, CompanyName = "here is the company name", companytel = "company telephone", contectuser = "Zhang San", createDate = datetime. Now};

      //Call method to generate static page
      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、 This is a simple article template, company template and column template tested

<!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>Author: @model.author - published on: @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>Company name: @model.companyname</p>
  <p>Company Tel.: @model.companytel</p>
  <p>Contact: @model.contectuser</p>
  <p>Creation time: @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>Column title: @model.title</p>
  <p>
    Article list
    <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>

Let’s run it and it’s done~~~

 

 

How to sort? How to get the first few? How do I format the date and time? How to page?

This is razor. There’s no need to talk more about it. To be simple, if you don’t sort or get the first few items before importing data, these operations need to be operated in the template, which is basically the same as that in. 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" rel="external nofollow">
        <div>@article.Title</div></a>
      </li>
}
 </ul>
   }
  else
   {

  }
</div>
}

It is widely used, and the parsing code is very concise and efficient compared with label replacement. If you have time, you can study more and write a template to replace the label another day for your reference. Some people will say that I have to teach the front desk to make razor grammar. We can’t comment on this statement. You still have to teach him how to use labels for label replacement, so it’s not the subject of exploration. It’s not the main factor to want the front desk producer to make a set of template grammar more convenient. For example, we can make a set of convenient template, Users click to generate code, or directly make it visual, which may make our programmers spend more energy, but once and for all, you still have to give the foreground producers a set of label specifications and syntax, and the background parsing is extremely large and complex.

 

The above is the whole content of this article. I hope it will be helpful to your study, and I hope you can support developpaer.