当前位置:   article > 正文

快速上手springboot+vue的前后端分离项目_springboot前后端分离项目怎么运行

springboot前后端分离项目怎么运行

快速上手springboot+vue的前后端分离项目

1.环境配置

  • Java环境
  • Mavan项目管理工具

2.springboot上手

  • 新建项目
    在这里插入图片描述
    在这里插入图片描述
  • 创建目录Controller和类Hello,启动项目
@RestController
public class Hello {

    @GetMapping("/hello")
    public String hello(){
        return "hello";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 访问 http://localhost:8080/hello,运行结果如下
    在这里插入图片描述
  • 系统配置(配置端口)
    在application.properties文件中添加配置
server.port = 8080
  • 1

3.springboot Controller

  • @Controller 请求页面和数据
  • @RestController 只请求数据,默认情况下会将返回的对象转换为JSON格式

4.springboot文件上传与拦截器

  • 使用IDEA创建Spring Boot项目,会默认创建出classpath:/static/目录,静态资源一般放在这个目录下即可。
  • 在application.properties中定义过滤规则和静态资源路径
spring.mvc.static-path-pattern=/static/**
spring.web.resources.static-locations=classpath:/static/
  • 1
  • 2
  • 设置表单的enctype=“multipart/form-data”
  • Spring Boot工程嵌入的tomcat限制了请求的文件大小,每个文件的配置最大为1Mb,单次请求的文件的总数不能大于10Mb。修改配置文件
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
  • 1
  • 2
  • 使用MultipartFile获取上传的文件数据,再通过transferTo方法写入到磁盘
	@PostMapping("/up")
    public String upload(MultipartFile f) throws IOException {
        System.out.println(f.getSize());
        System.out.println(f.getContentType());
        System.out.println(f.getOriginalFilename());
        saveFile(f);
        return "上传成功";
    }

    public void saveFile(MultipartFile f) throws IOException {
        File upDir = new File("E:\\2.Study\\SpringBoot2\\photos\\");
        if (!upDir.exists()){
            upDir.mkdir();
        }
        File file = new File("E:\\2.Study\\SpringBoot2\\photos\\" + f.getOriginalFilename());
        f.transferTo(file);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 拦截器在Web系统中非常常见,对于某些全局统一的操作,我们可以把它提取到拦截器中实现,主要用于:权限检查,性能监控,通用行为
  • Spring Boot定义了HandlerInterceptor接口来实现自定义拦截器的功能HandlerInterceptor接口定义了preHandle、postHandle、afterCompletion三种方法,通过重写这三种方法实现请求前、请求后等操作
  • 拦截器定义
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("登录拦截器");
        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 拦截器注册
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 仅拦截user路径下的所有资源
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/user/**");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

5.RESTful风格和Swagger

  • 客户端使用GET、POST、PUT、DELETE四种表示操作方式的动词对服务端资源进行操作:GET用于获取资源,POST用于新建资源(也可以用于更新资源),PUT用于更新资源,DELETE用于删除资源。
  • HTTP提供了POST、GET、PUT、DELETE等操作类型对某个Web资源进行Create、Read、Update和Delete操作。
  • spring boot实现restful api
    @GetMapping:处理GET请求,获取资源。
    @PostMapping:处理POST请求,新增资源。
    @PutMapping:处理PUT请求,更新资源。
    @DeleteMapping:处理DELETE请求,删除资源。
    @PatchMapping:处理PATCH请求,用于部分更新资源。
    在这里插入图片描述
	@GetMapping("/user/{id}")
    public String getUserById(@PathVariable String id){
        return "查询用户";
    }
    
    @PostMapping("/user")
    public String save(User user){
        return "保存用户";
    }

    @PutMapping("/user")
    public String update(User user){
        return "更新用户";
    }

    @DeleteMapping("/user/{id}")
    public String DeleteUserById(@PathVariable String id){
        return "删除用户";
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务,是非常流行的API表达工具。
		<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 配置Swagger
@Configuration//告诉Spring容器,这个类是一个配置类
@EnableSwagger2//启Swagger2功能
public class SwaggerConfig extends WebMvcConfigurationSupport {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com"))
                .paths(PathSelectors.any()).build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("演示项目API")
                .description("学习xxx的项目")
                .build();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("doc.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

配置解决springboot和swagger的冲突

# 解决springboot与Swagger的冲突
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
  • 1
  • 2
  • 访问http://localhost:8080/swagger-ui.html得到如下页面
    在这里插入图片描述
  • Swagger提供了一系列注解来描述接口信息,包括接口说明、请求方法、请求参数、返回信息等
    在这里插入图片描述

6.使用MybatisPlus

  • 添加依赖
		<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.17</version>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 全局配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=mzk
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 开启驼峰命名
mybatis-plus.configuration.map-underscore-to-camel-case=true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 添加@MapperScan注解
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 使用MybatisX插件来生成Service,Mapper文件,进行SQL查询的几种方法
    1、在xxxMapper.xml中写SQL语句
    2、在xxxMapper类中使用注解写SQL
    3、使用继承的BaseMapper和IService类自带的方法进行简单的SQL
    @Override
    public String getAllUser() {
        return userMapper.selectList(null).toString();
    }
  • 1
  • 2
  • 3
  • 4
  • 分页查询
    1、使用xxxMapper自带的selectPage方法,传入Page和QueryWrapper
    2、使用xxxService自带的page方法,传入Page和QueryWrapper
   @GetMapping("/findAll")
    public IPage findAll(){
        Page page = new Page(1,2);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        Page IPage = userService.page(page, queryWrapper);
        return IPage;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

7.vue组件化开发

  • 使用element ui
    npm install element-ui
import ElementUI from 'element-ui';
Vue.use(ElementUI)
  • 1
  • 2
  • 使用第三方图标库
    npm install font-awesome
    import 'font-awesome/css/font-awesome.min.css'

使用webStorm新建项目后,把 .lock文件删除后项目运行才不报错,配置文件 vue.config.js 中加入 lintOnSave: false 关闭eslint语法检测

8.axios发送请求

npm install axios

created() {
    axios.get('/user').then(response => {
      this.movies = response.data;
      console.log(this.movies)
    })
  },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在实际项目开发中,几乎每个组件中都会用到 axios 发起数据请求。此时会遇到如下两个问题:

  • 每个组件中都需要导入 axios
  • 每次发请求都需要填写完整的请求路径,可以通过全局配置的方式解决上述问题:
//配置请求根路径
axios.defaults.baseURL = 'http://api.com'
//将ax1os作为全局的自定义属性,每个组件可以在内部直接访问(Vue3)
app.config.globalProperties.$http = axios
//将axios作为全局的自定义属性,每个组件可以在内部直接访问(Vue2)
Vue.prototype.$http = axios
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

跨域问题,多种解决方法,需要时可百度
其中一种解决CORS 的方法, 在springboot项目中跨域的方法或类加上@CrossOrigin注解

9. 前端路由 VueRouter

  • Vue路由vue-router是官方的路由插件,能够轻松的管理 SPA 项目中组件的切换。
  • Vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来
  • vue-router 目前有 3.x 的版本和 4.x 的版本,vue-router 3.x 只能结合 vue2进行使用,vue-router 4.x 只能结合 vue3 进行使用
  • 安装:npm install vue-router@3
<template>
  <div id="app">
    <!--    声明路由链接-->
    <router-link to="/discover">发现音乐</router-link>
    <br>
    <router-link to="/my">我的</router-link>
    <br>
    <router-link to="/friends">朋友</router-link>
    <br>
    <button @click="toDiscover">跳转发现音乐</button>
    <!--    声明路由占位标签-->
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  components: {},
  methods: {
    toDiscover: function () {
      // 编程式导航 上面的为声明式,跳转同一页面会报错,所以catch
      this.$router.push('/discover').catch(error => error)
    }
  }
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

route.js

Vue.use(VueRouter)

const router = new VueRouter({
    // 指定hash属于与组件的对应关系
    routes: [
        {path: '/', redirect: '/my'},
        {path: '/discover', component: Discover},
        {
            path: '/friends', component: Friends,
            children: [
                {path: ":id", component: f, props:true}// props:true 表面以参数形式传参
            ],
        },
        {
            path: '/my',
            component: My,
            children: [
                {path: 'son1', component: my_son1},
                {path: 'son2', component: my_son2}
            ]
        }
    ]
})

export default router
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

main.js

import router from "@/router";

new Vue({
  render: h => h(App),
  router : router
}).$mount('#app')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

导航守卫

  • vue-router提供的导航守卫主要用来拦截导航,让它完成跳转或取消。
  • to:Route:即将要进入的目标路由。
  • from:Route:当前导航正要离开的路由。
  • next:在守卫方法中如果声明了next形参,则必须调用 next() 函数,否则不允许用户访问任何一个路由
  • 直接放行:next(),强制其跳转到登录页面:next(‘/login’),强制停留在当前页面:next(false)
router.beforeEach((to, from, next) => {
	if(to.path === '/main' && !isAuthenticated){
		next('/login')
	}
	else{
		next()
	}
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

10.状态管理 Vuex

对于组件化开发来说,大型应用的状态往往跨越多个组件。在多层嵌套的父子组件之间传递状态已经十分麻烦,而Vue更是没有为兄弟组件提供直接共享数据的办法。
基于这个问题,许多框架提供了解决方案——使用全局的状态管理器,将所有分散的共享数据交由状态管理器保管,Vue也不例外。

  • 安装 npm install vuex@ + 版本号
  • Vuex中有5个重要的概念:State、Getter、Mutation、Action、Module。
    在这里插入图片描述
  • State用于维护所有应用层的状态,并确保应用只有唯一的数据源,在组件中,可以直接使用this.$store.state.count访问数据,也可以先用mapState辅助函数将其映射下来
  • Getter维护由State派生的一些状态,这些状态随着State状态的变化而变化,在组件中,可以直接使用this.$store.getters.doneTodos,也可以先用mapGetters辅助函数将其映射下来
  • Mutation提供修改State状态的方法,在组件中,可以直接使用store.commit来提交mutation,也可以先用mapMutation辅助函数将其映射下来
  • Action类似Mutation,不同在于: Action不能直接修改状态,只能通过提交mutation来修改,Action可以包含异步操作, 在组件中,可以直接使用this.$store.dispatch(‘xxx’)分发 action,或者使用mapActions辅助函数先将其映射下来
  • 由于使用单一状态树,当项目的状态非常多时,store对象就会变得十分臃肿。因此,Vuex允许我们将store分割成模块(Module), 每个模块拥有独立的State、Getter、Mutation和Action,模块之中还可以嵌套模块,每一级都有着相同的结构。
    代码举例:

store.js

import Vuex from "vuex";
import Vue from "vue";

Vue.use(Vuex)

const store = new Vuex.Store({
    state: {
        count: 0,
        todos: [
            {id: 1, text: '吃饭', done: true},
            {id: 2, text: '睡觉', done: false}
        ]
    },
    mutations: {
        increment(state, n) {
            state.count += n;
        }
    },
    getters: {
        doneTodos: state => {
            return state.todos.filter(todo => todo.done)
        }
    }
})

export default store

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

main.js

import Vue from 'vue'
import App from './App.vue'
import store from "@/store";

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store: store
}).$mount('#app')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

hello.vue

<template>
  <div class="hello">
    <!--    <h1>{{this.$store.state.count}}</h1>-->
    {{ count }}
    <button @click="addOne">+1</button>
    <span v-for="todo in doneTodos" :key="todo.id">{{ todo.text }}</span>
  </div>
</template>

<script>
import {mapState, mapGetters} from 'vuex'

export default {
  name: 'HelloWorld',
  computed: {
    ...mapState([
      'count', 'todos'
    ]),
    ...mapGetters([
        'doneTodos'
    ])
  },
  // computed:{
  //   count(){
  //     return this.$store.state.count
  //   }
  // },
  methods: {
    addOne() {
      this.$store.commit('increment', 2);
    }
  }

}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

11.跨域认证

Session认证

互联网服务离不开用户认证。一般流程是下面这样。

  • 用户向服务器发送用户名和密码。
  • 服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等。
  • 服务器向用户返回一个 session_id,写入用户的 Cookie。
  • 用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
  • 服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

Token认证

Token 是在服务端产生的一串字符串,是客户端访问资源接口(API)时所需要的资源凭证,流程如下:

  • 客户端使用用户名跟密码请求登录,服务端收到请求,去验证用户名与密码
  • 验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
  • 客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者localStorage 里
  • 客户端每次向服务端请求资源的时候需要带着服务端签发的 token
  • 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据

JWT

  • JSON Web Token(简称 JWT)是一个token的具体实现方式,是目前最流行的跨域认证解决方案。
  • JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,具体如下:
  • 用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。
  • 为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。

JWT 的由三个部分组成,依次如下:

  • Header(头部)
  • Payload(负载)
  • Signature(签名)

三部分最终组合为完整的字符串,中间使用 . 分隔,如下:

  • Header.Payload.Signature

加入依赖

<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.1</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

生成token

//7天过期
private static Long expire 604800;
//32位秘钥
private static String secret "abcdfghiabcdfghiabcdfghiabcdfghi";
//生成token
public static String generateToken(String username){
	Date now new Date();
	Date expiration new Date(now.getTime() + 1000 * expire);
	return Jwts.builder()
		.setHeaderParam("type","JWT")
		.setSubject(username)
		.setIssuedAt(now)
		.setExpiration(expiration)
		.signwith(SignatureAlgorithm.HS512,secret)
		.compact();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

解析Token

//解析token
public static Claims getclaimsByToken(String token){
	return Jwts.parser()
	.setsigningKey(secret)
	.parseclaimsJws(token)
	.getBody();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/214412
推荐阅读
相关标签
  

闽ICP备14008679号