当前位置:   article > 正文

Springboot+Vue前后端联调和数据交互_vue框架和springboot框架前后端怎么交互的图

vue框架和springboot框架前后端怎么交互的图

目录

1 前后端联调

2 组件更改和数据交互

2.1 分页功能

2.2 添加用户

2.3 编辑和删除功能

2.3.1 编辑

2.3.2 删除


码字不易,喜欢就点个关注❤,持续更新技术内容。相关资料请私信。

相关内容:

第一篇:SpringBoot项目的创建和开发_Maxlec的博客-CSDN博客

第一篇:原生JS到Vue前端工程化开发_Maxlec的博客-CSDN博客

Servlet原理和简单的案例编写_Maxlec的博客-CSDN博客

1 前后端联调

现在将后台管理页面(前端)和后台服务服务端进行集成,之前都是通过MockJS拦截前端请求随机生成数据来模拟服务器的数据响应的。当服务端接口开发完成后,前端就可以通过连接服务端程序指定的端口,然后向指定功能接口发送请求。

如当我们要进入后台管理页面中,需要通过导航守卫进行登录权限验证,如token认证,用户信息请求,路由跳转等,在讲解Vue技术那篇文章中已经做了更详细的描述。

如下登录过程,向服务端发送POST请求,此时请求的URL中没有IP地址和端口号,我们需要在.env.development配置文件中添加URL前缀。

  1. export function login(data) {
  2. return request({
  3. url: '/user/login',
  4. method: 'post',
  5. data: data
  6. })
  7. }
  1. ENV = 'development'
  2. # base api
  3. VUE_APP_BASE_API = 'http://localhost:xxxx'

请求会服务器交给到以下控制器进行处理,然后返回包含token的结果集:

  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4. @Autowired
  5. private UserMapper userMapper;
  6. @PostMapping("/login")
  7. //前端一般传递的数据是json格式,必须使用对象接收,同时需要添加@RequestBody注解
  8. public Result login(@RequestBody User user){
  9. //生成token
  10. String token = JwtUtils.generateToken(user.getUname());
  11. //返回包含token的结果集
  12. return Result.ok().data("token", token);
  13. }
  14. }

接下来我们进行登录操作的讲解:

首先我们先启动前端项目:

进入登录组件页面,然后点击登录,导航守卫进行拦截走一遍权限认证,如没有token进入登录页登录,输入后点击登录会发送一次请求,传递账号和密码到服务端生成token后放在响应数据response在action中存储token,最后才进入首页。(实际上进入首页前还发送一次获取用户信息的请求。)

如果没有token想直接进入非白名单页面(一般是除登录页面以外的),则需要进入登录页进行上面过程后再重定向到想去的页面。

因为此时后端未启动,不能与服务端端口建立连接,也就发送不了网络请求:

启动服务端,与服务端端口建立连接,发送网络请求(注意跨域问题,在文首有相关内容的链接):

如图成功获取token和用户信息:

2 组件更改和数据交互

初步进行前后端联调后,如果有的页面组件不符合我们的需求,我们要改成我们想要的样子,然后通过Axios完成数据异步加载。

修改组件页面。

异步加载列表数据,并渲染到页面组件上。

首先在路由模块中找出组件与路由的关联,对路由对应的组件进行更改。

  1. {
  2. path: '/example',
  3. component: Layout,
  4. redirect: '/example/table',
  5. name: 'Example',
  6. meta: { title: '用户管理', icon: 'el-icon-s-help' },
  7. children: [
  8. {
  9. path: 'table',
  10. name: 'Table',
  11. component: () => import('@/views/table/index'),
  12. meta: { title: '用户列表', icon: 'table' }
  13. },
  14. {
  15. path: 'tree',
  16. name: 'Tree',
  17. component: () => import('@/views/tree/index'),
  18. meta: { title: '添加用户', icon: 'tree' }
  19. }
  20. ]
  21. },

所以要在在Table组件的进行更改,包括表单,表格,以及分页条,选择适当的组件后进行修改为符合自己需求的表单表格,注意需要在该表格用于展示用户的信息。

首先我们添加一个<el-main>来包含整个组件页面(template中只能有一个根标签),然后在其中添加elementui的表格标签<el-table :data="tableData">,在其中再添加表格列表,可以通过普通列<el-table-column prop="name" label="姓名" width="120">定义其属性,方便进行渲染,还可以定义普通列的标签名以及调整适当的宽度。

注意如果响应数据打他中的性别值是0或1,则需要进行判断然后再渲染,在需要进行判断后渲染的普通列中加入一个插槽:

  1. <template slot-scope="scope">
  2. {{ scope.row.gender == 1 ? '男':'女' }}
  3. </template>

表示判断每一条对象中的gender为"1"则显示为"男",否则为"女"。表格全部代码如下(注意还需要在数据模型返回绑定数据标签才会生效):

  1. <el-table :data="tableData" border>
  2. <el-table-column prop="id" label="序号" width="50"></el-table-column>
  3. <el-table-column prop="classes" label="班级" width="120"></el-table-column>
  4. <el-table-column prop="uname" label="姓名" width="120"></el-table-column>
  5. <el-table-column prop="gender" label="性别" width="50">
  6. <template slot-scope="scope">
  7. {{ scope.row.gender == 1 ? '男':'女' }}
  8. </template>
  9. </el-table-column>
  10. <el-table-column prop="birthday" label="生日"></el-table-column>
  11. <el-table-column label="操作">
  12. <el-button type="primary" size="mini">编辑</el-button>
  13. <el-button type="primary" size="mini">删除</el-button>
  14. </el-table-column>
  15. </el-table>

剩下的表单和分页条可查看文档加入到Table组件中。

最后向服务端发送axios异步请求然后完成数据的渲染。

然后我们定义一个组件生命周期函数created(钩子),当组件被创建展示为页面前调用,在该方法中我们可以向后端发送axios请求数据。如在fetchData方法中封装了getList方法,该方法负责向后端指定接口发送请求:

前端发送的异步请求的方法,一般在api目录下定义。如在getList方法封装的request方法中定义请求路径url,以及请求方法GET,以及要向后端传递的参数。(该参数是后面分页请求要传递的页号)。这样后端就能接收到该请求并响应对应的结果集。

所以当我们想在单页面应用模板vue-admin-template中向后端发送请求时,可以在api目录下定义对应的方法,在request方法中定义对应的属性。

  1. <script>
  2. import { getList } from '@/api/table'
  3. export default {
  4. data() {
  5. return {
  6. tableData: [],
  7. searchForm: {
  8. "name":"",
  9. gengder:""
  10. },
  11. value1:""
  12. }
  13. },
  14. created: function() {
  15. this.fetchData()
  16. },
  17. methods: {
  18. fetchData() {
  19. getList().then(response => {
  20. this.tableData = response.data.data
  21. })
  22. },
  23. }
  24. }
  25. </script>

如下图在前后端联调后,前端向后端发送请求后,后端返回响应数据data,通过赋值渲染到组件页面中:

上面只展现了各个组件的修改,简单地响应的数据,并未实现分页、编辑以及删除等功能。

2.1 分页功能

先分析,当我们点击分页的页号时,需要请求后端响应该页号的几条数据,如果是升序排序,就是向下读取几条数据作为一页;降序则是向上读取几条数据作为一页。然后返回响应数据然后进行赋值渲染。

  1. <!-- 分页条 -->
  2. <el-pagination background layout="total, prev, pager, next"
  3. @size-change="handleSizeChange"
  4. @current-change="changePage"
  5. :page-size="pageSize"
  6. v-if="isShow"
  7. :total="total"></el-pagination>

可以看到第一次进入页面,当组件创建完毕时自动向后端发送请求,后端读取几条数据作为第一页返回,然后赋值渲染给组件页面;而当我们点击分页号时,会在分页组件中触发changePage方法(依然是调用getList方法向后端发送异步请求)。可以看到,该方法重新向后端发送请求,并传递分页的页号,后端接收到后根据该页号进行分页查询,最后封装到结果集中返回,然后前端接收后进行赋值和渲染。这就完成了数据的分页显示。

  1. <script>
  2. import { getList } from '@/api/table'
  3. export default {
  4. data() {
  5. return {
  6. tableData: [],
  7. searchForm: {
  8. "name":"",
  9. gengder:"",
  10. },
  11. value1:"",
  12. total: 1,
  13. pageSize: 1,
  14. isShow: false //默认当为响应数据时不显示分页条
  15. }
  16. },
  17. created: function() {
  18. this.fetchData()
  19. },
  20. methods: {
  21. fetchData() {
  22. getList().then(response => {
  23. this.tableData = response.data.items.records
  24. //总记录
  25. this.total = response.data.items.total
  26. //每页显示条数
  27. this.pageSize = response.data.items.size
  28. //当响应回数据时才显示分页
  29. this.isShow = true
  30. })
  31. },
  32. changePage(pageNum){
  33. getList(pageNum).then((response => {
  34. this.tableData = response.data.items.records
  35. }))
  36. },
  37. }
  38. }
  39. </script>

如下后端后端的网络接口,观察理解以下,可以看到如果前端没有传递参数(也就是我们第一次打开页面时),后端默认响应第一页的数据,另外在查询条件中,通过id升序查询(如果用的是Sqlserver必须加该条件,否则报"@P0"附近有语法错误),最后封装到结果集中返回:

  1. @GetMapping("/getAll")
  2. public Result getAl(@RequestParam(defaultValue = "1") int pageNum){
  3. //分页对象
  4. Page<User> page = new Page<>(pageNum, 3);
  5. //Sqlserver中需要加一个唯一字段排序,否则系统排序结果不唯一
  6. QueryWrapper<User> wrapper = new QueryWrapper();
  7. wrapper.orderByAsc("id");
  8. Page<User> userPage = userMapper.selectPage(page, wrapper);
  9. return Result.ok().data("items", userPage);
  10. }

2.2 添加用户

在介绍编辑和删除之前,先对用户数据表进行添加用户。添加用户较简,只需在前端获取到用户数据就可以发送给后台进行插入。

在在用户表格中点击添加,触发doAdd方法。在doAdd方法中通过编程式路由跳转到添加页面。

<el-button type="primary" @click="doAdd">添加</el-button>
  1. doAdd(){
  2. //通过编程式路由跳转到添加页面
  3. this.$router.push("/users/add");
  4. },

在添加页面的表单中输入要添加的用户数据后确认添加,触发onSubmit方法,在方法中调用导入的网络请求方法add并传递表单数据。

  1. onSubmit() {
  2. add(this.form).then(res=>{
  3. this.$message({
  4. message: '添加成功',
  5. type: 'success'
  6. })
  7. });
  8. }

在add网络请求方法中向url发送post请求并发送填入的用户表单数据:

  1. export function add(data) {
  2. return request ({
  3. url: '/user/add',
  4. method: 'post',
  5. data
  6. })
  7. }

后端服务器接收请求后调用相应控制器的接口进行处理:

  1. @ApiOperation("添加用户")
  2. @PostMapping("/add")
  3. public Result addUser(@RequestBody User user) {
  4. userMapper.insertUser(user);
  5. return Result.ok();
  6. }

在用户控制器中定义了一个接收post请求的接口,通过@RequestBody注解接收实体类对象,调用UserMapper用户映射接口中自定义的insertUser方法添加用户。因为如果直接调用MybatisPlus的insert方法,该方法会根据用户User类定义的属性自动生成插入的sql语句,这样包括id在内的所有属性都作为字段插入数据。但是在sqlsever中自增长的id字段不能显示插入并报错。所以在进行插入操作时,需要在UserMapper用户映射接口中定义Mybatis发送手写的sql插入语句。

另外,在自定义插入sql语句时,需要字段和只一一对应,否则会出现混插的情况。

  1. @Insert("insert into t_user values(#{uname}, #{pwd}, #{birthday}, #{gender}), #{classes}")
  2. //或者
  3. @Insert("insert into t_user(uname, pwd, birthday, gender, classes)
  4. values(#{uname}, #{pwd}, #{birthday}, #{gender}), #{classes}")

另另外,其实MybatisPlus对sqlsever数据库进行插入操作的问题可以通过@TableField(exist = false)注解来说明id属性不作为字段处理,但是后面的查询、更新和删除操作又需要用到id,所以不能去掉,只能委屈一下插入操作了。

2.3 编辑和删除功能

编辑用户与添加用户类似,不过还要在前台展示要编辑的用户数据,方便编辑;在后台,接收到传递来的用户对象时,需要提取用户id作为条件进行用户的删除。

首先进行较删除功能难一点的编辑功能。

2.3.1 编辑

在用户表格中点击编辑,调用handleEdit方法并传递当前行的对象:

<el-button type="primary" size="mini" @click="handleEdit(scope)">编辑</el-button>

在handleEdit方法中,将flag设置为true显示编辑浮窗,然后将传递的对象赋值给form表单对象进行展示,方便用户对比编辑。

  1. handleEdit(scope) {
  2. //首先打开浮窗
  3. this.flag=true;
  4. //1.将传递的对象数据赋值给要编辑的表单,这种方式直接影响用户表格数据的显示
  5. this.form = scope.row
  6. //2.或者是将对象里面属性展开,不用原来的对象赋值,避免影响用户表格数据的显示
  7. // this.form = {...scope.row}
  8. },
  1. <!--定义浮窗的标签-->
  2. <el-dialog title="编辑" width="50%" :visible.sync="flag"></el-dialog>

修改完表单数据后,点击浮窗的确定按钮调用doEdit方法,并关掉浮窗:

<el-button type="primary" @click="doEdit();flag=false">确 定</el-button>

在doEdit方法中,调用从api目录导入的edit方法发送异步请求,将编辑好的表单作为参数传递。

  1. doEdit(){
  2. edit(this.form).then(res=>{
  3. this.$message({
  4. message: '编辑成功',
  5. type: 'success'
  6. })
  7. });
  8. },

在api目录下js文件中的edit网络请求方法中定义请求的url、请求操作以及传递的参数:

  1. export function edit(data) {
  2. return request ({
  3. url: '/user/edit',
  4. method: 'put',
  5. data // data: {data}
  6. })
  7. }

然后后端服务器接收请求后调用相应控制器的接口进行处理:

  1. @RestController
  2. @RequestMapping("/user")
  3. @CrossOrigin
  4. public class UserController {
  5. @Autowired
  6. private UserMapper userMapper;
  7. @PutMapping("/edit")
  8. public Result editById(@RequestBody User user) {
  9. UpdateWrapper<User> wrapper = new UpdateWrapper<>();
  10. int id = user.getId();
  11. System.out.println(user);
  12. wrapper.eq("id",id);
  13. userMapper.update(user, wrapper);
  14. return Result.ok();
  15. }
  16. }

在用户控制器中定义了一个接收put请求的接口,通过@RequestBody注解接收实体类对象,然后提取对象中的id作为条件对象传给MybatisPlus中的update更新方法进行修改。因是更新数据,数据库只返回影响行数,我们只需要返回请求成功的结果集即可。

2.3.2 删除

删除比较简单,在表格中点击删除,直接调用doDelete方法传递该行数据的id:

<el-button type="primary" size="mini" @click="doDelete(scope.row.id)">删除</el-button>

在doDelete方法中直接调用网络请求方法:

  1. doDelete(id) {
  2. del(id).then(res => {
  3. this.$message({
  4. message: '删除成功',
  5. type: 'success'
  6. })
  7. })
  8. },

在del方法中发送异步请求:

  1. export function del(id) {
  2. return request ({
  3. url: '/user/del',
  4. method: 'delete',
  5. params: { id } //跟data一样,如果参数为params也可以简写为params
  6. })
  7. }

后端控制器中定义的delete请求接口直接根据传递过来的id参数,调用BaseMapper接口中的deleteById删除用户:

  1. @ApiOperation("根据id删除用户")
  2. @DeleteMapping("/del")
  3. public Result deleteUser(int id){
  4. userMapper.deleteById(id);
  5. return Result.ok();
  6. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/808522
推荐阅读
相关标签
  

闽ICP备14008679号