在上篇文章所描述的基礎(chǔ)功能,本章采用MVC實(shí)現(xiàn)一個(gè)輕量的內(nèi)容發(fā)布。
基本原理
文章發(fā)布的三要素如下圖所示:
- 欄目——也稱網(wǎng)站頻道可以視為一個(gè)樹狀的網(wǎng)站內(nèi)容逐層分類。一個(gè)欄目下包含很多文章,通常一個(gè)欄目下的文章采用相同的模版發(fā)布,以保持相同的展示形式和風(fēng)格。
- 文章——包括:題名、內(nèi)容、作者等元數(shù)據(jù)的結(jié)構(gòu)化內(nèi)容。文章可以發(fā)布到一個(gè)或者多個(gè)欄目下。一篇文章綁定一個(gè)模版之后會(huì)形成一個(gè)唯一的URL。如果一篇文章綁定了多個(gè)模版,應(yīng)當(dāng)會(huì)形成多個(gè)URL。
-
模版——負(fù)責(zé)內(nèi)容的展示形式,綁定文章(本例中為數(shù)據(jù)庫表Article中的一條記錄),render出最終的頁面效果。
內(nèi)容發(fā)布三要素
Model:數(shù)據(jù)庫表及對(duì)應(yīng)的Entity
- article表用于文章;
- tag表用于文章分類標(biāo)簽;
- attach表用于上傳圖片或者附件;
View:管理界面提供結(jié)構(gòu)化數(shù)據(jù)維護(hù),模版實(shí)現(xiàn)內(nèi)容展示
網(wǎng)站欄目的管理
欄目信息非常適合用json樹描述,用json的好處是維護(hù)方便,而且可以直接提供給后臺(tái)界面用于發(fā)布欄目的多選。
用戶選擇發(fā)布的欄目之后,直接以簡(jiǎn)單的格式分隔存到一個(gè)文本字段即完成了文章發(fā)布,用戶體驗(yàn)是不是夠簡(jiǎn)單?
當(dāng)然如果業(yè)務(wù)要求對(duì)發(fā)布內(nèi)容進(jìn)行審查,默認(rèn)的狀態(tài)可以是預(yù)覽,后期也可通過置頂設(shè)置調(diào)整文章在頻道首頁的位置。
-
同時(shí)json定義也供后臺(tái)java邏輯解析,以獲得指定欄目對(duì)應(yīng)的模版。
網(wǎng)站欄目 如果在該json中通過
tpl指定了欄目模版,發(fā)布到該欄目的文章將使用指定模版,否則使用默認(rèn)模版。

模版
模版采用JSP EL或JSTL,只負(fù)責(zé)將傳入的model在頁面中進(jìn)行展示,不含其他邏輯。
此處也曾猶豫是否引入模版組件,最終SpringSide作者的見解影響了我,模版引擎怎么說相對(duì)jsp來說也是小眾,
JSP完全勝任模版引擎任務(wù),如果不考慮前端工程師的偏好,沒必要再引入其他模版引擎,增加程序員的麻煩和系統(tǒng)復(fù)雜度。
以下為模版中顯示文章相關(guān)圖片的代碼片段:
<c:forEach items="${robjs}" var="ro">
<c:choose>
<c:when test="${ro.suffix =='jpg' || ro.suffix =='png' || ro.suffix =='jpeg' }">
<figure class="figure">

<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:三個(gè)java類
- CMSController用于將發(fā)布生成的url 映射為模版+數(shù)據(jù);
- SecNode.java用于加載json節(jié)點(diǎn)到后端;
- CMSService.java封裝具體的實(shí)現(xiàn)邏輯。
CMSController.java
采用SpringMVC的映射注解可以輕松將上述URL匹配到負(fù)責(zé)綁定模版和數(shù)據(jù)的處理邏輯。
@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 狀態(tài), 只允許管理員預(yù)覽
model.addAttribute("obj", obj);
String tpl;
if (prs.length == 1) {
tpl = TPL_DEFAULT;
} else {
tpl = prs[1];
}
// 根據(jù)頻道綁定多個(gè)文章模版
return "tpl" + PATH_ARTICLE + tpl;
} catch (Exception e) {
e.printStackTrace();
return "error/404";
}
}
CMSService.java + SecNode.java 實(shí)現(xiàn)預(yù)覽
java中解析json文件,獲得指定欄目的文章模版。
基本思路如下:
- 定義與json節(jié)點(diǎn)對(duì)應(yīng)的java類SecNode
- 利用ObjectMapper讀取json文件并解析為SecNode[]
- 遍歷SecNode[],建立欄目全路徑名稱對(duì)應(yīng)的模版文件名,
例如:用戶中心#辦理流程-> 1.jsp ; 政策法規(guī)->default.jsp - 前臺(tái)Ajax請(qǐng)求主鍵值1的文章,發(fā)布對(duì)應(yīng)的URL
- 后臺(tái)根據(jù)主鍵值從數(shù)據(jù)庫提取Article對(duì)象,得到其發(fā)布欄目為:政策法規(guī);用戶中心#辦理流程 兩個(gè)欄目
- 根據(jù)欄目獲得對(duì)應(yīng)的模版,如果欄目在json節(jié)點(diǎn)通過tpl定義了模版,則采用指定模版,否則沿用父節(jié)點(diǎn)模版。
- 根據(jù)模版和文章id 按URL定義規(guī)則拼接出URL數(shù)組,返回前端
- 前端拿到URL數(shù)組之后,采用tab窗口打開對(duì)應(yīng)的兩個(gè)頁面預(yù)覽。
內(nèi)容管理界面


發(fā)布結(jié)果
由于模版也符合decorators.xml 定義的layout規(guī)則,所以最終呈現(xiàn)的詳細(xì)頁包含了網(wǎng)站的header和footer。


