初试【Vue+Springboot+SpringMVC+Druid+Mybatis+Caching+Redis】实现完整版前后端分离项目

最近喉咙不适,没法说话,只能做个安静的美男子了,上周住在医院病床上还想着自己可以从0开始完善整套WEB项目,出院后兴致勃勃地做起来了,调试了这两三晚,前端太难弄了

下面就是用到的一些常规框架:

前端框架:Vue
后端工程:Springboot
后端WEB框架:SpringMVC
连接池:Druid
持久层:Mybatis
缓存抽象:Caching
缓存类型:Redis

下面就是整个页面结果

NewImage

由于用了Mybatis持久框架,不像Hibernate可以在工程启动初始化的时候创建表,因此需要一开始手动创建一下数据表

CREATE TABLE user_info (
	id BIGINT ( 20 ) NOT NULL AUTO_INCREMENT,
	NAME VARCHAR ( 20 ),
	phone VARCHAR ( 30 ),
	age BIGINT ( 20 ),
	email VARCHAR ( 30 ),
	createTime datetime,
	updateTime datetime,
PRIMARY KEY ( id ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8;

假如不想手动建表,也可以通过第三方工具来完成,具体参考链接:https://github.com/YoWenhow/mybatis-enhance-actable

 

下面就是每个模块的内容

1、后端Springboot工程

直接列出pom依赖,包括springboot,springmvc,spring data redis,druid,mybatis等

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.0.0</version>
<scope>test</scope>
</dependency>

2、model层,对应ORM实体,其中createTime和updateTime不可编辑,不可输入,只是在mvc里写入持久层的时候打一个时间戳,最终web上只是显示出来,再者新增序列化是因为后面要进行redis缓存

package com.lihuia.web.model;

import java.io.Serializable;
import java.util.Date;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
* Copyright (C), 2018-2019
* FileName: User
* Author: lihui
* Date: 2019/12/9
*/

@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {

private static final long serialVersionUID = 2182129585108022309L;

private String id;

private Date createTime;

private Date updateTime;

private String name;

private String phone;

private Integer age;

private String email;
}

这里加了两个构造方法的注解@NoArgsConstructor和@AllArgsConstructor,不可丢弃,具体classes文件如下

public User() {
}

public User(final String id, final Date createTime, final Date updateTime, final String name, final String phone, final Integer age, final String email) {
this.id = id;
this.createTime = createTime;
this.updateTime = updateTime;
this.name = name;
this.phone = phone;
this.age = age;
this.email = email;
}

3、持久化层基于Mybatis框架,同时由于web查询较频繁,新增缓存,基于Spring的Caching缓存抽象在redis里,Mapper接口如下

package com.lihuia.web.mapper;

import java.util.List;

import com.lihuia.web.model.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import org.springframework.cache.annotation.Cacheable;

/**
* Copyright (C), 2018-2019
* FileName: UserMapper
* Author: lihui
* Date: 2019/12/9
*/

@Mapper
public interface UserMapper {

@Insert("insert into user_info (name, phone, age, email, createTime) " +
"values (#{name}, #{phone}, #{age}, #{email}, #{createTime})")
@Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
int insert(User user);

@Update("update user_info set name = #{name}, phone = #{phone}, age = #{age}," +
" email = #{email}, updateTime = #{updateTime} where id = #{id}")
int update(User user);

@Delete("delete from user_info where id = #{id}")
int delete(@Param("id") String id);

@Select("select * from user_info")
@Cacheable(value = "user-system")
List<User> getAll();

@Select("select * from user_info where id = #{id}")
@Cacheable(value = "user-system", key = "#a0")
User getById(@Param("id") String id);
}

4、缓存层除了上面新增Cache描述外,还需要新增注入配置,因为@EnableCaching要用到CacheManager来操作redis缓存

package com.lihuia.web.config;

import java.time.Duration;

import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;

/**
* Copyright (C), 2018-2019
* FileName: RedisConfig
* Author: lihui
* Date: 2019/12/9
*/

@Configuration
public class RedisCacheConfig {

@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration =
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(300));
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration)
.build();
}
}

5、持久化连接池基于Druid,springboot工程新增了druid-spring-boot-starter依赖,添加配置就可以直接使用了

spring.datasource.druid.url=jdbc:mysql://localhost:3306/user_center
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver

spring.cache.type=redis
spring.cache.cache-names=user
spring.cache.redis.time-to-live=5000
spring.cache.redis.cache-null-values=false

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=

6、Springboot的main方法新增mybatis的@MapperScan注解扫描mapper接口,以及开启缓存抽象

package com.lihuia.web;

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@MapperScan("com.lihuia.web.mapper")
@EnableCaching
public class WebApplication {

public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}

}

7、到这里持久层操作数据库基本就完成了,可以新建一个测试类校验一下Mapper接口正确性

package com.lihuia.web.mapper;

import javax.annotation.Resource;

import com.lihuia.web.model.User;
import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;

/**
* Copyright (C), 2018-2019
* FileName: UserMapperTest
* Author: lihui
* Date: 2019/12/9
*/

@SpringBootTest
@Slf4j
public class UserMapperTest extends AbstractTestNGSpringContextTests {

@Resource
private UserMapper userMapper;

@Test(description = "查询所有Users列表")
public void testGetAllUsers() {
userMapper.getAll().forEach(e -> log.info("{}", e.toString()));
}

@Test(description = "查询lucy用户信息")
public void testGetUserById() {
log.info("{}", userMapper.getById("1"));
}

@Test(description = "插入用户信息")
public void testInsertUser() {
userMapper.insert(User.builder().name("lihui").phone("18888888888").age(18).build());
userMapper.insert(User.builder().name("lilei").phone("19999999999").age(19).build());
userMapper.insert(User.builder().name("lucy").phone("17777777777").age(17).build());
}

@Test(description = "删除用户信息")
public void testDeleteUser() {
userMapper.delete("3");
}

@Test(description = "更新用户信息")
public void testUpdateUser() {
userMapper.update(User.builder().name("lucy").phone("16666666666").age(16).build());
}
}

8、SpringMVC的Controller层,insert的时候,createTime和updateTime是在写入的时候获取当前时间戳的,并不直接传参写入

package com.lihuia.web.controller;

import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import com.lihuia.web.model.User;
import com.lihuia.web.service.UserService;
import lombok.extern.slf4j.Slf4j;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
* Copyright (C), 2018-2019
* FileName: UserController
* Author: lihui
* Date: 2019/12/9
*/

@RestController
@RequestMapping("/api")
@Slf4j
public class UserController {

@Resource
private UserService userService;

@RequestMapping(value = "/insert", method = RequestMethod.POST)
public void insertUser(
@RequestParam String name,
@RequestParam String phone,
@RequestParam Integer age,
@RequestParam String email) {

User user = User.builder()
.name(name)
.phone(phone)
.age(age)
.email(email)
.createTime(new Date(System.currentTimeMillis()))
.build();
log.info("insert User: {}", user);
if (user != null) {
userService.insertUser(user);
}
}

@RequestMapping(value = "/update", method = RequestMethod.PUT)
public void updateUser(
@RequestParam String id,
@RequestParam String name,
@RequestParam String phone,
@RequestParam Integer age,
@RequestParam String email) {

User user = User.builder()
.id(id)
.name(name)
.phone(phone)
.age(age)
.email(email)
.updateTime(new Date(System.currentTimeMillis()))
.build();
log.info("update User: {}", user);
if (user != null) {
userService.updateUser(user);
}
}

@RequestMapping(value = "/delete", method = RequestMethod.DELETE)
public void deleteUser(@RequestParam(value = "id") String id) {
log.info("delete User id: {}", id);
userService.deleteUser(id);
}

@RequestMapping(value = "/getAll", method = RequestMethod.GET)
public List<User> getAllUsers() {
List<User> users = userService.getAllUsers();
log.info("get All Users: {}", users);
return users;
}

@RequestMapping(value = "/getById", method = RequestMethod.GET)
public User getUserById(@RequestParam(value = "id") String id) {
User user = userService.getUserById(id);
log.info("get User: {}", user);
return user;
}
}

9、Service层,我这里新增了一个校验方法,可以校验传入的参数,mapper接口封装在类方法里面

package com.lihuia.web.service;

import java.util.List;
import java.util.regex.Pattern;

import javax.annotation.Resource;

import com.lihuia.web.mapper.UserMapper;
import com.lihuia.web.model.User;

import org.springframework.stereotype.Service;

/**
* Copyright (C), 2018-2019
* FileName: UserService
* Author: lihui
* Date: 2019/12/9
*/

@Service
public class UserService {

@Resource
private UserMapper userMapper;

public void insertUser(User user) {
checkUser(user);
userMapper.insert(user);
}

public void deleteUser(String id) {
userMapper.delete(id);
}

public void updateUser(User user) {
checkUser(user);
userMapper.update(user);
}

public List<User> getAllUsers() {
return userMapper.getAll();
}

public User getUserById(String id) {
return userMapper.getById(id);
}

public void checkUser(User user) {

if (! Pattern.compile("^1\\d{10}$").matcher(user.getPhone()).find()) {
throw new IllegalArgumentException("phone: 电话号码数据异常");
}

if (user.getAge() <= 0 || user.getAge() > 100) {
throw new IllegalArgumentException("age: 年龄数据异常");
}

if (! Pattern.compile("^\\w+@.*").matcher(user.getEmail()).find()) {
throw new IllegalArgumentException("email: 邮箱数据异常");
}
}
}

10、到这里,后端基本就结束了,可以直接调用Rest请求来校验一下

NewImage

11、下面是前端了,这里用的是vue,首先是main入口,这里引用了axios,api方法绑定全局

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import router from './router'
import 'iview/dist/styles/iview.css'
import iView from 'iview'

// 引用axios,并设置基础URL为后端服务api地址
var axios = require('axios')
axios.defaults.baseURL = 'https://localhost:8080/api'

Vue.prototype.$axios = axios
Vue.config.productionTip = false

Vue.use(VueRouter)
Vue.use(iView)
new Vue({
router,
render: h => h(App),
}).$mount('#app')

12、接着就是axios依赖

axi0s/index.js

export default {
/**
* @description api请求基础路径
*/
baseUrl: {
dev: 'http://localhost:8080/api/'
}
}

axios/api.request.js

import axios from "axios"
import config from './index'
const baseUrl = process.env.NODE_ENV === 'development' ? config.baseUrl.dev : config.baseUrl.pro

const HttpRequest = axios.create({
baseURL: baseUrl, // api的base_url
timeout: 5000 // 请求超时时间
});
export default HttpRequest

13、页面路由

router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import user from "@/components/user";

Vue.use(Router)

export default new Router({
routes: [
{
path: '/user',
name: 'user',
component: user
}
]
})

14、路由代理

module.exports = {
lintOnSave: false,
devServer: {
port: 8000,
proxy: {
//配置跨域
'/api': {
target: 'https://localhost:8080',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
},
}

15、App.vue,不太清楚有啥用

<template>
<div id="app">
<router-view/>
</div>
</template>

<script>
export default {
name: 'app',

}
</script>

<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
#nav {
padding: 30px;
}

#nav a {
font-weight: bold;
color: #2c3e50;
}

#nav a.router-link-exact-active {
color: #42b983;
}
</style>

16、比较重要的,对接后端Controller的

import HttpRequest from '@/axios/api.request'

export const UserSelectByIdAction = (parameter) => {
return HttpRequest.request({
url: 'getById/',
method: 'get',
params: {
id: parameter
}
})
}

export const UserSelectAction = () => {
return HttpRequest.request({
url: 'getAll/',
method: 'get'
})
}

export const UserInsertAction = (parameter) => {
return HttpRequest.request({
url: 'insert/',
method: 'post',
params: parameter
})
}

export const UserUpdateAction = (parameter) => {
return HttpRequest.request({
url: 'update/',
method: 'put',
params: parameter
})
}

export const UserDeleteAction = (parameter) => {
return HttpRequest.request({
url: 'delete/',
method: 'delete',
params: {
id: parameter
}
})
}

17、user.vue的内容,前端view一些数据内容和响应,UserxxxxAction相关的方法重要

<template>
<div class="out-layer">
<!-- 按钮 -->
<div type="flex" class="right" justify="end">
<Input v-model="searchStr" placeholder="请输入编号查询" style="width: 800px"/>
<Button
type="info"
class="button"
style="margin-right: 5px;margin-left: 5px"
@click="UserSelectById(searchStr)"
>查询</Button>
<Button type="info" @click="AddBtnShow()">新增</Button>
</div>
<!-- 列表 -->
<div>
<Table border :columns="columns1" :data="dataList">
<template slot-scope="{row}" slot="action">
<Button type="primary" size="small" style="margin-right: 5px" @click="EditBtnShow(row)">修改</Button>
<Button type="error" size="small" @click="UserDelete(row)">删除</Button>
</template>
</Table>
</div>

<!-- 增加用户 -->
<div>
<Modal v-model="isAddShow" draggable footer-hide scrollable title="增加用户">
<Form ref="addFormList" :model="addFormList" :rules="ruleValidate" :label-width="80">
<FormItem label="编号" prop="id">
<Input v-model="addFormList.id" placeholder="请输入你的编号"/>
</FormItem>
<FormItem label="姓名" prop="name">
<Input v-model="addFormList.name" placeholder="请输入你的姓名"/>
</FormItem>
<FormItem label="电话" prop="phone">
<Input v-model="addFormList.phone" placeholder="请输入你的电话"/>
</FormItem>
<FormItem label="年龄" prop="age">
<Input v-model="addFormList.age" placeholder="请输入你的年龄"/>
</FormItem>
<FormItem label="邮箱" prop="email">
<Input v-model="addFormList.email" placeholder="请输入你的邮箱"/>
</FormItem>
<!-- <FormItem label="创建时间" prop="createTime">-->
<!-- <Input v-model="addFormList.createTime" placeholder="请输入你的创建时间"/>-->
<!-- </FormItem>-->
<!-- <FormItem label="更新时间" prop="updateTime">-->
<!-- <Input v-model="addFormList.updateTime" placeholder="请输入你的创建时间"/>-->
<!-- </FormItem>-->
<FormItem>
<Button type="primary" @click="HandleSubmit('addFormList')">提交</Button>
<Button @click="HandleReset('addFormList')" style="margin-left: 8px">重置</Button>
</FormItem>
</Form>
</Modal>
</div>

<!-- 修改用户 -->
<div>
<Modal v-model="isEditShow" draggable footer-hide scrollable title="修改用户">
<Form ref="editFormList" :model="editFormList" :rules="ruleValidate" :label-width="80">
<FormItem label="工号" prop="id">
<Input disabled v-model="editFormList.id" placeholder="请输入你的编号"/>
</FormItem>
<FormItem label="姓名" prop="name">
<Input v-model="editFormList.name" placeholder="请输入你的姓名"/>
</FormItem>
<FormItem label="手机" prop="phone">
<Input v-model="editFormList.phone" placeholder="请输入你的手机"/>
</FormItem>
<FormItem label="年龄" prop="age">
<Input v-model="editFormList.age" placeholder="请输入你的年龄"/>
</FormItem>
<FormItem label="邮箱" prop="email">
<Input v-model="editFormList.email" placeholder="请输入你的邮箱"/>
</FormItem>
<FormItem>
<Button type="primary" @click="HandleSubmit('editFormList')">提交</Button>
<Button @click="HandleReset('editFormList')" style="margin-left: 8px">重置</Button>
</FormItem>
</Form>
</Modal>
</div>
</div>
</template>
<script>
import {
UserSelectAction,
UserSelectByIdAction,
UserInsertAction,
UserDeleteAction,
UserUpdateAction
} from "../api/user.js";
export default {
data() {
return {
searchStr: "",
isAddShow: false,
isEditShow: false,
columns1: [
{
title: "编号",
key: "id"
},
{
title: "姓名",
key: "name"
},
{
title: "电话",
key: "phone"
},
{
title: "年龄",
key: "age"
},
{
title: "邮箱",
key: "email"
},
{
title: "创建时间",
key: "createTime"
},
{
title: "更新时间",
key: "updateTime"
},
{
title: "操作",
slot: "action",
width: 150,
align: "center"
}
],
dataList: [],
data: [],
addFormList: {
id: "",
name: "",
phone: "",
age: "",
email: ""
// createTime: "",
// updateTime: ""
},
editFormList: {
id: 0,
name: "",
phone: "",
age: "",
email: ""
},
ruleValidate: {
id: [
{
required: true,
// type: "number",
message: "编号不能为空!",
trigger: "blur"
}
],
name: [
{
required: true,
message: "姓名不能为空!",
trigger: "blur"
}
],
phone: [
{
required: true,
message: "电话不能为空!",
trigger: "blur"
}
],
age: [
{
required: true,
type: "number",
message: "年龄不能为空!",
trigger: "blur"
}
],
email: [
{
required: true,
message: "邮箱不能为空!",
trigger: "blur"
},
{
type: "email",
message: "不合法的邮箱!",
trigger: "blur"
}
]
}
};
},
methods: {
search() {
if (this.searchStr.trim() !== "") {
this.data.forEach(element => {
if (element.id == this.searchStr) {
var index = this.data.indexOf(element);
this.dataList = [];
this.dataList.push(this.data[index]);
}
});
} else {
this.dataList = this.data;
}
},
UserSelect() {
UserSelectAction().then(res => {
this.dataList = res.data;
this.data = res.data;
});
},
UserSelectById(searchStr) {
// eslint-disable-next-line no-unused-vars
UserSelectByIdAction(searchStr).then(res => {
this.$Message.success("查询该用户成功! ")
})
},
UserInsert() {
let flag = this.FindUser();
if (flag) {
this.$Message.error("编号已经存在!");
return;
}
// eslint-disable-next-line no-unused-vars
UserInsertAction(this.addFormList).then(res => {
this.AddBtnNotShow();
this.UserSelect();
this.$Message.success("增加成功!");
});
},
UserUpdate() {
// eslint-disable-next-line no-unused-vars
UserUpdateAction(this.editFormList).then(res => {
this.EditBtnNotShow();
this.UserSelect();
this.$Message.success("修改成功!");
});
},
UserDelete(item) {
// eslint-disable-next-line no-unused-vars
UserDeleteAction(item.id).then(res => {
this.UserSelect();
this.$Message.success("删除成功!");
});
},
HandleSubmit(name) {
this.$refs[name].validate(valid => {
if (valid) {
if (this.isAddShow) {
this.UserInsert();
} else if (this.isEditShow) {
this.UserUpdate();
}
} else {
this.$Message.error("填写信息错误!");
}
});
},
AddBtnShow() {
this.HandleReset("addFormList");
this.isAddShow = true;
},
AddBtnNotShow() {
this.isAddShow = false;
},
EditBtnShow(item) {
this.isEditShow = true;
this.editFormList.id = item.id;
this.editFormList.name = item.name;
this.editFormList.phone = item.phone;
this.editFormList.age = item.age;
this.editFormList.email = item.email;
},
EditBtnNotShow() {
this.isEditShow = false;
},
HandleReset(name) {
this.$refs[name].resetFields();
},
FindUser() {
let flag = false;
this.dataList.forEach(item => {
if (item.id == this.addFormList.id) flag = true;
});
return flag;
}
},
mounted() {
this.UserSelect();
}
};
</script>
<style>
.out-layer {
margin: 20px;
}

.right {
margin: 15px;
}
</style>

18、前端我用的是WebStorm,具体文件目录如下图

NewImage

19、本来以为这里就OK了,因为检查元素里可以查看到response的数据,但是页面显示报错

NewImage

20、google一下CORS相关报错,后端配置类需要注入

package com.lihuia.web.config;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
* Copyright (C), 2018-2019
* FileName: WebConfig
* Author: lihui
* Date: 2019/12/9
*/

@Configuration
@AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
public class WebConfig extends WebMvcConfigurationSupport {

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
super.addResourceHandlers(registry);
}

/**
* CROS跨域的处理
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")
.allowCredentials(true).maxAge(3600);
}
}

这样就结束了

 

OVER,接下来可以在该基础上来进行扩展了

发表回复