SpringMVC前后端数据交换简述

一.基本概念及知识准备:

1)基本概念

SpringMVC是Spring框架后续开发的web模块.主要负责前后端数据交换.其基于Servlet进行开发的框架,目的简化前后端的调用.
请求类型一共8种,但是常用的有4种(get/post/put/delete)

@RequestMapping("/hello")
负责用户的请求路径后台服务器之间的映射关系;如果请求路径不匹配,则用户报错404

@ResponseBody
作用: 将后端服务器的返回值转化为JSON. 如果服务器返回的是String类型,则按照自身返回.

接收参数

@RequestBody 接收前端自定义对象的参数 

2)JSON知识回顾

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
可参考Json官网:http://www.json.org.cn/
JSON数据格式:
①JSON格式-对象格式:     {"id": "100","name": "tomcat", "age": "18"}
②JSON格式-数组格式:     [100,"张三",true]
JSON可以嵌套,值(value) 可以是双引号括起来的字符串(string)、数值(number)、true、false、 null、对象(object)或者数组(array)。这些结构可以嵌套。

 3)Axios知识回顾

Axios封装了Ajax,
功能和作用: Ajax主要实现前后端交互.提高用户页面与服务器之间交互效率.

                     promise是Axios封装服务器返回值的对象
特点: 局部刷新,异步访问
缺点:
不同的服务器之间发送ajax请求时会有"跨域"问题,通过注解搞定. @CrossOrigin
注意: axios为了接收后端服务器数据,利用promise对象封装后端服务器数据参数
Ajax异步的原理:
1. 由Ajax引擎直接访问后端服务器。
2. 在回调函数没有执行之前,用户可以执行自己的任务。 异步

在这里插入图片描述

回调地狱优化-async-await

<script>
			axios.defaults.baseURL = "http://localhost:8080"
			async function saveUser(){
				let user = {id: 100,name:"春节已过!!!"}
				let promise = await axios.post("/axios/saveUser",user)
				console.log(promise.data)
			}
			/* 2.调用函数 */
			saveUser()
</script>

4)跨域问题

同源策略:
浏览器URL中的地址/Ajax请求的URL地址必须满足 协议/域名/端口号都相同时.表示满足同源策略.如果满足同源策略,则称之为 “同域访问” 反之称之为 “跨域访问” 跨域访问浏览器一般都会报错(浏览器默认端口号是http80,https443,localhost!=127.0.0.1)

跨域解决方案:
1.jsonp 方式跨域 淘汰了.
2.CORS 跨域资源共享:CORS要求在服务器端标识哪个网址可以跨域
@CrossOrigin(value="http://www.baidu.com")表示只准许该网址http://www.baidu.com访问

5)Tomcat/jetty对请求的处理

 二.SpringMVC底层实现原理

在这里插入图片描述

SpringMVC调用流程:


1. 用户发起请求之后,第一步访问就是前端控制器.
2. 前端控制器只负责请求的转发/响应. 没有办法直接处理业务.
3.当SpringMVC容器启动时,处理器映射器首先会加载所有的@RequestMapping注解.将请求路径与方法进行绑定.保存到Map中. Map</url,method方法>, 当前端控制器发送请求被处理器映射器接收.①如果URL地址匹配,则告知应该执行哪个方法.②如果url地址不匹配.,则提示用户404

4.前端控制器得知将要执行的方法是谁,但是前端控制只能转发,没有办法执行业务.
所以请求处理器适配器执行业务逻辑.

5.处理器适配器针对配置文件(xml方式/注解方式/其它方式)的格式,挑选合适的处理器去执行业务逻辑. 业务执行成功之后返回ModelAndView对象 Model:业务数据 View:页面

历史版本: 需要返回页面名称,及将数据填充到页面中
6. 前端控制器接收到返回的ModelAndView之后,交给视图解析器去解析数据. 视图解析器通过自身的配置获取页面的名称 (/web/user.html).
7. 最终通过经过视图渲染,将数据填充到页面中.最终用户看到的页面就包含了业务数据.

前后端分离方式:
关键用法: @ResponseBody 要求返回业务数据并且转化为JSON串. 程序直接跳过6-7直接将数据返回给用户.

三.传统方式发送请求

1)传统get/delete方式放松请求

url格式如: http://localhost:8080/findUser?name=tomcat&age=18&sex=

                http://localhost:8080/user/saveHobby?hobby=篮球,排球,乒乓球

①前端发送 axios请求

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Axios测试</title>
		<script src="../js/axios.js"></script>
	</head>
	<body>
		<h1>Axios测试案例-1</h1>
		<script>
            /* 简化方式1: 抽取后端服务器地址 */
			axios.defaults.baseURL = "http://localhost:8080"
				/*发送无参类型的请求*/
			let url = "/hello"
			axios.get(url).then(function(promise){
				console.log(promise.data)
			})

                /*发送含1个参数 类型的请求*/
            let url1 = "/axios/getUserById?id=100"	
			axios.get(url1)
				 .then(function(promise){
					 console.log(promise.data)
				 })

                 /*发送含多个参数 类型的请求*/
            let user2 = {name:"tomcat",age:100}
			let url2 = "/axios/getUserByNA"
			axios.get(url2,{params: user2})
				 .then( promise => {
					 console.log(promise)
				 })

            /* 简化方式3: async await简化调用 重点
			   问题描述: 如果ajax如果嵌套的层级较多,则引发"回调地狱"问题
			   解决问题: 能否将axios中的then进行简化.
			   语法:
					1. 使用async关键字标识函数
					2. 通过await标识ajax请求
					3. 必须同时出现
			 */	      
            async function getHello(){  //定义函数
				//let {data: result,status: code} = await axios.get("/web/hello")
				let {data: result} = await axios.get("/web/hello")
				alert(result)
			}
			//调用函数
			getHello()

            /* 箭头函数写法
				1.去除function关键字
				2.参数与函数体之间使用 => 连接
				3.如果只有一个参数,则参数括号可以省略
				4.箭头函数使用一般用于回调函数
				5.如果使用function关键字 则使用this时会产生歧义
			 */
			let user2 = {id: 200,name:"箭头函数!!!"}
			axios.post("/axios/saveUser",user2)
				 .then(function(promise){
					console.log(promise.data)
				 })
			axios.post("/axios/saveUser",user2)
				 .then(	promise => {
					console.log(promise.data)
				 })	 

		</script>
	</body>
</html>

②后端接收数据并返回处理好的数据

@RestController
@CrossOrigin    //主要解决跨域问题
@RequestMapping("/axios")
public class AxiosController {
    //@RequestParam("id")该注解主要考虑向前兼容,JDK1.5倩是需要加的
    @RequestMapping("/getUserById")
    public User getUserById(@RequestParam("id") Integer id){
        //根据ID查询数据库
        User user = new User();
        user.setId(id);
        user.setName("好好学习");
        user.setAge(1000);
        user.setSex("男");
        return user;
    }

    @RequestMapping("/findJSON")
    public User findJSON(User user){
        //在业务层扩展数据
        user.setId(101);
        user.setSex("男");
        return user;
    }
 
    @RequestMapping("/getUserByNA")
    public List<User> getUserByNA(User user){
        List<User> list = new ArrayList<>();
        list.add(user);//简化赋值操作 直接返回
        list.add(user);
        return list;
    }

服务端:

当方法return的类型为自定义对象或集合时,
springMVC框架会自动将集合或对象转成son格式的字符串

然后将字符串再转成二进制数据通过网络传输给客户端

客户端:
服务器返回的是二进制数据,会先将二进制数据转成son格式的字符串
然后axios框架会将JSON格式的字符串再转成数组里面装对象

 2)传统post/put方式放松请求

①前端发送 axios请求

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Axios测试</title>
		<script src="../js/axios.js"></script>
	</head>
	<body>
		<h1>Axios测试案例-2</h1>
		<script>		
				/*语法:
					1.参数 axios.post(URL地址,数据对象)
					2.注意事项: 与axios.get(url,{params:对象})请求写法不一样.*/
			 
			let url1 = "http://localhost:8080/axios/saveUser"
			let user1 = {id:100, name:"猫", age:2, sex: "母"}
			axios.post(url1,user1)
				 .then(function(promise){
					 console.log(promise.data)
				 })
				 
		</script>
	</body>
</html>

②后端接收数据并返回处理好的数据

@RestController
@CrossOrigin    //主要解决跨域问题
@RequestMapping("/axios")
public class AxiosController {
  
    @RequestMapping("/getUserById")
    public User getUserById(Integer id){
        int a = 100;
        //根据ID查询数据库
        User user = new User();
        user.setId(id);
        user.setName("好好学习");
        user.setAge(1000);
        user.setSex("男");
        return user;
    }
 
    @RequestMapping("/getUserByNA")
    public List<User> getUserByNA(User user){
        List<User> list = new ArrayList<>();
        list.add(user);//简化赋值操作 直接返回
        list.add(user);
        return list;
    }

    @RequestMapping("/findUserByNS/{name}/{sex}") //调用set方法为属性赋值
    public List<User> findUserByNS(User user){
        List<User> list = new ArrayList<>();
        list.add(user);
        list.add(user); 
        return list;
    }

    //@RequestMapping(value="/saveUser",method = RequestMethod.POST)
    @PostMapping("/saveUser")
    public String saveUser(@RequestBody User user){
        System.out.println(user);
        return "新增用户成功!!!";
    }
}

四.restFul风格发送请求

restFul风格方式提交数据一般用于更新操作
url格式如: http://localhost:8080/findUser/tomcat/18/男
要求:  restFul的风格数据的位置一旦确定,不能修改.
注意: restFul风格请求支持常用的4种请求类型(get/post/put/delete),
          但是为了数据安全问题一般不使用post请求

1)前端发送 axios请求 

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Axios测试</title>
		<script src="../js/axios.js"></script>
	</head>
	<body>
		<h1>Axios测试案例-1</h1>
		<script>
            /**动态获取参数
				 * 模版字符串写法:ES6引入的新功能
				 * 语法:
				 * 		1. 使用反引号  ``
				 * 		2. 作用:  1.可以保证字符串的格式
				 * 				  2.可以动态获取变量的值
				 */ 
				let user3 = {name: "tomcat", sex: "男"}
				let url3 = `http://localhost:8080/axios/findUserByNS/${user3.name}/${user3.sex}`
				axios.get(url3)
					 .then(function(promise){
						 console.log(promise.data)
					 })
        </script>
	</body>
</html>
            

 2)后端接收数据并返回处理好的数据

 
    @RequestMapping("/findUser/{name}/{age}/{sex}")
    public String findUser(@PathVariable String name,
                           @PathVariable int age,
                           @PathVariable String sex){

        return name+":"+age+":"+sex;
    }
    
     /**注意:如果{ }的属性与对象的属性名称一致则可以使用对象接收 */
    @RequestMapping("/findUserByNS/{name}/{sex}") //调用set方法为属性赋值
    public List<User> findUserByNS(User user){
        List<User> list = new ArrayList<>();
        list.add(user);
        list.add(user);
        return list;
    }

使用注解说明 

@Controller
将类交给SpringMVC管理,SpringMVC交给Spring容器管理

JSON串与对象之间的相互转化
1.@ResponseBody
①如果返回的是对象,将返回对象转化为JSON串到前端
②如果返回String类型,则@ResponseBody将字符串本身返回给前端.
2.@RequestBody   JSON串转化为User  
要求: JSON串转化 要求json串中的属性与对象中的属性一致,并且赋值时调用对象的set方法

@RestController
public class WeiboController {
    @Autowired(required = false)
    WeiboMapper mapper;
    @RequestMapping("/insert")
    public void insert(@RequestBody Weibo weibo){
        //由于客户端提交的参数不是FormData对象了 是一个自定义的对象
        // 需要通过@RequestBody注解修饰接收数据的参数
        System.out.println("weibo = " + weibo);
        mapper.insert(weibo);
    }
}

@RestController
等同于@Controller + @ResponseBody 

@RequestMapping
负责用户的请求路径后台服务器之间的映射关系
可以支持任意类型的请求. 但是这样的写法不安全
对应请求使用如下注解:

新增: post请求类型    @PostMapping("")
删除: delete请求类型 @DeleteMapping("")
修改: put请求类型      @PutMapping("")
查询: get请求类型      @GetMapping("")

@PathVariable
接收RestFul风格数据时,利用@PathVariable注解,动态获取路径中的数据,要求名称必须匹配

 五.请求常见异常

  1. 405 异常 ajax的请求类型与后端接收的请求类型不匹配.
  2. 400异常 参数类型不匹配
  3. 404异常 请求路径找不到
  4. 403异常 后端未对请求进行处理

 会话管理

客户端和服务器之间进行数据交互,遵循的是HTTP协议,此协议属于无状协议(一次请求对应一次响应)响应完连接断开,服务器无法跟踪客户端的请求,通过会话管理中的Cookie技术,可以在客户端向服务器发出请求时服务器给客户端添加一个标识(把数据保存到这个标识里面) , 之后客户端每次发出请求时都会带上这个标识,这样服务器通过此标识就能识别出该客户端的信息, 但是这种把数据保存在客户端的方式存在数据被篡改的风险,Session的作用就是能够解决这种问题, 因为Session的数据是保存在服务器端,不存在被篡改的问题.

Cookie: 数据保存在客户端,类似打孔式的会员卡

  • 只能保存字符串类型的数据
  • 保存时长: 默认保存在浏览器内存中, 浏览器关闭时清除, 也可以设置任意的保存时长,设置后数据会保存到客户端磁盘中,时间到了后清除.
  • 应用场景: 长时间保存的数据使用Cookie,比如记住用户名和密码

 Session: 数据保存在服务器端, 类似银行卡

  • 可以保存任意对象类型的数据
  • 保存时长: 只能保存半小时左右
  • 应用场景: 对数据安全性要求比较高并且是短时间保存的,比如登录状态

通过Cookie实现记住用户名和密码功能 

@RestController
public class UserController {
    @Autowired(required = false)
    UserMapper mapper;

    @RequestMapping("/login")
    public int login(@RequestBody User user, HttpSession session, HttpServletResponse response){
        System.out.println("user = " + user);

        User u = mapper.selectByUsername(user.getUsername());
        if (u!=null){
            if (u.getPassword().equals(user.getPassword())){
                //登录成功后把从数据库里面查询到的用户对象保存到Session会话对象中
                session.setAttribute("user",u);

                //判断是否需要记住用户名和密码
                if (user.isRem()){
                    //创建Cookie记住用户名和密码
                    Cookie c1 = new Cookie("username",user.getUsername());
                    Cookie c2 = new Cookie("password",user.getPassword());
                    //设置Cookie保存数据的时长
                    c1.setMaxAge(60*60*24*30);
                    //把cookie下发给客户端
                    response.addCookie(c1);
                    response.addCookie(c2);
                }
                return 1;
            }
            return 3;
        }
        return 2;
    }
    @RequestMapping("/currentUser")
    public User currentUser(HttpSession session){
        //获取登录成功时保存的用户对象
        return (User) session.getAttribute("user");
    }
    @RequestMapping("/logout")
    public void logout(HttpSession session){
        //删除登录成功时保存的用户对象
        session.removeAttribute("user");
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录页面</h1>
<form action="">
    <input type="text" name="username" v-model="user.username" placeholder="用户名"><br>
    <input type="password" name="password" v-model="user.password" placeholder="密码"><br>
    <input type="checkbox" name="rem" v-model="user.rem">记住用户名和密码<br>
    <input type="button" value="登录" @click="login()">
</form>
<script src="js/vue.min.js"></script>
<script src="js/axios.min.js"></script>
<script>
    let v = new Vue({
        el:"form",
        data:{
            user:{
                username:"",
                password:"",
                rem:""
            }
        },
        methods:{
            login(){
                axios.post("/login",v.user).then(function (response) {
                    if (response.data==1){
                        alert("登录成功!");
                        location.href="/";
                    }else if(response.data==2){
                        alert("用户名不存在!");
                    }else{
                        alert("密码错误!");
                    }
                })
            }
        }
    })
    //获取cookie里面的数据
    let cookieStr = document.cookie;
    //'username=tom; password=123456'
    let cookieArr = cookieStr.split(";");
    for (let cookie of cookieArr) {
        let arr = cookie.split("=")
        let name = arr[0].trim();  // trim()去掉空格
        let value = arr[1];
        if (name=="username"){
            v.user.username = value;
        }else if (name=="password"){
            v.user.password = value;
        }
    }

</script>
</body>
</html>

文件上传实现:

@RestController
public class UploadController {

    @RequestMapping("/upload")
    public String upload(MultipartFile picFile) throws IOException {
        //得到原始文件名
        String fileName = picFile.getOriginalFilename();
        //得到后缀  .jpg
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        //得到唯一文件名 UUID.randomUUID()获取唯一标识符
        fileName = UUID.randomUUID()+suffix;
        System.out.println("文件名:"+fileName);
        //保存图片的文件夹
        String dirPath = "D:/file";
        File dirFile = new File(dirPath);
        //判断如果文件夹不存在 则创建文件夹
        if (!dirFile.exists()){
            dirFile.mkdirs();//创建文件夹
        }
        //准备一个完整的文件路径
        String filePath = dirPath+"/"+fileName;
        //把上传的文件保存到上面指定的路径   异常抛出
        picFile.transferTo(new File(filePath));

        //把图片在服务器中的路径返回给客户端
        return "/"+fileName;
    }

    @RequestMapping("/remove")
    public void remove(String name){
        //文件夹路径
        String dirPath = "D:/file";
        //得到文件的完整路径
        String file = dirPath+name;
        System.out.println("删除的文件路径:"+file);
        //删除服务器中的文件
        new File(file).delete();
    }
}
#配置静态资源文件夹 默认的是static文件夹
spring.web.resources.static-locations=file:D:/file,classpath:static
#设置单个上传文件的大小
spring.servlet.multipart.max-file-size=10MB
#设置批量上传文件的总大小
spring.servlet.multipart.max-request-size=100MB

cookie实现记住用户名和密码及session保存登录状态

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值