SpringBoot实战项目——博客笔记项目

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、项目介绍
  • 二、项目的整体框架
  •       2.1 数据库模块
  •       2.2 前端模块
  •       2.3 后端模块
  • 三、项目图片展示
  • 四、项目的实现
  •       4.1 准备工作
  •             4.1.1 数据库,表的创建
  •             4.1.2 配置数据库和MyBatis
  •             4.1.3 引入前端项目
  •       4.2 统一功能处理
  •             4.2.1 统一异常功能处理
  •             4.2.2 统一数据格式返回
  •             4.2.3 统一用户登录验证
  •       4.3 博客登录页的实现
  •             4.3.1 实体类的创建
  •             4.3.2 前后端交互
  •             4.3.3 后端代码实现
  •       4.4 博客列表页的实现
  •       4.5 博客修改删除功能实现
  •       4.6 博客分页功能实现
  • 总结


前言


一、项目介绍

项目名称:博客笔记项目

主要操作的对象是:文章和用户,用户可在该系统上发表自己的博客,查看自己或别人已经发表的文章


需要用到两张表:userinfo(用户表)、articleinfo(文章表)


需要实现的功能:

登录+注册

博客的分页列表功能

新增发表博客

修改、删除自己的博客


 项目亮点

  1. 手动对用户密码实现随机加盐
  2. 统一异常处理、拦截器
  3. 用户登录持久化(session内存)
  4. 分页功能

项目技术栈

SSM(SpringBoot + SpringMVC + MyBatis)

MySQL

jQuery


二、项目的整体框架

2.1 数据库模块

两张表:用户表 + 文章表


2.2 前端模块

前端设计到7个页面

  • login.html登录页
  • reg.html注册页
  • blog_list.html总的博客列表页——》用到了blog_l
  • myblog_list.html个人博客列表页
  • blog_content.html博客详情页
  • blog_edit.html博客编辑页
  • blog_update.html博客修改页


2.3 后端模块

后端模块:


  • 控制层(controller包)——》控制器
  • 服务层(service包)——》服务类
  • 持久层--数据访问层(mapper类 + mapper.xml)——》mapper
  • 实体层(model包)——》实体类
  • 工具层(config包+util包)——》统一异常处理、统一返回、随机加盐


 三、项目具体展示

四、项目的实现

4.1 准备工作

首先我们新建一个springboot项目,项目具体的创建流程我这里就不在赘述。

详细见链接:SpringBoot的创建icon-default.png?t=N7T8http://t.csdnimg.cn/XCd3R


引入需要的依赖pom.xml代码:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.8</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.10</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins><finalName>sxy_blog_system</finalName></build></project>

4.1.1 数据库,表的创建

1.用户表的创建


2.文章表的创建


4.1.2 配置数据库和MyBatis

1.配置数据库的连接信息


注意: 这里的很多内容是固定的

# 数据库连接配置
Spring:datasource:url: jdbc:mysql://localhost:3306/你要连接的数据库名?characterEncoding=utf8&useSSL=falseusername: 用户名password: 自己的密码driver-class-name: com.mysql.cj.jdbc.Driver #只要你数据库用的是mysql这个是固定的

2.配置MyBatis XML存放规则和命名规则


此时我们已经在通过maven将MySQL Driver和MyBatis Framework这两个包导入了进来,此时启动项目,项目依然能够正常运行,说明我们数据库连接是正常的。


4.1.3 引入前端项目

下面的前端的静态资源复制到我们resource/static目录下面:


4.2 统一功能处理

上面我们说了,当程序出现了异常获取其他情况,我们不统一处理。

4.2.1 统一异常功能处理

所以我们需要单独在工具层中(我们的common包下面,建一个统一异常处理的类)


4.2.2 统一数据格式返回

一般在web项目中,我们前后端都是通过json这种数据格式来交换数据格式

——》我们后端需要给前端返回json格式的数据。

我们前后端用户交互的数据个数一般是统一的,不会出现你一个接口用一种数据格式,而那个接口又换了,这样就会有很多问题,不利于开发。


统一数据的优点:

  1. 方便前端程序员更好的接受和解析后端数据接口返回的数据。
  2. 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就可以了,因为所有接口都是这样返回的。
  3. 有利于项目统一数据的维护和修改。
  4. 有利于后端技术部门的统一规范的标准制定,不会出现稀奇古怪的返回内容。

统一数据格式的返回有两种实现方式,返回一个公共对象或者重写。

这里我们采用第二种重写(不过他的灵活性有待提升) 

具体的我们可以使用@ControllerAdvice+ResponseBodyAdvice的方式实现,

具体实现代码如下:

但是正如我们上面所说的,通过重写来进行统一数据格式的返回,他的灵活性的确有待提高。你看我们上面只是处理了成功的情况,但要是失败的情况呢?——》并且即使失败,也是分好几种情况呢!!!


我们不如再创建一个工具类,用来自定义返回hashmap数据(再通过@ResponseBody转成json格式)


那么对应的我们的统一数据格式返回类就发生了变化

package com.example.demo.common;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import java.util.HashMap;/*** 统一数据格式返回(灵活性有待提高)*  通过统一数据格式的返回,不管我们控制层的方法返回了什么类型的数据*  通过重写末尾都能把他转成hashmap格式的数据,然后又通过@ResponseBody注解,将java对象转成了json对象。*/
@ControllerAdvice
@ResponseBody
public class ResponseAdvice implements ResponseBodyAdvice {@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true; // 这个值为true的时候,才会对返回的数据进行重写}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 在有了我们自定义数据返回后,我们的这个统一数据格式返回类就像是一个托地的。// 因为controller层可以直接调用AjaxResult,来返回hashmap(通过@ResponseBody转成json)// 但如果controller没有调用AjaxResult,直接返回了if (body instanceof HashMap) {return body;  // 此时已经是hashmap格式了}if (body instanceof Integer){ // 当controller层中的方法直接返回int类型时候int num = (int) body;if (num <= 0) {// 应对int类型错误返回(查询文章列表,新增和删除博客可能会用到)——》也可能用不到,如果新增或查询失败,我直接就在controller就返回了(通过调用AjaxResult)// 新增、删除或查询失败(非得在controller返回int值,再通过这里返回json对象的话,不灵活(出错信息显示的不具体// 所以说这里我们只是以防万一,我们还是选择再controller层直接返回json对象,这样更信息具体,更有怎针对性)return AjaxResult.fail("抱歉,本次操作失败,请稍后再试!"); // 这里无法区分是新增失败还是删除失败// 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式}}if (body == null) { // (比如查询操作,直接返回查询到的UserInfo对象,然后直接返回该对象)// 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式return AjaxResult.fail("抱歉,查询失败!"); // 这时对查询当前用户的特判}// 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式return AjaxResult.success("操作成功", body);// 前端是通过result中的status值来判断操作是否成功的,这个类用来处理操作成功的情况(为操作成功的情况兜底)// 但这可能存在问题,如果操作失败,并且在controller层没有调用AjaxResult中的fail方法(而是直接返回,通过这个类来返回统一的数据格式,就会出现问题——》在这个类我们都是按成功的处理的)// 解决方案,在该类中提前判断body(判断操作失败的情况)--->我们约定如果操作失败就返回负数(在controller层调用AjaxResult的情况)}
}

那么与之对应的我们在controller层的类也就发生了改变

package com.example.demo.controller;import com.example.demo.common.AjaxResult;
import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.jws.soap.SOAPBinding;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
/*** 而Spring Boot框架项目接口返回 JSON格式的数据比较简单:* 在 Controller 类中使用@RestController注解即可返回 JSON格式的数据。*  @RestController是@Controller和@ResponseBody两者的结合,使用这个注解后该controller的所有方法都会返回json格式的数据,*  因为@ResponseBody的作用就是把返回的对象转换为json格式,并把json数据写入response的body中,前台收到response时就可以获取其body中的json数据了。* 如果在整个controller类上方添加@RestController,其作用就相当于把该controller下的所有方法都加上@ResponseBody,使每个方法直接返回response对象。*/
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredpublic UserService userService;@RequestMapping("/reg")public Object reg(String username, String password1, String password2) {HashMap<String, Object> result = new HashMap<>();if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password1) || !StringUtils.hasLength(password2)) {return AjaxResult.fail("你输入的参数有误,请重新输入!");}else {if (!password1.equals(password2)) {return AjaxResult.fail("前后密码不一致,请重新输入!");}else {UserInfo userInfo = new UserInfo();userInfo.setUsername(username);userInfo.setPassword(password1);int ret = userService.reg(userInfo);if (ret != 1) {return AjaxResult.fail("数据库添加用户失败,请稍后再试!");}else {return AjaxResult.success("恭喜,注册成功!", ret);}}}}@RequestMapping("/login")public Object login(String username, String password) {HashMap<String, Object> result = new HashMap<>();if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return AjaxResult.fail("你输入的参数有误,请重新输入!");}else {// 需要在数据库中查询当前登录的用户是否存在UserInfo userInfo = userService.selectByUsername(username);if (userInfo == null || !password.equals(userInfo.getPassword())) {return AjaxResult.fail("你当前的用户名或密码错误,请重新输入!");}else  {return AjaxResult.success("恭喜,登录成功!", "");}}}
}

4.2.3 统一用户登录验证

spring拦截器

对于以上问题Spring中提供了具体的实现拦截器:HandlerInterceptor,

拦截器的实现分为以下两个步骤:

1、创建自定义拦截器,实现 HandlerInterceptor 接口的perHandle(执行具体方法之前的预处理)方法。

2、将自定义拦截器加入 WebMvcConfiger的 addInterceptors方法中。


步骤1:创建用户登录拦截器


步骤2:

 将该自定义拦截器放到系统的配置文件中

(将自定义拦截器加入 WebMvcConfiger的 addInterceptors方法中)


检验:用浏览器测试一下是否真的拦截了


4.3 博客登录页的实现

4.3.1 实体类的创建

首先根据我们数据库的表,在我们的程序中创建想对应的实体类——我们的MyBatis是ORM框架,我们的程序对象与关系数据库数据之间有响应的映射关系


只有在启动类的同级目录下,springboot才会对该目录下的类进行扫描,配合类注解存到spring容器中。


4.3.2 前后端交互

1.首先用户在前端页面输入了用户信息


2.然后前端紧接着就把用户输入的信息传递给后端(提交到后端指定的接口上,比如登录提交的就是/user/reg 


3.后端接收到用户信息,存到数据库中,并返回注册的结果(成功了?还是失败了?) 


4.前端接受到后端返回的结果后做进一步的处理


4.3.3 后端代码实现

后端流程图:


下面是根据上图流程构建的目录

在controller层中调用了service服务层的reg方法
service服务层又调用了持久层中的mapper接口
mapper接口的实现:UserMapper.xml文件

注册前后端流程交互


4.4 博客列表页的实现

 controller层代码:

1.ArticleController代码实现:

package com.example.demo.controller;import com.example.demo.config.AjaxResult;
import com.example.demo.model.ArticleInfo;
import com.example.demo.model.UserInfo;
import com.example.demo.service.ArticleService;
import com.example.demo.service.UserService;
import com.example.demo.util.ConstVariable;import lombok.SneakyThrows;
import org.apache.ibatis.annotations.ResultMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.*;/*** 而Spring Boot框架项目接口返回 JSON格式的数据比较简单:* 在 Controller 类中使用@RestController注解即可返回 JSON格式的数据。*/
@RestController
@RequestMapping("/article")
public class ArticleController {@Autowiredprivate ArticleService articleService;// 属性注入// 新增操作(发表博客)@RequestMapping("/add")public Object add(HttpServletRequest request, String title, String content) {// todo 非空校验HttpSession session = request.getSession(false);if (session == null || session.getAttribute(ConstVariable.USER_SESSION_KEY) == null) {return AjaxResult.fail(-1, "当前用户未登录,博客发表失败!");}UserInfo userInfo = (UserInfo) session.getAttribute(ConstVariable.USER_SESSION_KEY);int uid = userInfo.getId(); // 标记当前博客是那个用户发布的int rcount = 1; // 表示当前博客的阅读量,初始值(新发表的博客阅读量)==1int state = 1; // state表示当前博客状态 1 ==>已发布, 2==》编辑中,保存草稿// 新增博客ArticleInfo articleInfo = new ArticleInfo();articleInfo.setTitle(title);articleInfo.setContent(content);articleInfo.setUid(uid);articleInfo.setRcount(rcount);articleInfo.setState(state);int result = articleService.add(articleInfo);if (result == 1) {return AjaxResult.success("博客发表成功!", 1);}else {return AjaxResult.fail(-1, "数据库插入失败,博客发表失败,请稍后再试!");}}// 查询指定用户id的所有博客@SneakyThrows@RequestMapping("/get_mylist")public Object getMyBlogList(Integer uid, HttpServletResponse response) {
//        if (uid == null) {
//            response.sendRedirect("/login.html"); // 当前用户未登录
//        }
//        int uid = Integer.parseInt(id);if (uid <= 0) {// response.sendRedirect("/lolgin.html"); // 当前用户未登录return AjaxResult.fail(401, "当前用户未登录");}return AjaxResult.success("查找成功",articleService.getMyBlogList(uid));}// 查看指定博客id的博客详情-->博客详情页,此id为blogId,形参名称要和前端传过来的参数一致@RequestMapping("/get_by_blog_id")public Object selectByBlogId(int id) {if (id <= 0) {return AjaxResult.fail(-1, "参数出错,无法找到指定博客的相关信息!");}ArticleInfo articleInfo = articleService.selectByBlogId(id);if (articleInfo == null) {return AjaxResult.fail(-1, "数据库查询出错");}return AjaxResult.success("查找博客详情成功", articleInfo);}// 获取总的博客列表_无分页功能@RequestMapping("/get_list")public List<ArticleInfo> getBlogList() {return articleService.getBlogList();}// 获取总的博客列表(实现分页功能)@RequestMapping("/get_list_by_page")public Object getListByPage(Integer pindex, Integer psize) {HashMap<String, Object> data = new HashMap<>();// 登录进去后,第一次初始化页面时候url = list.html,即pindex是空的,username等也是空的if (pindex == null || pindex < 1) {pindex = 1;}if (psize == null || psize <= 0) {psize = 2;}// 上面这些特殊情况的处理,很重要(就是应对url = list.html这种情况,如果没有特判,会出错(查询不到数据)// 我们前后端都对这些特殊情况做了处理——不多余(双重保障,你进入公司后前端可不是你写的,所以我们就要把我们负责的后端写的扎实(不管前端怎么传,我后端都能应对)int offset = (pindex - 1) * psize;List<ArticleInfo> list = articleService.getListByPage(psize, offset);// 得出用户名为username,地址为address,邮箱为email的所有用户个数// getPageCount函数————》得到总的博客条数int totalCount = articleService.getPageCount();data.put("list", list);data.put("count", totalCount);return AjaxResult.success("分页功能的实现", data);}// 删除指定博客id的博客@SneakyThrows@RequestMapping("/del")public Object del(HttpServletRequest request, int id, HttpServletResponse response) {ArticleInfo articleInfo = articleService.selectByBlogId(id);HttpSession session = request.getSession(false);if (session == null || session.getAttribute(ConstVariable.USER_SESSION_KEY) == null) {// response.sendRedirect("/login.html");return AjaxResult.fail(401, "当前用户未登录!");}// 这篇博客的作者(uid)和当前登录的用户id相同才有权限删除UserInfo userInfo = (UserInfo) session.getAttribute(ConstVariable.USER_SESSION_KEY);if (articleInfo.getUid() != userInfo.getId()) {
//            response.sendRedirect("/blog_list.html");return AjaxResult.fail(-1, "你不是该篇博客的作者,你无权限进行删除操作");}if (id <= 0) {return AjaxResult.fail(-1, "找不到要删除的博客,删除失败");}int result = articleService.del(id);if (result <= 0) {return AjaxResult.fail(-1, "数据库删除操作失败!");}// 获取该篇博客的作者idQueue<Integer> queue = new LinkedList<>();ArrayList<Integer> list = new ArrayList<>();ArrayList<ArrayList<Integer>> lists = new ArrayList<>();// response.sendRedirect("/blog_list.html");return AjaxResult.success("删除博客成功!", result);}/*** 修改指定id的博客内容或标题*/@RequestMapping("/update")public Object update(HttpServletRequest request, int id, String title, String content) {if (id <= 0) return AjaxResult.fail(-1, "你指定的博客id有误,稍后再试!");ArticleInfo articleInfo = articleService.selectByBlogId(id);HttpSession session = request.getSession(false);if (session == null || session.getAttribute(ConstVariable.USER_SESSION_KEY) == null) {// response.sendRedirect("/login.html");return AjaxResult.fail(401, "当前用户未登录!");}// 这篇博客的作者(uid)和当前登录的用户id相同才有权限删除UserInfo userInfo = (UserInfo) session.getAttribute(ConstVariable.USER_SESSION_KEY);if (articleInfo.getUid() != userInfo.getId()) {
//            response.sendRedirect("/blog_list.html");return AjaxResult.fail(-1, "你不是该篇博客的作者,你无权限进行修改操作");}else {int ret = articleService.update(id, title, content);if (ret <= 0) {return AjaxResult.fail(-1, "数据库更新操作失败!");}else {return AjaxResult.success("更新博客成功", ret);}}}}

2.UserController代码实现:

package com.example.demo.controller;import com.example.demo.config.AjaxResult;
import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import com.example.demo.util.ConstVariable;
import com.example.demo.util.PasswordUtil;import lombok.SneakyThrows;
import org.apache.ibatis.annotations.ResultMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;/*** 用户注册* @param username* @param password1* @param password2* @return*/@RequestMapping("/reg")public Object reg(String username, String password1, String password2) {if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password1) || !StringUtils.hasLength(password2)) {return AjaxResult.fail(-1, "参数输入错误");}else {if (!password1.equals(password2)) {return AjaxResult.fail(-1, "前后密码不一致");}else {int result = userService.reg(username, PasswordUtil.encrypt(password1));if (result != 1) {return AjaxResult.fail(-1, "数据库添加出错");}return AjaxResult.success("注册成功", "1");}}}/*** 用户登录* @param request* @param username* @param password* @return*/@RequestMapping("/login")public Object login(HttpServletRequest request, String username, String password) {if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return AjaxResult.fail(-1, "参数输入错误,登录失败");}UserInfo userInfo = userService.selectByUsername(username);// 随机加盐算法,未加密的密码用同样的加密算法和数据库中的加密算法进行比较if (userInfo == null || ! PasswordUtil.decrypt(password, userInfo.getPassword()) || userInfo.getId() <= 0) {return AjaxResult.fail(-1, "账号或密码错误,登录失败");}// 如果程序走到这里,说明登陆成功了// 每次登录成功都要对数据库中的密码进行重新生成,这样才符合随机加密// userService.passwordRefresh()HttpSession session = request.getSession(); // 获取session,如果没有就创建一个session.setAttribute(ConstVariable.USER_SESSION_KEY, userInfo);return AjaxResult.success("登录成功", 1);}/***   获取当前登录的用户名*/@RequestMapping("/get_user")public Object getUser(HttpServletRequest request) {HttpSession session = request.getSession(false);if (session == null) {return AjaxResult.fail(401, "当前用户未登录");}UserInfo userInfo = (UserInfo) session.getAttribute(ConstVariable.USER_SESSION_KEY);if (userInfo == null) {return AjaxResult.fail(401, "当前用户未登录");}return userInfo; // 这里我们可以直接用AjaxResult自定义统一格式返回,也可以传统的统一格式返回}/*** 退出登录* @param request* @param response*/@SneakyThrows@RequestMapping("/logout")public Object logout(HttpServletRequest request, HttpServletResponse response) {HttpSession session = request.getSession(false);if (session == null || session.getAttribute(ConstVariable.USER_SESSION_KEY) == null) {// response.sendRedirect("/login.html"); // 当前用户未登录return AjaxResult.fail(401, "当前用户未登录!");}session.setAttribute(ConstVariable.USER_SESSION_KEY, null);//response.sendRedirect("/login.html");return AjaxResult.success("当前状态正常,可以正常退出!", "true");}/*** 通过个人id(uid)查询个人详细信息* @param authorId* @return*/@RequestMapping("/get_detail_by_uid")public Object selectByUid(Integer authorId) {int uid = authorId;if (uid <= 0) {return AjaxResult.fail(-1, "uid《=0,查询出错!");}UserInfo userInfo = userService.selectByUid(uid);if (userInfo == null) {return AjaxResult.fail(-1, "在数据库中查询不到指定用户id的详细信息,请稍后再试!");}return AjaxResult.success("通过uid查询个人详细信息成功", userInfo);}
}

在controller层中调用了service服务层的reg方法


 service服务层又调用了持久层中的mapper接口


4.5 博客修改删除功能实现

mapper层代码:

1.Articlemapper:

package com.example.demo.mapper;import com.example.demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;@Mapper
public interface ArticleMapper {// 发布新文章public int add(@Param("articleinfo")ArticleInfo articleinfo);// 查询指定博客id的博客详情--》用于博客详情页public ArticleInfo selectByBlogId(@Param("blogId") int blogId);// 查询个人的博客列表——》个人博客详情页public List<ArticleInfo> getMyBlogList(@Param("uid") int uid);// 查询总的博客列表(无分页功能)public List<ArticleInfo> getBlogList();// 删除指定博客id的博客public int del(@Param("id") int id);// 得到当前的博客总数目public int getPageCount();// 查询总的博客列表(分页功能的实现)_>注意limit和offset的位置(相对位置(参数的相对位置,不要弄反——》查询数据库会出错public List<ArticleInfo> getListByPage(@Param("limit") int psize, @Param("offset") int offset);// psize对应limit--》表示每页显示的记录条数。pindex对应offset——》表示当前查看的是第几页的数据// 修改指定博客id的博客title和content----------->注意形参的顺序public int update(@Param("title") String title, @Param("content") String content, @Param("id") int id);}

2.Usermapper代码实现:

package com.example.demo.mapper;import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;@Mapper
public interface UserMapper {// 用户注册public int reg(@Param("username") String username, @Param("password") String password);// 通过名称查询用户-->用于登录的验证public UserInfo selectByUsername(@Param("username") String username);// 通过个人id(uid)查询个人详细信息public UserInfo selectByUid(@Param("uid") int uid);
}

1.AjaxResult代码:

用户登录拦截器,自定义拦截器添加到系统的配置文件中。

package com.example.demo.config;import org.springframework.stereotype.Controller;import java.util.HashMap;/*** 自定义返回数据(我们通过AjaxResult类返回的都是hashmap格式的对象* 之后还有通过@ResponseBody将java对象转成json格式的数据*/
public class AjaxResult {/*** 操作成功(无msg)* @param data* @return*/public static Object  success(Object data) {HashMap<String, Object> result = new HashMap<>();result.put("state", 200);result.put("msg", "");result.put("data", data);return result;}/*** 操作成功,有msg* @param msg* @param data* @return*/public static Object success(Object msg, Object data) {HashMap<String, Object> result = new HashMap<>();result.put("state", 200);result.put("msg", msg);result.put("data", data);return result;}/*** 操作失败(无data* @param state* @param msg* @return*/public static Object fail(int state, Object msg) {HashMap<String, Object> result = new HashMap<>();result.put("state", state);result.put("msg", msg);result.put("data", "");return result;}/*** 操作失败(有data)* @param state* @param msg* @param data* @return*/public static Object fail(int state, Object msg,  Object data) {HashMap<String, Object> result = new HashMap<>();result.put("state", state);result.put("msg", msg);result.put("data", data);return result;}
}

2.Logininterception代码:

用户登录拦截器(查看当前用户是否登录)

package com.example.demo.config;import com.example.demo.util.ConstVariable;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashSet;/*** 用户登录拦截器(查看当前用户是否登录)*/
@Component
public class LoginInterception implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession(false);if (session == null || session.getAttribute(ConstVariable.USER_SESSION_KEY) == null) {// 当前用户未登录,进行拦截--->跳转到登录页面--->后端我们只用给前端返回状态和数据,我们后端不用管页面怎么,跳转工作是前端的事情// response.sendRedirect("/login.html");// 401表示未登录,没有权限,403不是因为没有登录而没有权限,而是该用户没有权限,管理员和普通用户的区别response.setStatus(401);response.sendError(401, "当前用户为登录,没有权限访问该接口");return false;}return true;}
}

3.ResponseAdvice代码:

数据格式同样返回(这个是兜底的,即使controller层返回的是int或string类型的数据,也可以转换成json格式)

注意:只能兜底int类的返回(可以应对操作成功和失败两种情况)

如果是String或其他格式的返回(可以应对操作成功的情况,但无法应对操作失败的情况,要继续对body进行特判)

package com.example.demo.config;import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import java.beans.PropertyEditorSupport;
import java.util.HashMap;/*** 数据格式同样返回(这个是兜底的,即使controller层返回的是int或string类型的数据,也可以转换成json格式)* 注意:只能兜底int类的返回(可以应对操作成功和失败两种情况)* 如果是String或其他格式的返回(可以应对操作成功的情况,但无法应对操作失败的情况,要继续对body进行特判)** 这里我们只是以防万一,我们尽量还是选择再controller层直接返回json对象,这样更信息具体,更有怎针对性)*/
@ControllerAdvice // 统一功能处理需要加这个注解
@ResponseBody
// @ResponseBody的作用就是把返回的对象转换为json格式,并把json数据写入response的body中,前台收到response时就可以获取其body中的json数据了。
public class ResponseAdvice implements ResponseBodyAdvice {@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true; // 返回true才会继续执行下面的代码}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof HashMap) {return body;  // 此时已经是hashmap格式了}
//        HashMap<String, Object> result = new HashMap<>();
//        result.put("state", 200);
//        result.put("msg", "");
//        result.put("data", body);if (body instanceof Integer){int num = (int) body;if (num <= 0) {// 应对int类型错误返回(查询文章列表,新增和删除博客可能会用到)——》也可能用不到,如果新增或查询失败,我直接就在controller就返回了(通过调用AjaxResult)// 新增、删除或查询失败(非得在controller返回int值,再通过这里返回json对象的话,不灵活(出错信息显示的不具体// 所以说这里我们只是以防万一,我们还是选择再controller层直接返回json对象,这样更信息具体,更有怎针对性)return AjaxResult.fail(-1,"抱歉,本次操作失败,请稍后再试!"); // 这里无法区分是新增失败还是删除失败// 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式}}if (body == null) { // 应对String类型错误返回// 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式return AjaxResult.fail(-1,"抱歉,查询失败!"); // 这时对查询当前用户的特判}if (body instanceof String) { // 以String类型正确返回ObjectMapper objectMapper = new ObjectMapper();return objectMapper.writeValueAsString(body); // 用jackson中的工具类返回(没弄明白)——》直接返回String吧(不序列化了,详见https://zhuanlan.zhihu.com/p/196372502)}// 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式return AjaxResult.success(body);// 前端是通过result中的state值来判断操作是否成功的,这个类用来处理操作成功的情况(为操作成功的情况兜底)// 但这可能存在问题,如果操作失败,并且在controller层没有调用AjaxResult中的fail方法(而是直接返回,通过这个类来返回统一的数据格式,就会出现问题——》在这个类我们都是按成功的处理的)// 解决方案,在该类中提前判断body(判断操作失败的情况)--->我们约定如果操作失败就返回负数(在controller层调用AjaxResult的情况)}
}

4.6 博客分页功能实现

1.ExcptionAdvice代码:

统一异常处理

@ResponseBody的作用就是把返回的对象转换为json格式,并把json数据写入response的body中,前台收到response时就可以获取其body中的json数据了

package com.example.demo.config;import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;/*** 统一异常处理*/
@ControllerAdvice
@ResponseBody
// @ResponseBody的作用就是把返回的对象转换为json格式,并把json数据写入response的body中,前台收到response时就可以获取其body中的json数据了。
public class ExceptionAdvice {@ExceptionHandler(Exception.class)public Object exceptionAdvice(Exception e) {// 这里我们本来返回的是一个hashmap格式的对象,但加了@ResponseBody,把我们的java对象转成的了json格式return AjaxResult.fail(-1, e.getMessage());}// 你这里的统一异常处理,并不完整}

2.ApplicationConfig代码:

package com.example.demo.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;// 系统配置文件类
@Configuration // 该配置文件在项目启动时配置
public class ApplicationConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterception loginInterception; // 用户登录拦截器// 自定义拦截器添加到系统的配置文件中@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterception).addPathPatterns("/**")       // 设置拦截规则
//                .excludePathPatterns("/login.html")// 下面这些指定的url,不会检查是否登录——进行拦截
//                .excludePathPatterns("/reg.html").excludePathPatterns("/**/*.html").excludePathPatterns("/user/login").excludePathPatterns("/user/reg")// 总的博客的列表,和博客详情,我们需要开放权限,即使用户未登录也可以查看.excludePathPatterns("/article/get_list_by_page") // 放行总博客列表.excludePathPatterns("/article/get_by_blog_id") // 放行博客详情页面.excludePathPatterns("/user/get_detail_by_uid")//.excludePathPatterns("/css/**")--->和下一行的作用是一样的//.excludePathPatterns("/**/*.css").excludePathPatterns("/css/**").excludePathPatterns("/js/**").excludePathPatterns("/editor.md/**").excludePathPatterns("/img/**"); // 直接img目录下的所有都放行}
}

总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/277059.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Transformer代码从零解读【Pytorch官方版本】

文章目录 1、Transformer大致有3大应用2、Transformer的整体结构图3、如何处理batch-size句子长度不一致问题4、MultiHeadAttention&#xff08;多头注意力机制&#xff09;5、前馈神经网络6、Encoder中的输入masked7、完整代码补充知识&#xff1a; 1、Transformer大致有3大应…

AcWing 848. 有向图的拓扑序列

#include<iostream> #include<cmath> #include<queue> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; const int N1e510; int n,m,a,b; int e[N],ne[N],h[N],idx; int d[N],top[N],cnt1;//top是拓扑排序…

香港理工大学主办!2024年第八届电力能源系统与应用国际会议(ICoPESA 2024)即将召开!

2024年第八届电力能源系统与应用国际会议&#xff08;ICoPESA 2024&#xff09; 2024年6月24日-26日 中国香港 ICoPESA 2024-Hong Kong (icpesa.org)https://icpesa.org/index.html 会议组织单位 会议出版及检索&#xff1a; 会议录用并注册的论文将由IEEE出版&#xff0c;…

SQL Server错误:15404

执行维护计划失败&#xff0c;提示SQL Server Error 15404 无法获取有关... 异常如下图&#xff1a; 原因&#xff1a;数据库用户名与计算机名称不一致 解决办法&#xff1a;1.重名称数据库用户名 将前缀改成计算机名 2.重启SQL Server代理

Mybatis sql 控制台格式化

package com.mysql; import org.apache.commons.lang.StringUtils; import org.apache.ibatis.logging.Log;import java.util.*;/*** Description: sql 格式化* Author: DingQiMing* Date: 2023-07-17* Version: V1.0*/ public class StdOutImpl implements Log {private stati…

【消息队列开发】 测试MessageFileManager(对硬盘中的消息操作)类

文章目录 &#x1f343;前言&#x1f384;测试流程&#x1f334;准备工作&#x1f332;测试创建队列功能&#x1f333;测试统计文件的读写&#x1f38b;测试将相应消息放入文件中&#x1f38d;测试读文件里的消息到内存&#x1f340;测试删除消息&#x1f60e;测试垃圾回收⭕总…

【设计模式】一、设计模式概述

文章目录 一、设计模式概述&#xff08;一&#xff09;设计模式是什么1. 设计模式的定义2. 设计模式的组成要素3、常用设计模式一览表 &#xff08;二&#xff09;设计模式的优点&#xff08;用途&#xff09;※ 本文小结 一、设计模式概述 &#xff08;一&#xff09;设计模式…

复杂网络——半局部中心法

一、概述 由于最近写论文需要使用复杂网络知识中的半局部中心法&#xff0c;但是截止目前来说&#xff0c;网上几乎搜索不到有关的MATLAB程序代码&#xff0c;只有一篇用Python编写的程序&#xff0c;我的电脑中没有python&#xff0c;所以我花费一些时间&#xff0c;利用matla…

基于SpringBoot+Vue交流和分享平台的设计与实现(源码+部署说明+演示视频+源码介绍)

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通…

Day44-sersync企业实时复制实战

Day44-sersync企业实时复制实战 1. sersync实时复制工具介绍1.1 sersync工具简介1.2 sersync特点1.3 sersync图解原理1.4 sersyncrsync实时复制方案项目实践1.4.1 图解项目方案架构及实现原理1.4.2 确保远程数据传输服务部署完成1.4.3 检查当前系统nfs01是否支持inotify实时监控…

008:安装Docker

安装Docker 如果不太熟悉Linux命令&#xff0c;不想学习Linux命令&#xff0c;可以直接看文末NAS面板章节&#xff0c;通过面板&#xff0c;像使用Window一样操作NAS。 一、安装 Docker 1.安装 Docker wget -qO- https://get.docker.com/ | sh2.启动 Docker 服务 sudo sys…

qt vs 编程 字符编码 程序从源码到编译到显示过程中存在的字符编码及隐藏的字符编码转换

理解字符编码&#xff0c;请参考&#xff1a;unicode ucs2 utf16 utf8 ansi GBK GB2312 CSDN博客 了解windows字符显示必须了解locale概念 参考&#xff1a;揭密 Windows 上的各种 locale - 知乎 汉字&#xff08;或者说多字节字符&#xff09;的存放需求&#xff0c;是计算…

K8S CNI

OCI概念 OCI&#xff0c;Open Container Initiative&#xff0c;开放容器标准&#xff0c;是一个轻量级&#xff0c;开放的治理结构&#xff08;项目&#xff09;&#xff0c;在 Linux 基金会的支持下成立&#xff0c;致力于围绕容器格式和运行时创建开放的行业标准。 OCI 项目…

AI智慧校园电子班牌云平台源码

目录 家长端 学校端 电子围栏 亲情通话 课堂答题 移动化管理模式 统一资源管理平台 模板内容智能更换 家校互联 家长端 多场景通话:上学放学联系、紧急遇险求助联系、日常亲情通话关注孩子人身安全:到校离校情况、进入危险区域预警等。 学校端 课堂秩序管理:提高教…

深入浅出:Objective-C中使用MWFeedParser下载豆瓣RSS

摘要 本文旨在介绍如何在Objective-C中使用MWFeedParser库下载豆瓣RSS内容&#xff0c;同时展示如何通过爬虫代理IP技术和多线程提高爬虫的效率和安全性。 背景 随着信息量的激增&#xff0c;爬虫技术成为了获取和处理大量网络数据的重要手段。Objective-C作为一种成熟的编程…

3D地图在BI大屏中的应用实践

前言 随着商业智能的不断发展&#xff0c;数据可视化已成为一项重要工具&#xff0c;有助于用户更好地理解数据和分析结果。其中&#xff0c;3D地图作为一种可视化工具&#xff0c;已经在BI大屏中得到了广泛地应用。 3D地图通过将地理信息与数据相结合&#xff0c;以更加直观…

工具-百度云盘服务-身份认证

目标 通过百度网盘API的方式去获取网盘中的文件&#xff0c;要实现这的第一步就是需要获取网盘的权限。资料(参考) 如果期望应用访问用户的网盘文件&#xff0c;则需要经过用户同意&#xff0c;这个流程被称为“授权”。百度网盘开放平台基于 OAuth2.0 接入授权。OAuth2.0 是…

嵌入式学习第二十七天!(TCP并发模型)

TCP并发模型&#xff1a; 1. TCP多线程模型&#xff1a; 缺点&#xff1a;创建线程会带来资源开销&#xff0c;能够实现的并发量比较有限。 2. IO模型&#xff1a; 1. 阻塞IO&#xff1a; 没有数据到来时&#xff0c;可以让任务挂起&#xff0c;节省CPU资源开销&#xff0c;提…

物理隔离条件下,如何安全高效地进行内外网文件导入导出?

内外网文件导入导出通常指的是在内部网络&#xff08;内网&#xff09;和外部网络&#xff08;外网&#xff09;之间传输文件的过程。这在企业环境中尤其常见&#xff0c;因为内部网络通常包含敏感数据&#xff0c;而外部网络&#xff08;如互联网&#xff09;则允许更广泛的访…

Apache zookeeper kafka 开启SASL安全认证

背景&#xff1a;我之前安装的kafka没有开启安全鉴权&#xff0c;在没有任何凭证的情况下都可以访问kafka。搜了一圈资料&#xff0c;发现有关于sasl、acl相关的&#xff0c;准备试试。 简介 Kafka是一个高吞吐量、分布式的发布-订阅消息系统。Kafka核心模块使用Scala语言开发…