Chapter III
3.1. Trie tree filtering sensitive words
Sensitive words file: sensitive words txt
gambling go whoring Drug Billing
Filter sensitive words tool class sensitivefilter java
Note: the Bean is initialized when the service is started, and then the method annotated by @ PostConstruct is called. So the init() method in the SensitiveFilter class is called as soon as the program starts.
package com.nowcoder.community.util; import org.apache.commons.lang3.CharUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; @Component public class SensitiveFilter { private static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class); // Substitution character private static final String REPLACEMENT = "***"; // Root node private TrieNode rootNode = new TrieNode(); @PostConstruct public void init() { try ( InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt"); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); ) { String keyword; while ((keyword = reader.readLine()) != null) { // Add to prefix tree this.addKeyword(keyword); } } catch (IOException e) { logger.error("Failed to load sensitive word file: " + e.getMessage()); } } // Add a sensitive word to the prefix tree private void addKeyword(String keyword) { TrieNode tempNode = rootNode; for (int i = 0; i < keyword.length(); i++) { char c = keyword.charAt(i); TrieNode subNode = tempNode.getSubNode(c); if (subNode == null) { // Initialize child nodes subNode = new TrieNode(); tempNode.addSubNode(c, subNode); } // Point to the child node and enter the next cycle tempNode = subNode; // Set end ID if (i == keyword.length() - 1) { tempNode.setKeywordEnd(true); } } } /** * Filter sensitive words * * @param text Text to be filtered * @return Filtered text */ public String filter(String text) { if (StringUtils.isBlank(text)) { return null; } // Pointer 1 TrieNode tempNode = rootNode; // Pointer 2 int begin = 0; // Pointer 3 int position = 0; // result StringBuilder sb = new StringBuilder(); while (position < text.length()) { char c = text.charAt(position); // Skip symbol if (isSymbol(c)) { // If the pointer is at 1, the result will be included in this step if (tempNode == rootNode) { sb.append(c); begin++; } // Pointer 3 goes one step down regardless of whether the symbol is at the beginning or in the middle position++; continue; } // Check child nodes tempNode = tempNode.getSubNode(c); if (tempNode == null) { // String starting with begin is not a sensitive word sb.append(text.charAt(begin)); // Go to the next position position = ++begin; // Re point to the root node tempNode = rootNode; } else if (tempNode.isKeywordEnd()) { // If sensitive words are found, replace the begin~position string sb.append(REPLACEMENT); // Go to the next position begin = ++position; // Re point to the root node tempNode = rootNode; } else { // Check next character position++; } } // Count the last batch of characters into the result sb.append(text.substring(begin)); return sb.toString(); } // Judge whether it is a symbol private boolean isSymbol(Character c) { // 0x2E80~0x9FFF are East Asian characters return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF); } // trie private class TrieNode { // Keyword end identifier private boolean isKeywordEnd = false; // Child node (key is a child character and value is a child node) private Map<Character, TrieNode> subNodes = new HashMap<>(); public boolean isKeywordEnd() { return isKeywordEnd; } public void setKeywordEnd(boolean keywordEnd) { isKeywordEnd = keywordEnd; } // Add child node public void addSubNode(Character c, TrieNode node) { subNodes.put(c, node); } // Get child nodes public TrieNode getSubNode(Character c) { return subNodes.get(c); } } }
Test, sensitivetests java
package com.nowcoder.community; import com.nowcoder.community.util.SensitiveFilter; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest @ContextConfiguration(classes = CommunityApplication.class) public class SensitiveTests { @Autowired private SensitiveFilter sensitiveFilter; @Test public void testSensitiveFilter() { String text = "You can gamble here,You can whore,Can take drugs,Can be invoiced,Ha ha ha!"; text = sensitiveFilter.filter(text); System.out.println(text); text = "Here you can☆bet☆Bo☆,sure☆Whoring☆Whore☆,sure☆absorb☆poison☆,sure☆open☆ticket☆,Ha ha ha!"; text = sensitiveFilter.filter(text); System.out.println(text); } }
3.6. AJAX post
pom.xml
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.58</version> </dependency>
CommunityUtil.java :
public class CommunityUtil { public static String getJSONString(int code, String msg, Map<String, Object> map) { JSONObject json = new JSONObject(); json.put("code", code); json.put("msg", msg); if (map != null) { for (String key : map.keySet()) { json.put(key, map.get(key)); } } return json.toJSONString(); } public static String getJSONString(int code, String msg) { return getJSONString(code, msg, null); } public static String getJSONString(int code) { return getJSONString(code, null, null); } public static void main(String[] args) { Map<String, Object> map = new HashMap<>(); map.put("name", "zhangsan"); map.put("age", 25); System.out.println(getJSONString(0, "ok", map)); } }
Test part:
AlphaController.java test
@Controller @RequestMapping("/alpha") public class AlphaController { // ajax example @RequestMapping(path = "/ajax", method = RequestMethod.POST) @ResponseBody public String testAjax(String name, int age) { System.out.println(name); System.out.println(age); return CommunityUtil.getJSONString(0, "Operation successful!"); } }
Test page, / HTML / Ajax demo html
Note: $ ajax(); It can be simplified to $ post(); And $ get();.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>AJAX</title> </head> <body> <p> <input type="button" value="send out" onclick="send();"> </p> <script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script> <script> function send() { $.post( "/community/alpha/ajax", {"name":"Zhang San","age":23}, function(data) { console.log(typeof(data)); console.log(data); data = $.parseJSON(data); console.log(typeof(data)); console.log(data.code); console.log(data.msg); } ); } </script> </body> </html>
Test results:
AJAX request is adopted to realize the function of publishing posts
discusspost-mapper.xml
<insert id="insertDiscussPost" parameterType="DiscussPost"> insert into discuss_post(<include refid="insertFields"></include>) values(#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score}) </insert>
Service,DiscussPostService.java:
notes:
HtmlUtils. htmlEscape("<div>"); Get & lt; div>
HtmlUtils. htmlUnescape("<div>"); Get < div >
public int addDiscussPost(DiscussPost post) { if (post == null) { throw new IllegalArgumentException("Parameter cannot be empty!"); } // Escape HTML tags post.setTitle(HtmlUtils.htmlEscape(post.getTitle())); post.setContent(HtmlUtils.htmlEscape(post.getContent())); // Filter sensitive words post.setTitle(sensitiveFilter.filter(post.getTitle())); post.setContent(sensitiveFilter.filter(post.getContent())); return discussPostMapper.insertDiscussPost(post); }
Controller, 0803 final discusspostcontroller java:
@Controller @RequestMapping("/discuss") public class DiscussPostController implements CommunityConstant { @Autowired private DiscussPostService discussPostService; @Autowired private HostHolder hostHolder; @RequestMapping(path = "/add", method = RequestMethod.POST) @ResponseBody public String addDiscussPost(String title, String content) { User user = hostHolder.getUser(); if (user == null) { return CommunityUtil.getJSONString(403, "You haven't logged in yet!"); } DiscussPost post = new DiscussPost(); post.setUserId(user.getId()); post.setTitle(title); post.setContent(content); post.setCreateTime(new Date()); discussPostService.addDiscussPost(post); // Errors reported will be handled in a unified manner in the future return CommunityUtil.getJSONString(0, "Published successfully!"); } }
0803 version of index JS is the operation after clicking the Publish button
$(function(){ $("#publishBtn").click(publish); }); function publish() { $("#publishModal").modal("hide"); // Before sending the AJAX request, set the CSRF token into the request header // var token = $("meta[name='_csrf']").attr("content"); // var header = $("meta[name='_csrf_header']").attr("content"); // $(document).ajaxSend(function(e, xhr, options){ // xhr.setRequestHeader(header, token); // }); // Get title and content var title = $("#recipient-name").val(); var content = $("#message-text").val(); // Send asynchronous request (POST) $.post( CONTEXT_PATH + "/discuss/add", {"title":title,"content":content}, function(data) { data = $.parseJSON(data); // Display the return message in the prompt box $("#hintBody").text(data.msg); // Display prompt box $("#hintModal").modal("show"); // After 2 seconds, the prompt box will be hidden automatically setTimeout(function(){ $("#hintModal").modal("hide"); // Refresh page if(data.code == 0) { window.location.reload(); } }, 2000); } ); }
3.11 post details (one line of CRUD)
DiscussPostMapper interface
discusspost-mapper.xml
DiscussPostService class
Controller, discusspostcontroller version 0803 java
@RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET) public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page) { // Posts DiscussPost post = discussPostService.findDiscussPostById(discussPostId); model.addAttribute("post", post); // author User user = userService.findUserById(post.getUserId()); model.addAttribute("user", user); return "/site/discuss-detail"; }
index.html
notes:
< a th: href = "@ {| / discussions / detail / ${map. Post. ID} |}" > Hello < / a >, there are variables and constants, so add vertical lines on both sides.
<a th:href="@{|/discuss/detail/${map.post.id}|}" th:utext="${map.post.title}">Prepare for spring moves, brush questions in the interview and review with him. It's all done in a month!</a>
discuss-detail.html page
3.13. Transaction management (important and new knowledge. Only test, no project code)
Mainly explain ppt.
Spring transaction management
- Declarative transaction
- Declare the transaction characteristics of a method through XML configuration.
- Declare the transaction characteristics of a method through annotation.
- For: This is preferred and simple.
- Programming transaction
- Manage transactions through TransactionTemplate and perform database operations through it.
- For: the business logic is complex. We just want to manage a small number of transactions in the middle.
test
AlphaService.java
Note: A calls B. for B, A is the current transaction (external transaction)
@Service //@Scope("prototype") public class AlphaService { @Autowired private UserMapper userMapper; @Autowired private DiscussPostMapper discussPostMapper; @Autowired private TransactionTemplate transactionTemplate; // REQUIRED: support the current transaction (external transaction). If it does not exist, create a new transaction // REQUIRES_NEW: create a new transaction and suspend the current transaction (external transaction) // NESTED: if there is a transaction (external transaction), it will be NESTED in the transaction (independent commit and rollback), otherwise it will be the same as REQUIRED @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) public Object save1() { // New user User user = new User(); user.setUsername("alpha"); user.setSalt(CommunityUtil.generateUUID().substring(0, 5)); user.setPassword(CommunityUtil.md5("123" + user.getSalt())); user.setEmail("alpha@qq.com"); user.setHeaderUrl("http://image.nowcoder.com/head/99t.png"); user.setCreateTime(new Date()); userMapper.insertUser(user); // New post DiscussPost post = new DiscussPost(); post.setUserId(user.getId()); post.setTitle("Hello"); post.setContent("New reports !"); post.setCreateTime(new Date()); discussPostMapper.insertDiscussPost(post); Integer.valueOf("abc"); return "ok"; } public Object save2() { transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); return transactionTemplate.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus status) { // New user User user = new User(); user.setUsername("beta"); user.setSalt(CommunityUtil.generateUUID().substring(0, 5)); user.setPassword(CommunityUtil.md5("123" + user.getSalt())); user.setEmail("beta@qq.com"); user.setHeaderUrl("http://image.nowcoder.com/head/999t.png"); user.setCreateTime(new Date()); userMapper.insertUser(user); // New post DiscussPost post = new DiscussPost(); post.setUserId(user.getId()); post.setTitle("Hello"); post.setContent("I'm new!"); post.setCreateTime(new Date()); discussPostMapper.insertDiscussPost(post); Integer.valueOf("abc"); return "ok"; } }); } }
TransactionTests.java
package com.nowcoder.community; import com.nowcoder.community.service.AlphaService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(classes = CommunityApplication.class) public class TransactionTests { @Autowired private AlphaService alphaService; @Test public void testSave1() { Object obj = alphaService.save1(); System.out.println(obj); } @Test public void testSave2() { Object obj = alphaService.save2(); System.out.println(obj); } }
3.20. Display comments (complex) (CRUD one line)
CommentMapper interface
comment-mapper.xml
<sql id="selectFields"> id, user_id, entity_type, entity_id, target_id, content, status, create_time </sql> <sql id="insertFields"> user_id, entity_type, entity_id, target_id, content, status, create_time </sql> <select id="selectCommentsByEntity" resultType="Comment"> select <include refid="selectFields"></include> from comment where status = 0 and entity_type = #{entityType} and entity_id = #{entityId} order by create_time asc limit #{offset}, #{limit} </select> <select id="selectCountByEntity" resultType="int"> select count(id) from comment where status = 0 and entity_type = #{entityType} and entity_id = #{entityId} </select>
CommentService.java
Controller, discusspostcontroller version 0803 java
@RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET) public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page) { // Posts DiscussPost post = discussPostService.findDiscussPostById(discussPostId); model.addAttribute("post", post); // author User user = userService.findUserById(post.getUserId()); model.addAttribute("user", user); // Number of likes long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, discussPostId); model.addAttribute("likeCount", likeCount); // Like status int likeStatus = hostHolder.getUser() == null ? 0 : likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_POST, discussPostId); model.addAttribute("likeStatus", likeStatus); // Comment paging information page.setLimit(5); page.setPath("/discuss/detail/" + discussPostId); page.setRows(post.getCommentCount()); // Comments: comments to posts // Re: comments for comments // Comment list List<Comment> commentList = commentService.findCommentsByEntity( ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit()); // Comment VO list List<Map<String, Object>> commentVoList = new ArrayList<>(); if (commentList != null) { for (Comment comment : commentList) { // Comment VO Map<String, Object> commentVo = new HashMap<>(); // comment commentVo.put("comment", comment); // author commentVo.put("user", userService.findUserById(comment.getUserId())); // Number of likes likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT, comment.getId()); commentVo.put("likeCount", likeCount); // Like status likeStatus = hostHolder.getUser() == null ? 0 : likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, comment.getId()); commentVo.put("likeStatus", likeStatus); // Reply list List<Comment> replyList = commentService.findCommentsByEntity( ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE); // Reply to VO list List<Map<String, Object>> replyVoList = new ArrayList<>(); if (replyList != null) { for (Comment reply : replyList) { Map<String, Object> replyVo = new HashMap<>(); // reply replyVo.put("reply", reply); // author replyVo.put("user", userService.findUserById(reply.getUserId())); // Reply target User target = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId()); replyVo.put("target", target); // Number of likes likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT, reply.getId()); replyVo.put("likeCount", likeCount); // Like status likeStatus = hostHolder.getUser() == null ? 0 : likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, reply.getId()); replyVo.put("likeStatus", likeStatus); replyVoList.add(replyVo); } } commentVo.put("replys", replyVoList); // Number of replies int replyCount = commentService.findCommentCount(ENTITY_TYPE_COMMENT, comment.getId()); commentVo.put("replyCount", replyCount); commentVoList.add(commentVo); } } model.addAttribute("comments", commentVoList); return "/site/discuss-detail"; }
discuss-detail.html
3.22. Add comments (CRUD one line)
Sometimes when there is no mistake, IDEA will report a red line. The solution: Ctrl+F9 recompile. If it is not good, cut and paste the class marked with the red line back. There will be no error and no red line.
Transactions are used, because after adding comments, you need to add discussions_ Modify the number of comments in the post table.
CommentMapper interface
comment-mapper.xml
CommentService
-
DiscussPostMapper interface
discusspost-mapper.xml
DiscussPostService class
-
CommentController version 0803
package com.nowcoder.community.controller; import com.nowcoder.community.entity.Comment; import com.nowcoder.community.entity.DiscussPost; import com.nowcoder.community.entity.Event; import com.nowcoder.community.event.EventProducer; import com.nowcoder.community.service.CommentService; import com.nowcoder.community.service.DiscussPostService; import com.nowcoder.community.util.CommunityConstant; import com.nowcoder.community.util.HostHolder; import com.nowcoder.community.util.RedisKeyUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import java.util.Date; @Controller @RequestMapping("/comment") public class CommentController implements CommunityConstant { @Autowired private CommentService commentService; @Autowired private HostHolder hostHolder; @Autowired private EventProducer eventProducer; @Autowired private DiscussPostService discussPostService; @Autowired private RedisTemplate redisTemplate; @RequestMapping(path = "/add/{discussPostId}", method = RequestMethod.POST) public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment) { comment.setUserId(hostHolder.getUser().getId()); comment.setStatus(0); comment.setCreateTime(new Date()); commentService.addComment(comment); // Trigger comment event Event event = new Event() .setTopic(TOPIC_COMMENT) .setUserId(hostHolder.getUser().getId()) .setEntityType(comment.getEntityType()) .setEntityId(comment.getEntityId()) .setData("postId", discussPostId); if (comment.getEntityType() == ENTITY_TYPE_POST) { DiscussPost target = discussPostService.findDiscussPostById(comment.getEntityId()); event.setEntityUserId(target.getUserId()); } else if (comment.getEntityType() == ENTITY_TYPE_COMMENT) { Comment target = commentService.findCommentById(comment.getEntityId()); event.setEntityUserId(target.getUserId()); } eventProducer.fireEvent(event); if (comment.getEntityType() == ENTITY_TYPE_POST) { // Trigger posting event event = new Event() .setTopic(TOPIC_PUBLISH) .setUserId(comment.getUserId()) .setEntityType(ENTITY_TYPE_POST) .setEntityId(discussPostId); eventProducer.fireEvent(event); // Calculate post score String redisKey = RedisKeyUtil.getPostScoreKey(); redisTemplate.opsForSet().add(redisKey, discussPostId); } return "redirect:/discuss/detail/" + discussPostId; } }
discuss-detail.html
<!-- Reply to someone's reply input box --> <div th:id="|huifu-${rvoStat.count}|" class="mt-4 collapse"> <form method="post" th:action="@{|/comment/add/${post.id}|}"> <div> <input type="text" class="input-size" name="content" th:placeholder="|reply ${rvo.user.username}|"/> <input type="hidden" name="entityType" value="2"> <input type="hidden" name="entityId" th:value="${cvo.comment.id}"> <input type="hidden" name="targetId" th:value="${rvo.user.id}"> </div> <div class="text-right mt-2"> <button type="submit" class="btn btn-primary btn-sm" onclick="#"> & nbsp; & nbsp; back to & nbsp; & nbsp; reply to & nbsp; & nbsp; < / button > </div> </form> </div>
<!-- Reply input box --> <li class="pb-3 pt-3"> <form method="post" th:action="@{|/comment/add/${post.id}|}"> <div> <input type="text" class="input-size" name="content" placeholder="Please enter your opinion"/> <input type="hidden" name="entityType" value="2"> <input type="hidden" name="entityId" th:value="${cvo.comment.id}"> </div> <div class="text-right mt-2"> <button type="submit" class="btn btn-primary btn-sm" onclick="#"> & nbsp; & nbsp; back to & nbsp; & nbsp; reply to & nbsp; & nbsp; < / button > </div> </form> </li>
<!-- Reply input --> <div class="container mt-3"> <form class="replyform" method="post" th:action="@{|/comment/add/${post.id}|}"> <p class="mt-3"> <a name="replyform"></a> <textarea placeholder="Feel free to express your views here!" name="content"></textarea> <input type="hidden" name="entityType" value="1"> <input type="hidden" name="entityId" th:value="${post.id}"> </p> <p class="text-right"> <button type="submit" class="btn btn-primary btn-sm"> return Placard </button> </p> </form> </div>
3.24. Private letter list (one line of CRUD)
MessageMapper interface
message-mapper.xml
Test in MapperTests class
MessageService
MessageController
letter.html
letter-detail.html
3.27 sending private messages (one line of CRUD)
The sending here is not the kind of network transmission, but the sender, which puts the private message into the database of the server. Then, when the receiver refreshes the page, it will read the private message from the database.
The problem of the project: the User name should not be the same, because the userservice in the sendLetter() method in the MessageController class Finduserbyname (toname), query the User with the User name. However, the User of the database has only the User name and no nickname. The User name should not be the same, but the nickname can be the same.
There are two places to send private messages:
1. Message list page. Send a private letter here to write the user name of the other party, just like sending an email.
2. A page that sends messages to someone. Sending private messages here is like wechat. Poke it in and chat directly.
- Send private messages
- Send private messages asynchronously.
- Refresh the private message list after sending successfully.
- Set read
- When accessing private message details, set the displayed private message to read status.
MessageMapper interface
// New message int insertMessage(Message message); // Modify the status of the message int updateStatus(List<Integer> ids, int status);
message-mapper.xml
MessageService
MessageController
@RequestMapping(path = "/letter/send", method = RequestMethod.POST) @ResponseBody public String sendLetter(String toName, String content) { User target = userService.findUserByName(toName); if (target == null) { return CommunityUtil.getJSONString(1, "Target user does not exist!"); } Message message = new Message(); message.setFromId(hostHolder.getUser().getId()); message.setToId(target.getId()); if (message.getFromId() < message.getToId()) { message.setConversationId(message.getFromId() + "_" + message.getToId()); } else { message.setConversationId(message.getToId() + "_" + message.getFromId()); } message.setContent(content); message.setCreateTime(new Date()); messageService.addMessage(message); return CommunityUtil.getJSONString(0); }
letter.js, when sending a private message, click the send button "#sendBtn" and come here.
$(function(){ $("#sendBtn").click(send_letter); $(".close").click(delete_msg); }); function send_letter() { $("#sendModal").modal("hide"); var toName = $("#recipient-name").val(); var content = $("#message-text").val(); $.post( CONTEXT_PATH + "/letter/send", {"toName":toName,"content":content}, function(data) { data = $.parseJSON(data); if(data.code == 0) { $("#hintBody").text(" sent successfully! "); } else { $("#hintBody").text(data.msg); } $("#hintModal").modal("show"); setTimeout(function(){ $("#hintModal").modal("hide"); location.reload(); }, 2000); } ); } function delete_msg() { // TODO delete data $(this).parents(".media").remove(); }
Unread messages are set to read.
MessageController
private List<Integer> getLetterIds(List<Message> letterList) { List<Integer> ids = new ArrayList<>(); if (letterList != null) { for (Message message : letterList) { if (hostHolder.getUser().getId() == message.getToId() && message.getStatus() == 0) { ids.add(message.getId()); } } } return ids; }
Modify the getLetterDetail method in MessageController.
3.31. Handle exceptions uniformly (@ ControllerAdvice@ExceptionHandler )
- @ControllerAdvice
- Used to decorate a class, indicating that this class is the global configuration class of Controller.
- In this class, three global configurations can be made for the Controller: exception handling scheme, binding data scheme and binding parameter scheme.
- @ExceptionHandler - used to decorate the method, which will be called after the Controller has an exception and used to handle the caught exception.
- @ModelAttribute - used to decorate the method, which will be called before the Controller method is executed to bind parameters to the Model object.
- @DataBinder - used to decorate the method, which will be called before the Controller method is executed, and is used to bind the converter of parameters.
In SpringBoot, 404 HTML and 500 Drag HTML to the template/error / directory, and the two pages will take effect automatically.
404 nothing, 500 errors need to be recorded in a log.
HomeController
@RequestMapping(path = "/error", method = RequestMethod.GET) public String getErrorPage() { return "/error/500"; } @RequestMapping(path = "/denied", method = RequestMethod.GET) public String getDeniedPage() { return "/error/404"; }
Create a new package advice under the controller package, / controller/advice/ExceptionAdvice
Comments: @ ExceptionHandler({Exception.class}) / / multiple exceptions can be written in braces, indicating which exceptions to handle
package com.nowcoder.community.controller.advice; import com.nowcoder.community.util.CommunityUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @ControllerAdvice(annotations = Controller.class) public class ExceptionAdvice { private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class); @ExceptionHandler({Exception.class})//Multiple exceptions can be written in braces to indicate which exceptions to handle public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException { logger.error("Server exception: " + e.getMessage()); for (StackTraceElement element : e.getStackTrace()) { logger.error(element.toString()); } String xRequestedWith = request.getHeader("x-requested-with"); if ("XMLHttpRequest".equals(xRequestedWith)) { response.setContentType("application/plain;charset=utf-8"); PrintWriter writer = response.getWriter(); writer.write(CommunityUtil.getJSONString(1, "Server exception!")); } else { response.sendRedirect(request.getContextPath() + "/error"); } } }
3.33 unified logging (AOP)
You cannot use @ ControllerAdvice because this is for Exception. The interceptor cannot be used, because the interceptor can only be used for the Controller, and we record logs, which may not only be used for the Controller, but also for the Service and data access layer.
AOP and OOP are complementary, not competitive.
New package aspect
test
AlphaAspect
Note: @ pointcut ("execution (* com. Nowcode. Community. Service. * * (..)"), The first * indicates all return values, com nowcoder. community. Service is the package name, the second * represents the class name, and the third * represents the method name, (..) Represents all parameters. Write add * in the method name to indicate the method starting with add.
package com.nowcoder.community.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; //@Component //@Aspect public class AlphaAspect { @Pointcut("execution(* com.nowcoder.community.service.*.*(..))") public void pointcut() { } @Before("pointcut()") public void before() { System.out.println("before"); } @After("pointcut()") public void after() { System.out.println("after"); } @AfterReturning("pointcut()") public void afterRetuning() { System.out.println("afterRetuning"); } @AfterThrowing("pointcut()") public void afterThrowing() { System.out.println("afterThrowing"); } @Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("around before"); Object obj = joinPoint.proceed(); System.out.println("around after"); return obj; } }
output
around before before around after after afterRetuning
ServiceLogAspect
A log should be recorded before accessing the service method every time.
package com.nowcoder.community.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.text.SimpleDateFormat; import java.util.Date; //@Component //@Aspect public class ServiceLogAspect { private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class); @Pointcut("execution(* com.nowcoder.community.service.*.*(..))") public void pointcut() { } @Before("pointcut()") public void before(JoinPoint joinPoint) {//The method being accessed by joinPoint is // The user [1.2.3.4] visited [com.nowcoder.community.service.xxx()] on [xxx] ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes == null) { return; } HttpServletRequest request = attributes.getRequest(); String ip = request.getRemoteHost(); String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();//Class name Method name logger.info(String.format("user[%s],stay[%s],Visited[%s].", ip, now, target)); } }