在上篇文章所描述的基础功能,本章采用MVC实现一个轻量的内容发布。
基本原理
文章发布的三要素如下图所示:
- 栏目——也称网站频道可以视为一个树状的网站内容逐层分类。一个栏目下包含很多文章,通常一个栏目下的文章采用相同的模版发布,以保持相同的展示形式和风格。
- 文章——包括:题名、内容、作者等元数据的结构化内容。文章可以发布到一个或者多个栏目下。一篇文章绑定一个模版之后会形成一个唯一的URL。如果一篇文章绑定了多个模版,应当会形成多个URL。
-
模版——负责内容的展示形式,绑定文章(本例中为数据库表Article中的一条记录),render出最终的页面效果。
Model:数据库表及对应的Entity
- article表用于文章;
- tag表用于文章分类标签;
- attach表用于上传图片或者附件;
View:管理界面提供结构化数据维护,模版实现内容展示
网站栏目的管理
栏目信息非常适合用json树描述,用json的好处是维护方便,而且可以直接提供给后台界面用于发布栏目的多选。
用户选择发布的栏目之后,直接以简单的格式分隔存到一个文本字段即完成了文章发布,用户体验是不是够简单?
当然如果业务要求对发布内容进行审查,默认的状态可以是预览,后期也可通过置顶设置调整文章在频道首页的位置。
-
同时json定义也供后台java逻辑解析,以获得指定栏目对应的模版。
如果在该json中通过
tpl
指定了栏目模版,发布到该栏目的文章将使用指定模版,否则使用默认模版。
模版
模版采用JSP EL或JSTL,只负责将传入的model在页面中进行展示,不含其他逻辑。
此处也曾犹豫是否引入模版组件,最终SpringSide作者的见解影响了我,模版引擎怎么说相对jsp来说也是小众,
JSP完全胜任模版引擎任务,如果不考虑前端工程师的偏好,没必要再引入其他模版引擎,增加程序员的麻烦和系统复杂度。
以下为模版中显示文章相关图片的代码片段:
<c:forEach items="${robjs}" var="ro">
<c:choose>
<c:when test="${ro.suffix =='jpg' || ro.suffix =='png' || ro.suffix =='jpeg' }">
<figure class="figure">
![](/fopen?fp=${ro.path})
<figcaption style="text-align:center;color:rgb(153, 153, 153);">${ro.title}</figcaption>
</figure>
<p><br></p>
<p>${ro.content}</p>
</c:when>
</c:choose>
</c:forEach>
Controller:三个java类
- CMSController用于将发布生成的url 映射为模版+数据;
- SecNode.java用于加载json节点到后端;
- CMSService.java封装具体的实现逻辑。
CMSController.java
采用SpringMVC的映射注解可以轻松将上述URL匹配到负责绑定模版和数据的处理逻辑。
@RequestMapping(value = PATH_ARTICLE + "{id_tpl}", method = RequestMethod.GET)
public String showDetailByTpl(@PathVariable("id_tpl") String id_tpl, Model model) {
try {
String[] prs = id_tpl.split("_");
long id = Long.parseLong(prs[0]);
Article obj = cmsService.getArticle(id);
// TODO 检查status 状态, 只允许管理员预览
model.addAttribute("obj", obj);
String tpl;
if (prs.length == 1) {
tpl = TPL_DEFAULT;
} else {
tpl = prs[1];
}
// 根据频道绑定多个文章模版
return "tpl" + PATH_ARTICLE + tpl;
} catch (Exception e) {
e.printStackTrace();
return "error/404";
}
}
CMSService.java + SecNode.java 实现预览
java中解析json文件,获得指定栏目的文章模版。
基本思路如下:
- 定义与json节点对应的java类SecNode
- 利用ObjectMapper读取json文件并解析为SecNode[]
- 遍历SecNode[],建立栏目全路径名称对应的模版文件名,
例如:用户中心#办理流程-> 1.jsp ; 政策法规->default.jsp - 前台Ajax请求主键值1的文章,发布对应的URL
- 后台根据主键值从数据库提取Article对象,得到其发布栏目为:政策法规;用户中心#办理流程 两个栏目
- 根据栏目获得对应的模版,如果栏目在json节点通过tpl定义了模版,则采用指定模版,否则沿用父节点模版。
- 根据模版和文章id 按URL定义规则拼接出URL数组,返回前端
- 前端拿到URL数组之后,采用tab窗口打开对应的两个页面预览。
内容管理界面
发布结果
由于模版也符合decorators.xml
定义的layout规则,所以最终呈现的详细页包含了网站的header和footer。