项目初始化
一、背景
学一点新东西
二、技术选型
SpringBoot + MybatisPlus+mysql +Vue
说明:为了简单点
后端:SpringBoot + MybatisPlus
前端:开源的模板 vue-admin-template
三、项目构建
1、后端
1.1 新建项目
初始化后的最终目录如下
1.2 配置pom.xml
需要启动项+mysql+mvc+lombak
<?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.5.3-SNAPSHOT</version>-->
<version>2.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.leiyuee</groupId>
<artifactId>manage</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>manage</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--MVC 支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--数据库支持-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--数据库连接-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--对mybatisplus的自动化配置-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.0</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>
</build>
</project>
1.3 配置 yml 文件
################### 项目启动端口 ###################
server:
port: 8888
max-http-header-size: 10000000
################### 通用mapper设置 ###################
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
auto-mapping-behavior: full
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:mapper/**/*Mapper.xml
global-config:
# 逻辑删除配置
db-config:
# 删除前
logic-not-delete-value: 1
# 删除后
logic-delete-value: 0
################### spring配置 ###################
spring:
main:
allow-bean-definition-overriding: true
datasource:
url: jdbc:mysql://localhost:3306/manage?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
1.4 配置启动项
@SpringBootApplication
@MapperScan("com.leiyuee.manage.web.mapper")
public class ManageApplication {
public static void main(String[] args) {
SpringApplication.run(ManageApplication.class, args);
}
}
1.5 编写测试
@RestController
public class LoginController {
@GetMapping("login/test")
public String LoginTest(){
return "登录";
}
}
1.6 启动
2、 前端
#克隆项目
git clone https://github.com/PanJiaChen/vue-admin-template.git
#进入项目目录
cd vue-admin-template
#安装依赖
npm install
#开发
npm run dev
# 自动打开
http://localhost:9528
四、统一配置
1、统一结果返回
目前的前后端开发大部分数据的传输格式都是json,因此定义一个统一规范的数据格式有利于前后端的交互与UI的展示
1.1 状态码
/**
* 返回状态码
*/
public interface HttpStatus{
/**
* 操作成功
*/
public static final int SUCCESS = 200;
/**
* 对象创建成功
*/
public static final int CREATED = 201;
/**
* 请求已经被接受
*/
public static final int ACCEPTED = 202;
/**
* 操作已经执行成功,但是没有返回数据
*/
public static final int NO_CONTENT = 204;
/**
* 资源已被移除
*/
public static final int MOVED_PERM = 301;
/**
* 重定向
*/
public static final int SEE_OTHER = 303;
/**
* 资源没有被修改
*/
public static final int NOT_MODIFIED = 304;
/**
* 参数列表错误(缺少,格式不匹配)
*/
public static final int BAD_REQUEST = 400;
/**
* 未授权
*/
public static final int UNAUTHORIZED = 401;
/**
* 访问受限,授权过期
*/
public static final int FORBIDDEN = 403;
/**
* 资源,服务未找到
*/
public static final int NOT_FOUND = 404;
/**
* 不允许的http方法
*/
public static final int BAD_METHOD = 405;
/**
* 资源冲突,或者资源被锁
*/
public static final int CONFLICT = 409;
/**
* 不支持的数据,媒体类型
*/
public static final int UNSUPPORTED_TYPE = 415;
/**
* 系统内部错误
*/
public static final int ERROR = 500;
/**
* 接口未实现
*/
public static final int NOT_IMPLEMENTED = 501;
}
1.2 结果集
/**
* 返回数据
*
*/
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public R() {
put("code", 0);
put("msg", "success");
}
public static R error() {
return error(HttpStatus.ERROR, "未知异常,请联系管理员");
}
public static R error(String msg) {
return error(HttpStatus.ERROR, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok() {
return new R();
}
public R put(String key, Object value) {
super.put(key, value);
return this;
}
public Integer getCode() {
return (Integer) this.get("code");
}
public static R error(int code, Object msg,Object data) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
r.put("data",data);
return r;
}
public static R ok(int code, Object msg,Object data) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
r.put("data",data);
return r;
}
}
1.3 控制层返回
@RequestMapping("/getAll")
public R getAll(){
sysUserService.list();
return R.ok().put("userList", sysUserService.list());
}
@RequestMapping("/getOne")
public R getOne(){
SysUser sysUser = sysUserService.getOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getPassword,"123456"));
return R.ok().put("sysUser", sysUser);
}
2、统一异常处理
使用统一返回结果时,还有一种情况,就是程序的保存是由于运行时异常导致的结果,有些异常我们可以无法提前预知,不能正常走到我们return的R对象返回。
2.1 自定义异常
/**
* 自定义异常
*/
public class MyException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String msg;
private int code = 500;
public MyException(String msg) {
super(msg);
this.msg = msg;
}
public MyException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public MyException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public MyException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
2.2 异常处理
/*
异常处理器
*/
@RestControllerAdvice
public class MyExceptionhandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 处理自定义异常
*/
@ExceptionHandler(MyException.class)
public R handleRRException(MyException e){
R r = new R();
r.put("code", e.getCode());
r.put("msg", e.getMessage());
return r;
}
@ExceptionHandler(NoHandlerFoundException.class)
public R handlerNoFoundException(Exception e) {
logger.error(e.getMessage(), e);
return R.error(404, "路径不存在,请检查路径是否正确");
}
@ExceptionHandler(DuplicateKeyException.class)
public R handleDuplicateKeyException(DuplicateKeyException e){
logger.error(e.getMessage(), e);
return R.error("数据库中已存在该记录");
}
/* @ExceptionHandler(AuthorizationException.class)
public R handleAuthorizationException(AuthorizationException e){
logger.error(e.getMessage(), e);
return R.error("没有权限,请联系管理员授权");
}*/
@ExceptionHandler(Exception.class)
public R handleException(Exception e){
logger.error(e.getMessage(), e);
return R.error();
}
}
3、统一日志
日志是追踪错误定位问题的关键,尤其在生产环境中,需要及时修复热部署,不会提供开发者debug的环境,此时日志将会是最快解决问题的关键
3.1 yml添加
logging:
file:
path: src\main\resources\logger\ # logger文件夹需要提前生成
五、跨域处理
因为浏览器的同源策略会阻止一个域的js脚本和另外一个域的内容进行交互,两个页面需要有相同的协议,主机,和端口,三者之间任何一个不同即为跨域
常用CROS跨域资源分享来解决
1、普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
2、带cookie跨域请求:前后端都需要进行设置
1、后端
1.1 跨域设置
/**
* 设置跨域:这里有5种跨域方法
*/
//实现 WebMvcConfigurer
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**") //拦截所有请求
//放入哪些原始域
// .allowedOrigins("*")//注意:springboot 升级后这里要改成下面这个
.allowedOriginPatterns("*")
//是否发送Cookie信息
.allowCredentials(true)
.allowedMethods("GET","POST","PUT","DELETE")
.allowedHeaders("*");
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
//.exposedHeaders(HttpHeaders.SET_COOKIE)
}
}
/*
继承 HandlerInterceptorAdapter
@Component
public class CrossInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
return true;
}
}
* */
/*
使用Filter过滤器来过滤服务请求,向请求端设置Response Header(响应头部)的Access-Control-Allow-Origin属性声明允许跨域访问。
@WebFilter
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(req, res);
}
}*/
/*
* 使用@CrossOrgin 注解
如果只是想部分接口跨域,且不想使用配置来管理的话,可以使用这种方式
在controller 或者在方法前
* */
/*Spring Cloud Gateway 跨域配置
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
# 允许跨域的源(网站域名/ip),设置*为全部
# 允许跨域请求里的head字段,设置*为全部
# 允许跨域的method, 默认为GET和OPTIONS,设置*为全部
allow-credentials: true
allowed-origins:
- "http://xb.abc.com"
- "http://sf.xx.com"
allowed-headers: "*"
allowed-methods:
- OPTIONS
- GET
- POST
- DELETE
- PUT
- PATCH
max-age: 3600
* */
1.2 登录
项目初始化暂时没有涉及到数据库
@RestController
public class LoginController {
// @CrossOrigin
@PostMapping("vue-admin-template/user/login")
@ResponseBody
public R Login(SysUser user){
HashMap<String ,Object> responseData = new HashMap<>();
responseData.put("token",1);
return R.ok(20000,"登录成功",responseData);
}
// @CrossOrigin
@GetMapping("vue-admin-template/user/info")
@ResponseBody
public R info(SysUser user){
HashMap<String ,Object> responseData = new HashMap<>();
responseData.put("roles","admin");
responseData.put("name","Super admin");
responseData.put("avator","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
return R.ok(20000,"登录成功",responseData);
}
}
2、前端
2.1 修改vue.config.js
publicPath: '/',
outputDir: 'dist',
assetsDir: 'static',
lintOnSave: process.env.NODE_ENV === 'development',
productionSourceMap: false,
devServer: {
port: port,
open: true,
overlay: {
warnings: false,
errors: true
},
// before: require('./mock/mock-server.js')
2.2 修改 env.development
# just a flag
ENV = 'development'
# base api
# VUE_APP_BASE_API = '/dev-api'
VUE_APP_BASE_API= 'http://localhost:8888'
port=8880
2.3 修改main.js
// if (process.env.NODE_ENV === 'production') {
// const { mockXHR } = require('../mock')
// mockXHR()
// }
3、项目启动后
前端:控制台输入npm run dev
后端:启动项
页面显示
直接登录,数据是后台接口提供的,初始化完成