StarBlog Development Notes (7): Page Development — Article Detail Page
Series articles Developing a Blog Project with .NetCore: StarBlog - (1) Why write your own blog? Developing a Blog Project with .NetCore: StarBlog - (2) Environment setup and creating the project Developing a Blog Project with .NetCore: StarBlog - (3) Model design Developing a Blog Project with .NetCore: StarBlog - (4) Batch importing markdown blogs Developing a Blog Project with .NetCore: StarBlog - (5) Starting to build the Web project Developing a Blog Project with .NetCore: StarBl
目录9 个章节
Series Articles
- Developing a Blog Project with .NetCore: StarBlog - (1) Why write your own blog?
- Developing a Blog Project with .NetCore: StarBlog - (2) Environment setup and creating the project
- Developing a Blog Project with .NetCore: StarBlog - (3) Model design
- Developing a Blog Project with .NetCore: StarBlog - (4) Batch importing markdown blogs
- Developing a Blog Project with .NetCore: StarBlog - (5) Starting to build the Web project
- Developing a Blog Project with .NetCore: StarBlog - (6) Page development: blog post list
- Developing a Blog Project with .NetCore: StarBlog - (7) Page development: article detail page
- ...
Preface
The previous post finished developing the article list. Now it's time to write the article detail page (this update isn't late, is it? hehe).
The most important part of a blog site is arguably the article detail page. When users come to read a blog, they care first about the content, then about the reading experience, so the design of this article detail page cannot be sloppy~
Approach
The article body is stored in markdown format. To display it on a web page, the markdown needs to be rendered into HTML.
So there are two approaches:
- One is server-side rendering: use C# to convert markdown to HTML and then render it as a web page
- The other is to have the backend output markdown directly, and use some open-source JS libraries to render the markdown
At first I went with the first approach, server-side rendering, using the C# library Markdig. After using it more deeply, I found that some features I wanted were rather troublesome to implement. In particular, this library has almost no documentation, so customizing anything all came down to reading the source + guessing. In the end I had to give up and switch to the second approach.
This article will cover the implementation of both approaches, with the focus on the second one: client-side rendering.
Server-side Rendering
I've written a detailed introduction to the Markdig library in a previous post, so I won't repeat it here. Interested readers can check it out: Parsing Markdown documents in C# and replacing image links
First, install the Markdig library via NuGet
Markdown can be converted to HTML with a single line of code
Markdig.Markdown.ToHtml(markdownContent);
Of course, the page rendered this way is quite bare: no code highlighting, no blockquote styling, no list styles, and so on. So this alone is definitely not enough.
As the only actively maintained Markdown library for C# at present, Markdig naturally takes extensibility into account. It has an extension system, ships with more than 20 built-in extensions, and you can also install extensions developed by others to achieve effects such as code highlighting.
Using extensions is also simple: just add a pipeline parameter
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
var result = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
Markdig itself does not come with a code-highlighting extension, so you need a third-party component. I tested the following two and they work
- Markdig.Prism: client-side rendering, but needs a server-side component to cooperate
- Markdown.ColorCode: server-side rendering
Client-side Rendering
This project ultimately went with client-side rendering. The frontend ecosystem has many markdown components; after looking around, I eventually chose the Editor.md component.
The main reasons were that it makes it relatively convenient to implement a TOC (table of contents) for the article, and it has decent highlighting.
It's simple to use
First, output the markdown into the page
<div id="test-editormd-view" class="post-content">
<textarea id="append-test" style="display:none;">@Model.Content</textarea>
</div>
With display:none added, this textarea is not shown — there's no point showing markdown source to the user
Include the edtor.md stylesheet
<link rel="stylesheet" href="~/lib/editormd/css/editormd.preview.min.css">
Include the editor.md JS — you read that right, that's how many there are. The static assets were already installed in a previous article, so I won't repeat that here. See: (5) Starting to build the Web project
<script src="~/lib/editormd/examples/js/jquery.min.js"></script>
<script src="~/lib/editormd/lib/marked.min.js"></script>
<script src="~/lib/editormd/lib/prettify.min.js"></script>
<script src="~/lib/editormd/lib/raphael.min.js"></script>
<script src="~/lib/editormd/lib/underscore.min.js"></script>
<script src="~/lib/editormd/lib/sequence-diagram.min.js"></script>
<script src="~/lib/editormd/lib/flowchart.min.js"></script>
<script src="~/lib/editormd/lib/jquery.flowchart.min.js"></script>
<script src="~/lib/editormd/editormd.min.js"></script>
Then, use JS to call editor.md's render method
let testEditormdView = editormd.markdownToHTML("test-editormd-view", {
// htmlDecode: "style,script,iframe", // you can filter tags decode
htmlDecode: true,
//toc : false,
tocm: true, // Using [TOCM]
tocContainer: "#custom-toc-container", // 自定义 ToC 容器层
//gfm : false,
//tocDropdown : true,
// markdownSourceCode : true, // 是否保留 Markdown 源码,即是否删除保存源码的 Textarea 标签
emoji: true,
taskList: true,
tex: true, // 默认不解析
flowChart: true, // 默认不解析
sequenceDiagram: true, // 默认不解析
})
Done.
ViewModel
The Post model is just the data stored in the database. Displaying it directly cannot fully meet the needs of the page design, so as before, we need to define a ViewModel to use.
Still placed in StarBlog.Web/ViewModels
The code is as follows
public class PostViewModel {
public string Id { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string Content { get; set; }
public string ContentHtml { get; set; }
public string Path { get; set; }
public DateTime CreationTime { get; set; }
public DateTime LastUpdateTime { get; set; }
public Category Category { get; set; }
public List<Category> Categories { get; set; }
}
Compared with the Post model, it adds ContentHtml, and Categories is changed to a list
Service
That's the key rendering part. Now for some of the secondary bits~
The Service's role is to convert the Post model into a ViewModel
Let's just get to the code
public PostViewModel GetPostViewModel(Post post) {
var vm = new PostViewModel {
Id = post.Id,
Title = post.Title,
Summary = post.Summary,
Content = post.Content,
ContentHtml = Markdig.Markdown.ToHtml(post.Content),
Path = post.Path,
CreationTime = post.CreationTime,
LastUpdateTime = post.LastUpdateTime,
Category = post.Category,
Categories = new List<Category>()
};
foreach (var itemId in post.Categories.Split(",").Select(int.Parse)) {
var item = _categoryRepo.Where(a => a.Id == itemId).First();
if (item != null) vm.Categories.Add(item);
}
return vm;
}
Although we're not using the server-side rendering approach, I still kept Markdig's server-side rendering.
View
PS: I skipped the Controller part — it's really too simple, no need to paste the code
There doesn't seem to be much to introduce here either, so I won't paste the full code. The detailed code is here: https://github.com/Deali-Axy/StarBlog/blob/master/StarBlog.Web/Views/Blog/Post.cshtml
Use Bootstrap's Grid layout for two columns: the left column shows the article's TOC, and the right column shows the article's main content.
The top of the page should show the category hierarchy, with different categories separated by "/", but there should be no slash before the first category (a complicated way of putting it)
The code that implements this requirement looks like this
<div>
分类:
@foreach (var category in Model.Categories) {
@if (Model.Categories.IndexOf(category) > 0) {
<span> / </span>
}
<a asp-controller="Blog" asp-action="List"
asp-route-categoryId="@category.Id">
@category.Name
</a>
}
</div>
The result looks roughly like this:

Then we also need to improve how the time is displayed
@Model.LastUpdateTime.ToShortDateString()
@Model.LastUpdateTime.ToString("hh:mm")
The result after this is as follows
The Result

That's about it. I may further polish the page later.
Done~
_EOF_
StarBlog Development Notes (7): Page Development — Article Detail Page
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
StarBlog博客开发笔记(3):模型设计
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetCore开发博客项目 StarBlog - (3) 模型设计 ... 基于.NetCore开发博客项目 StarBlog - 接口返回值包装 基于.NetCore开发博客项目 StarBlog - 上传
2022-04-22 · StarBlog博客开发笔记 · 约 1 分钟
博客上线
终于上线啦! StarBlog博客项目从春节开始,跨越三个季节终于上线了~ 其实服务器这几天已经部署好了,然后我又测试了一会,暂时觉得OK,终于可以和大家分享了~ 博客地址是:blog.deali.cn 欢迎访问哈~ PS:友链模块即将完成,欢迎各位站长大佬们来交换友链~! 系列文章 顺便回顾一下已更新的12篇文章,接下来要介绍RESTFul接口和管理后台的前端开发了,欢迎继续关注~ 基于.Net
2022-06-20 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(23):文章列表接口分页、过滤、搜索、排序
前言 上一篇留的坑,火速补上。 在之前的第6篇中,已经有初步介绍,本文做一些补充,已经搞定这部分的同学可以快速跳过,基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表 对标准的WebApi来说,分页、过滤、搜索、排序是很常见的功能,既可以方便用户查看数据,又可以提升程序性能。 通用请求参数 定义一个类来作为通用的请求参数 列表接口通用的参数是这几个:PageSi
2023-01-02 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(14):实现主题切换功能
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetCore开发博客项目 StarBlog - (3) 模型设计 基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入 基于.NetCore开发博客项目 StarBl
2022-07-03 · StarBlog博客开发笔记 · 约 1 分钟
讨论
共 0 条加载评论中…