我開發的博客網站的地址:http://118.89.29.170/RiXiang_blog/
博客項目代碼github:https://github.com/SonnAdolf/sonne_blog
有了我的已成型的項目和代碼,可以更容易理解這篇文章。
本篇文章記錄下自己博客項目評論功能實現的全過程,重點其實是評論回復功能。
【一,寫評論】
寫評論部分我沒有使用富文本編輯器,只是單純地使用了textarea標簽,所以后台不需要作html標簽的白名單檢驗(關於防范xss攻擊,可以看我之前的一篇文章http://www.cnblogs.com/rixiang/p/6239464.html),只需要將所有<和>字符作轉義即可。
提交到后台需要做的處理:必要的校驗,存儲。然后生成消息。消息會在用戶個人空間消息中心顯示。提示用戶有新的評論。
數據庫存儲方面,評論與文章的關系是雙向多對一。設置懶加載和級聯刪除。
寫評論部分就這些,沒什么好說的。
【二,評論顯示】
評論的顯示是基於文章的。也就是說在點擊、查看一篇文章的同時,在該文章下面顯示對這篇文章的所有評論。
上面提到評論和文章是多對一的關系,可知,查詢到文章即可查詢到該文章的所有評論。也正因此,且鑒於自己博客評論數並不很多情況,對於評論的分頁我沒有采用數據庫查詢層的分頁方法,而是用java寫了分頁、排序。我並不確定最好的實現。
/* * Select the article by the id, and show it at the jsp page. * * @param HttpServletRequest request, Integer id, Model model * * @return the jsp page */ @RequestMapping(value = "/show", method = RequestMethod.GET) public String showFromMainPage(HttpServletRequest request, Integer id, Integer currentPage, Model model) throws Exception { if (null == id) { return "error"; } Article article = articleService.find(id, Article.class); // click the link, then read_times ++ article.setRead_times(article.getRead_times() + 1); articleService.update(article); article = getArticleOfContentByUrl(article); // sort the comments List<Comment> comments = commentService.sort(article.getComments()); String username = userService.getUsernameFromSession(request); model.addAttribute("article", article); model.addAttribute("username", username); model.addAttribute("article_id", id); if (currentPage == null || currentPage <= 0) { currentPage = 1; } int totalSize = comments.size(); PageInfo pageInfo = PageUtils.createPage(10, comments.size(), currentPage); int beginIndex = pageInfo.getBeginIndex(); long totalNum = pageInfo.getTotalCount(); int everyPage = pageInfo.getEveryPage(); if (totalNum - beginIndex < everyPage) { comments = comments.subList(beginIndex, (int) totalNum); } else { comments = comments.subList(beginIndex, beginIndex + everyPage); } // 評論分頁 Page<Comment> comments_page = new Page<Comment>(comments, totalSize, pageInfo); model.addAttribute("comments_page", comments_page); return "showArticlePage"; }
以上代碼可看出,訪問文章路徑需要兩個參數,一個是文章id,一個是評論頁,類似這樣:http://118.89.29.170/RiXiang_blog/article/show.form?id=101¤tPage=2
根據id查詢文章Article article = articleService.find(id, Article.class);
然后獲取此文章的所有評論List<Comment> comments = commentService.sort(article.getComments());
之后便可以根據評論總數、每頁評論數、當前頁這三項信息來創建分頁類。關於分頁工具類,可以看我之前總結的這篇文章http://www.cnblogs.com/rixiang/p/5257085.html
一般情況下,分頁的邏輯要放在數據庫查詢層,而非java后台的service和controller層,我的這個博客項目也實現了基於jpa的查詢分頁底層工具類。
【三、評論回復功能實現】
評論回復是本篇的重點。這部分我是參考博客園的邏輯實現的,效果是這樣:
之后我F12查看了下博客園的前端代碼,然后便有了思路了。
下面先寫下流程:
1,點擊文章下評論欄【回復】鏈接后,調用javascript方法。
2,組合回復內容:@usrname [quote]+引用內容+[/quote]作為寫評論的<textarea>標簽的初始value。
3,用戶在二的基礎上寫了回復后,點提交,數據庫會存儲帶有[quote]標簽的Comment內容,由於是[]而不是<>所以不會因為xss跨站腳本校驗而被攔截。然后生成回復的消息(用於通知被回復用戶新消息)。
4,頁面顯示時,查詢到的數據庫中的Comment內容(帶有[quote]標簽的Comment內容),在前端顯示前用javascript作字符串轉化:
[quote]替換為
<fieldset class="comment_quote">
<legend>引用</legend>
xxxxxxxx(引用的內容)
[/quote]替換為</fieldset><br>
這樣便完成了html的拼接。顯示出來的將不是[quote][/quote],而是:
上述流程的第一二點的實現(quote拼接):
下面是基於jsp標簽的評論顯示:
<c:forEach items="${comments_page.content}" var="comment"> <div class="comment_box"> <span class = "date">#${comment.floor}樓  ${fn:substring(comment.date,0,16)}</span>   <span class = "author">${comment.authorName}</span>     【<a href="javascript:void(0)" onclick="quote_comment('${comment.content}','${comment.authorName}')">回復</a>】<br> <p class = "comment_content">${comment.content}</p> </div> </c:forEach>
【<a href="javascript:void(0)" onclick="quote_comment('${comment.content}','${comment.authorName}')">回復</a>】表示點擊【回復】鏈接后,調用quote_comment(content, usr_name)這一javascript方法。注意點擊鏈接調用js方法的寫法。以及jsp標簽內容作為js方法參數的寫法,里面的單引號一定不能落下。
function quote_comment(content, usr_name) { quote_content = '@' + usr_name + ' [quote]' + content.trim() + '[/quote]'; document.getElementById("comment_txt").value = quote_content; document.getElementsByTagName('body')[0].scrollTop=document.getElementsByTagName('body')[0].scrollHeight; }
上面代碼用於組裝@用戶名和[quote]標簽,document.getElementById("comment_txt").value = quote_content;這句用於給textarea(寫評論輸入框)賦值。
document.getElementsByTagName('body')[0].scrollTop=document.getElementsByTagName('body')[0].scrollHeight;表示跳轉至頁面最底端。(寫評論輸入框在頁面最底下)
上述流程的第三四點(回復內容顯示部分):
在
$(document).ready(function() { ......});
里加入這兩句(頁面初始化時候):
var comment_arr=getElementsClass("comment_content"); commentsQuoteTagReset(comment_arr);
首先調用方法function getElementsClass(classnames)獲取所有class屬性為comment_content的內容,(上面貼的jsp代碼,<p class = "comment_content">${comment.content}</p>這句說明設置顯示評論內容的p標簽的class為comment_content,然后我們根據該class標簽做javascript dom操作):
function getElementsClass(classnames){ var classobj= new Array(); //數組下標 var classint=0; //獲取HTML的所有標簽 var tags=document.getElementsByTagName("*"); for(var i in tags){ if(tags[i].nodeType==1){ //判斷節點類型 if(tags[i].getAttribute("class") == classnames) { classobj[classint]=tags[i]; classint++; } } } return classobj; }
之后替換[quote]和[/quote]:
function commentsQuoteTagReset(comment_arr) { var str; for(var i=0; i < comment_arr.length; i++) { str = comment_arr[i].innerHTML; str = str.replace(/\[quote]/g,"<fieldset class=\"comment_quote\"><legend>引用</legend>"); str = str.replace(/\[\/quote]/g,"</fieldset>"); comment_arr[i].innerHTML=str; } }
要注意javascript里字符串替換沒有類似java replaceAll的寫法,replace的話只能替換查詢到的第一個。
需要這樣的寫法:
str = str.replace(/\aaa/g,'bbb');
comment_arr[i].innerHTML=str;寫法可以替換對應的<p>標簽的內容。
這樣實現的效果便是:
以上です
主要講的是思路,加上一些我自己覺得有必要記錄的技術點。細節方面,如果基礎好的話,有了思路就可以瞬間懂了。我就是f12看了博客園頁面然后瞬間有了思路。
最后貼幾張博客的圖片。這個博客功能基本都已實現,以后就是不斷優化了。