快速创建一个SpringBoot + Vue的前后端分离项目

首先,本项目是模仿的B站的Up主MarkHub的博客系统,视频中详细得对每一个文件的作用都做了解释。在本篇文章中不会进行过多赘述,只是专注于项目的搭建。
视频链接请戳下方
https://www.bilibili.com/video/BV1PQ4y1P7hZ?from=search&seid=1550841645384365421
前端搭建传送门
项目演示效果:www.markerhub.com:8084/blogs

后端SpringBoot服务器搭建

后端使用的技术栈有:
技术栈:

  • SpringBoot
  • mybatis plus
  • shiro
  • lombok
  • redis
  • hibernate validatior
  • jwt

首先使用IDEA初始化一个SpringBoot项目,选择的依赖有如下
图片描述
DevTools为开发热部署,修改代码时,按Ctrl + F9可重启服务器。
Lombok用于简化代码,使用注解代替get、set、构造方法,让代码更优雅。
在这里插入图片描述
在这里插入图片描述
这两个依赖也要选上,数据库驱动使用的是MySQL。

整合mybatis plus

<!--mp-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.2.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<!--mp代码生成器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.2.0</version>
</dependency>

将以上代码放入pom.xml文件中以整合mybatis

接下来创建两张表m_blog和m_user,m_blog用于存放博客信息,m_user用于存放用户信息

CREATE TABLE m_user ( id bigint(20) NOT NULL AUTO_INCREMENT,
username varchar(64) DEFAULT NULL, avatar varchar(255) DEFAULT
NULL, email varchar(64) DEFAULT NULL, password varchar(64)
DEFAULT NULL, status int(5) NOT NULL, created datetime DEFAULT
NULL, last_login datetime DEFAULT NULL, PRIMARY KEY (id),
KEY UK_USERNAME (username) USING BTREE ) ENGINE=InnoDB DEFAULT
CHARSET=utf8; CREATE TABLE m_blog ( id bigint(20) NOT NULL
AUTO_INCREMENT, user_id bigint(20) NOT NULL, title
varchar(255) NOT NULL, description varchar(255) NOT NULL,
content longtext, created datetime NOT NULL ON UPDATE
CURRENT_TIMESTAMP, status tinyint(4) DEFAULT NULL, PRIMARY KEY
(id) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4;
INSERT INTO vueblog.m_user (id, username, avatar, email,
password, status, created, last_login) VALUES (‘1’,
‘markerhub’,
‘https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg’,
NULL, ‘96e79218965eb72c92a549dd5a330112’, ‘0’, ‘2020-04-20 10:44:01’,
NULL);

这边数据库和表创建好了以后,在application.yml文件中添加配置信息

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: root
mybatis-plus:
  mapper-locations: classpath*:/mapper/**Mapper.xml

现在可以测试启动一下数据库连接是否正常,如果正常下一步就可以使用CodeGenerator生成代码。该类文件可在 https://github.com/Alenjoke/vue-master 下载
运行该文件之前,需要配置数据库相关的连接。

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(null);
        pc.setParent("com.project.blog.markerhub");

配置完成后,运行该文件,并在控制台输入表名m_user, m_blog

请输入表名,多个英文逗号分割:
m_user, m_blog

运行后生成以下文件
在这里插入图片描述
可以看到开发业务的Controller层,Dao层,Service层都生成好了,非常方便。

生成了相关文件以后,还要配置一个包扫描器,否则项目会启动失败,这里我在com.project.blog.config下增加一个类:

@Configuration
@EnableTransactionManagement
@MapperScan("com.project.blog.mapper")
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        return paginationInterceptor;
    }


}

好了,目前为止,各层的访问已经能够形成一个闭环,可以开始业务逻辑的开发。
对于前后端分离项目,数据的封装是必要的,因为除了数据,前端还需要了解数据的状态,提示信息等。

在com.project.blog.common.lang包中生成一个Result类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result implements Serializable {
    private int code;
    private String msg;
    private Object data;

    public static Result status(int code, String msg, Object data) {
        return new Result(code, msg, data);
    }

    public static Result success(Object obj) {

        return new Result(200, "操作成功", obj);
    }

    public static Result fail(Object obj) {

        return new Result(400, "操作失败", obj);
    }

}

这里我做了一个简单的封装。

整合shiro+jwt

shiro这块的官方文档如下
https://github.com/alexxiyang/shiro-redis/blob/master/docs/README.md#spring-boot-starter
shiro + jwt的主要作用是在请求链接到达controller层以前进行一个过滤,判断用户是否带有token,对请求进行一个角色以及权限认证。shiro这块深入了解的话也需要花费很长时间,本篇文章专注于框架的搭建,对于shiro如何对用户权限的认证就不做过多赘述。

首先添加相关依赖

<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis-spring-boot-starter</artifactId>
    <version>3.2.1</version>
</dependency>
<!-- hutool工具类-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.3</version>
</dependency>
<!-- jwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

添加以下文件,可在https://github.com/Alenjoke/vue-master 下载
com.project.blog.config.ShiroConfig;
com.project.blog.shiro.AccountProfile;
com.project.blog.shiro.AccountRealm;
com.project.blog.shiro.JwtFilter;
com.project.blog.shiro.JwtToken;

com.markerhub.util.JwtUtils;
com.markerhub.util.ShiroUtil;

JwtUtils用了配置文件注解,需要添加依赖

        <!-- 配置注解器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

application.yml增加以下配置

shiro-redis:
  enabled: true
  redis-manager:
    host: 127.0.0.1:6379
markerhub:
  jwt:
    # 加密秘钥
    secret: f4e2e52034348f86b67cde581c0f9eb5
    # token有效时长,7天,单位秒
    expire: 604800
    header: token

除此以外
新增文件resources/META-INF/spring-devtools.properties,增加以下配置,否则devtool重新启动时会报错

restart.include.shiro-redis=/shiro-[\\w-\\.]+jar

异常处理

shiro配置好了以后,进行异常的配置处理,创建一个异常捕获类,用于捕获后台出现的所有异常,前端每次发起请求时,如果出现了异常,返回对应的异常信息进行处理,这样浏览器不会出现500的页面错误请求。

创建类 com.markerhub.common.lang.Exception.GlobalExceptionHandler
类名上增加ControllerAdvice注解表示全局处理异常
这里我使用了@RestControllerAdvice它整合了ControllerAdvice和RestController注解

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 捕获异常
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(value = RuntimeException.class)
    public Result handler(RuntimeException e) {
        log.error("运行时异常============");
        return Result.fail(e.getMessage());
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = ShiroException.class)
    public Result handler(ShiroException e) {
        log.error("运行时异常============");
        return Result.status(401, e.getMessage(), null);
    }

当我们的程序抛出一个异常时,就会被这个类捕获,根据ExceptionHandler注解中传入的异常类进行匹配,进入到对应的handler中处理。

实体校验

在实体类上增加以下注解

    @NotBlank(message = "昵称不能为空")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

然后在 Controller的参数中添加Valid注解即可验证

    @PostMapping("/save")
    public Result save(@Validated @RequestBody User user) {

        return Result.success(user);
    }

验证失败时,会抛出异常,转到全局异常捕获类中,从而将错误信息传给前端。

注意,我之前使用的spring-boot-starter-parent为2.4.4版本,找不到验证的包,后换成2.2.6.RELEASE版本

跨域处理

前后端分离项目必然面对的一个问题,处理代码如下

/**
 * 解决跨域问题
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

除此以外,jwt的拦截器也需要配置跨域

    /**
     * 拦截器的跨域处理
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(org.springframework.http.HttpStatus.OK.value());
            return false;
        }

        return super.preHandle(request, response);
    }

如此,前后端分离的后端就完成了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值