概述
作为一枚菜鸟,前后端交互可是大问题,经常数据交互失败,不知道怎么跳转。在这分享一下交互的小心得。
我们不妨先大概了解一下整个访问流程:
用户从输入网址按下回车,交互就已经开始了。
浏览器会将请求按照http协议(或者其他https,ftp等)将请求数据封装包从电脑的端口发出 -> 路由器 -> 运营商(域名解析之类的)-> 目标服务器(可能会有代理,负载均衡等等)
最终从服务器开放的端口,进入到服务器(TCP三次握手和四次挥手,Tomcat之类的,会根据协议的内容进行解析)-> web项目进行具体的逻辑处理 -> 返回数据 -> 浏览器接收数据(根据协议内容进行解析)
整个过程,协议很重要,因为客户端和服务器都是通过协议来解析和发送信息的,最常见的http协议,协议头部的参数常用的哪几个,有什么影响。
Http协议的简介,说的很有意思:https://blog.csdn.net/u010256388/article/details/68491509/
还有缓存问题(cookie、session、localstorage、cashe-controller等),不仅提高效率,而且有时候你更改了代码测试却没变化的重要原因:https://www.jianshu.com/p/9ed3e8759ce3
相应的后台tomcat的缓存处理:http://www.360doc.com/content/17/0721/17/41344223_673116604.shtml(了解即可)
具体到使用分为:前端交互,分为同步和异步。
同步交互
常见的from表单提交(post方式带参数),URL直接访问(get方式,有参数在地址栏可见,特殊符号需要转义,不安全)。
异步交互
关键是XMLHttpRequest对象,平时说的ajax只是一种交互模式,并不是什么新技术,其原理就是对XMLHttpRequest的封装。所以对XMLHttpRequest深入全面的了解可以帮助我们更好的掌握异步交互。
可以看这篇精品文章了解:https://www.jianshu.com/p/b037f71af548,里面涉及到了在传送过程中各个参数的用法,会触发的事件,返回值等等很实用的知识。(虽然能设置同步交互,但不推荐)
其中传输类型 contentType就非常重要,这个值设置决定了后台怎么去解析http协议。
- 上图所示,如果格式前后端发送和接收对不上,那是获取不了数据。如图的最后一步返回,如果想页面跳转,则不需要@ResponseBody,改为返回ModelAndView
- @RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。
- @RequestParam接收的是key-value里面的参数,所以GET方式的数据和表单提交,可以接收。
- @RequestBody和@RequestParma的使用知识,更轻松的全面掌握:https://blog.csdn.net/justry_deng/article/details/80972817
前端交互类
前端我创建了两个工具类,来负责交互。
异步交互类
我把jQuery的ajax进行了封装,主要是获取和计算信息,代码如下:
1 /* 2 * 以下为程序错误码 3 */ 4 //通用的请求失败,包括未知原因 5 var EXPECTATION_Failed = 417; 6 var EXPECTATION_QUERY = 404 7 8 /** 9 * 访问后台的对象,为ajax封装 10 * @constructor 11 12 var Query = function (url,param,callback,contentType) { 13 this.url = url; 14 15 先确认参数存在,如果不存在则创建空map 16 if (!param) { 17 param = new Map(); 18 } 19 注意,要根据不同的传输格式来确定传输的值的类型 20 if (contentType == Query.NOMAL_TYPE) { 21 this.param = JSON.parse(this._convertParam(param)); 22 } else { 23 this.param = ._convertParam(param); 24 25 26 27 this.callback = callback; 28 this.contentType = contentType; 29 请求超时,默认5秒 30 this.timeout = 5000 31 是否异步请求,默认异步 32 this.async = true 33 } 34 35 Query.JSON_TYPE = 'application/json' 36 Query.NOMAL_TYPE = 'application/x-www-form-urlencoded' 37 38 39 * ajax请求的访问 40 * @param url 要访问的地址 41 * @param paramMap 传给后台的Map参数,key为字符串类型 42 * @param callback 回调函数 43 * @param contentType 传输数据的格式 默认传输application/x-www-form-urlencoded格式 44 45 Query.create = 46 return Query(url,Query.NOMAL_TYPE); 47 48 49 Query.createJsonType = 50 51 52 53 54 * 将ParamMap转为json格式,目前只支持Map对象,以后会扩展 55 * @param paramMap 56 * @private 57 58 Query.prototype._convertParam = (param) { 59 60 if (param instanceof Map) { 61 return strMap2Json(param); 62 63 64 65 66 * 对ajax回调函数的封装 67 * @param callBack 68 69 70 Query.prototype._callback = (queryResult) { 71 72 取消加载框 73 if (.loadDom) { 74 $(this.loadDom).remove("#loadingDiv"); 75 76 77 Query对象 78 var self = queryResult.queryObj; 79 var data = $.parseJSON(queryResult.responseText); 80 记录请求是否有错误 81 self.queryException = false 82 var handleError; 83 84 if (queryResult.status == EXPECTATION_Failed || queryResult.status == EXPECTATION_QUERY) { 85 var error = queryResult.responseText; 86 self.queryException = 87 88 89 调用回调函数,如果返回结果为true,则对于出错不会默认错误处理 90 if (self.callback Function) { 91 handleError = self.callback(data); 92 93 94 如果出现了异常并且没有被处理,那么将进行默认错误处理 95 if (self.queryException && !handleError) { 96 window.location.href = "/system/error/" + error.code + "/" + error.msg; 97 98 99 如果需要跳转,则进行跳转 100 if (data.redirect_url) { 101 window.location.href = data.redirect_url; 102 103 104 105 106 * 正式发送ajax 107 108 109 Query.prototype.sendMessage = () { 110 var self = 111 var xhr = $.ajax( 112 { 113 type: "post",114 url: .url,1)">115 contentType: .contentType,1)">116 data: .param,1)">117 ajax发送前调用的方法,初始化等待动画 118 @param XHR XMLHttpRequest对象 119 beforeSend: (XHR) { 120 绑定本次请求的queryObj 121 XHR.queryObj = self; 122 if (self.beforeSendFunc 123 self.beforeSendFunc(XHR); 124 } 125 126 if (self.loadDom HTMLElement) { 127 self.loadDom.innerText = ""128 $(self.loadDom).append("<div id='loadingDiv' class='loading'><img src='/image/loading.gif'/></div>"129 } else jQuery) { 130 self.loadDom.empty(); 131 self.loadDom.append("<div id='loadingDiv' class='loading'><img src='/image/loading.gif'/></div>"132 133 },1)">134 complete: ._callback,1)">135 timeout:.timeout,1)">136 async:.async 137 } 138 ); 139 140 141 142 143 * 检测是否有错误,返回ture有错误,或者false 144 145 Query.prototype.checkEception = 146 .queryException; 147 148 149 ------------------------以下为对Query的参数设置--------------------------- 150 151 * 在ajax发送前设置参数,可以有加载的动画,并且请求完成后会自动取消 152 * @param loadDom 需要显示动画的dom节点 153 * @param beforeSendFunc ajax发送前的自定义函数 154 155 Query.prototype.setBeforeSend = (loadDom,beforeSendFunc) { 156 this.loadDom = loadDom; 157 this.beforeSendFunc = beforeSendFunc; 158 159 160 161 * 设置超时时间 162 * @param timeout 163 164 Query.prototype.setTimeOut = (timeout) { 165 this.timeout = timeout; 166 167 168 Query.prototype.setAsync = (async) { 169 this.async = async; 170 }
封装的好处:
同步交互类
from表单创建类,主要是进行有参数传输的页面跳转,因为直接URL跳转参数会暴露不安全
1 2 * 访问后台的类,构造form表单来进行post请求 3 * @param url 4 * @param paramMap 参数map 5 6 7 var QueryForm = 8 form表单的JQ对象 9 this.form = $("<form></form>"10 this.form.attr("action"11 this.form.attr("method","post"12 遍历Map 13 for( [key,value] of paramMap){ 14 var inputDom = $("<input/>") ; 15 inputDom.attr("name"16 inputDom.attr("value"17 .form.append(inputDom); 18 19 必须要放入body里面,不然请求发不出去 20 var bodyDom = $("body"21 bodyDom.append(.form); 22 .sendMessage(); 23 24 发送完后销毁 25 .destroy(); 26 27 28 QueryForm.create = 29 QueryForm(url,paramMap); 30 31 32 QueryForm.prototype.sendMessage = 33 .form.submit(); 34 35 36 QueryForm.prototype.destroy = 37 .form.remove(); 38 }
后端交互类
普通的Controller,在@RequestMapping中填写正确的路径,根据前端传输的数据类型来获取参数(见上文),根据逻辑来确定要不要@ResponseBody来返回信息,还是ModelAndView来进行页面跳转
路径
经常404怎么办,那就是路径有问题。得先明白,java虚拟机运行的不是java文件而是编译后的.calss文件,所以最先检查的是target文件夹下是否有你URL写的这个文件,是否路径正确
平时网上看到的classpath,就是指target下的classes文件夹
顺带提一句,如果是eclipse,右键点击新建,你会发现有几个文件夹可以选
folder:就是普通的文件夹,它和我们window下面使用的文件夹没有任何区别
source folder:文件夹是一种特别的文件夹,如果你用面向对象的思想去看待这个source folder,那么他是folder的一个子集,作为子集,肯定是有folder的所有功能,而且还有自己特别的功能,他的特别之处,就是在source folder下面的java文件都会被编译,编译后的文件会被放在我们设置的某个文件夹下面(一般我们设置成WEB-INF/classes),source folder下面的非java文件会被copy一份放在我们的设置的文件夹下面
package:文件夹也是一种特别的文件夹,他的特别之处在于:他必须存在于source folder下面,上下级通过.来区分,他的路径最后组成了每一个类的包路径名
所以当出现404,请先检查路径,如果是springboot项目,则要遵守一些默认路径规则,比如模板必须放在templates,静态文件必须放在static