Ajax异步交互,及POST方式传参到Servlet的参数获取问题(原生js)
Ajax(原生js)
简介(个人拙见)
Ajax是个啥
不是个新东西,但是个非常重要的东西,从我第一次接触它就觉得它很重要
因为,它是用来做网页的异步刷新,通俗一点,就是刷新网页局部!
这里的刷新当然是向服务端请求新的数据刷新!
所以Ajax可以同服务端交互数据!
这个异步刷新非常实用,以至于到现在像jQuery、Vue.js等都封装有ajax函数,甚至当前最火的前后端分离解耦合也要借助Ajax完成(个人拙见)
为什么可以刷新网页局部就重要呢?
我们通过servlet,jsp使用请求转发、重定向,基本都是以网页整体作为响应,也就是刷新整个网页。
而如果我们刷新整个网页,造成的最严重的后果就是数据的丢失!
难道你希望在一个网页评论区发送一条评论之后,整个网页刷新,请您重新登录吗?
当然,这有解决方法,记录登录数据,啥的,但是麻烦啊!
那么,Ajax难吗?
答案是不难,针对兼容性问题,像jQuery也封装了很实用的ajax函数!
使用
在这之前,先简要的介绍一下GET和POST请求方式的区别:
get方式发送数据是有大小限制的,post方式则不会。(POST传递文件、音频等)
从上面看似乎post请求方式比get请求方式好,但是我们不能只看表象。
get请求方式为表单、超链接的默认方式,它简单,用于传递一些可以透明的参数,是很方便的!
post请求方式相对复杂一点,但是使用起来并不复杂,不过,还是有些坑需要注意的!
比如:
ajax通过POST方式传参到Servlet的参数获取问题,它不能直接通过request.getParameter()方法获取!
后面讨论这个问题。
先看简单的ajax使用方法:
其实整个原生ajax的关键在于:XMLHttpRequest对象(老IE是ActiveXObject)
不管是get还是post,其都是借助该对象完成的!
ajax全称: Asynchronous JavaScript And XML
异步的 JavaScript 和 XML
创建XMLHTTPRequest对象
// 创建XMLRequest对象,通过该对象像服务端发送请求
let xmlReq = null;
if (XMLHttpRequest) {
xmlReq = new XMLHttpRequest();
} else {
// ie 5、6
xmlReq = new ActiveXObject("Microsoft.XMLHTTP");
}
监听该对象的readystatechange
// 这里使用新的事件注册方式,旧方式为:xmlReq.onreadystatechange = function() {...}
xmlReq.addEventListener('readystatechange', () => {
// 这里就是你的处理逻辑了,很关键,一般个人封装ajax我会将该处逻辑抽出,由外部传入
// 其实第一步和第三步都是死的,最多注意一下POST方式的请求头。这里才是最灵活的部分!
if (xmlReq.readyState === 4 && xmlReq.status == 200){
// 正常的获取了响应!
xmlReq.responseText // 文本响应结果
xmlReq.responseXML // XML响应结果
/*
用的比较多的是文本响应结果,因为XML在后端封装会比较麻烦一点。
而文本响应,并不一定是纯文本,一般是封装JSON对象进行字符串文本响应。
然后前台这边将JSON字符串解析为JSON对象,然后就实现了数据的获取!
*/
}
/*
这里还可以有其他逻辑,比如等待响应时的DOM变化,以及请求错误之后的处理。
当然还有自定义网页状态码,然后通过状态码进行 location.href 的网页跳转的
这就需要解释一下上面的判断条件了
首先 readyState 最初为0,每次状态改变都会加1,也就是该事件会被触发4次(只要状态改变就触发)
并不需要知道底层的状态变化过程,我们只需要关心readyState === 4时,就代表正常!
当然如果不为4,则可以进行其他的逻辑了...
然后 status 其实就是网页状态码,200 表示正常,404资源找不到,500服务器内部错误等
所以自定义网页状态码,就需要判断这个属性的值了...
需要格外注意!!!
XMLHttpRequest 对象 有个 state 属性,不要把它当成 status来判断了,
不然你永远也无法进入上面正常的判断语句内了...我就不小心中过招...
*/
});
打开链接,发送数据(POST方式则可能需要指定请求头)
// 默认异步
/*
type: 为请求类型,"GET" 或者 "POST"
url: 为一个连接地址,一般对应servlet,get类型的参数应该以键值对字符串拼接在其后
async:表示为异步或者同步、默认为true,表示异步,false表示同步。
*/
xmlReq.open(type, url, async | true);
// POST方式需要设置请求头,告诉服务端你的数据类型。
// 不过一般会有一个默认的请求头,具体问题,具体分析,针对你的实际内容,合理设置请求头才能避免服务端收不到参数。
if (type === "POST") {
// xmlReq.setRequestHeader("Content-type", "application/x-www-form-urlencoded;");
xmlReq.setRequestHeader("Content-type","text/plain; charset=utf-8");
// 其他的请求头 ... 这个不用刻意记,像什么请求头、响应头啥的查一下就知道了。
// 这种头部信息主要是告诉服务端你的提交数据是表单、还是普通文本、或是其他数据。数据的编码等相关信息。
// 在服务端的,比如response的响应头,主要是告诉客户端浏览器我所响应的内容是网页、还是xml还是其他一些数据,以及你应该通过什么编码来解析,现在一般都是统一utf-8编码。
}
// 发送
if (param) {
// POST请求应该把需要传递的参数,作为send的参数传入
xmlReq.send(param);
} else {
// GET请求则只需要把键值对:name=xxx&sex=xxx等 拼接在url的后面即可(地址栏会显示)
xmlReq.send(null);
}
是不是很简单呢?
就三个步骤,几个注意点,关键逻辑在第二步中,以及后端的数据获取,和响应结果的封装。
POST方式传参到Servlet的参数获取问题
首先,如果你是想获取表单里面input组件的值通过ajax以post请求方式传到servlet,那么首先得阻止表单的默认提交行为,也就是在submit事件上使用preventDefault方法。
【说明】 其实如果是表单数据,可以直接在action中提交到servlet。在method中指定post方式。
表单的action提交post请求,servlet获取参数也可以直接通过request.getParameter(key);方法获取。
对于Ajax的post请求,传递参数,servlet就无法直接通过request.getParameter(key);方法获取了。
下面是一个简单的登录表单,它通过action指定提交。
<!doctype html>
<html lang="zh-CN">
<head>
<!-- 头部内容 -->
</head>
<body>
<!-- 登录表单 默认get方式 -->
<form action="/web2/UserServlet">
账号:<input type="text" name="account" placeholder="请输入账号"/><br/>
密码:<input type="password" name="password" placeholder="请输入密码"/><br/>
<input type="submit" value="登入"/>
</form>
<div><a href="page/register.html">注册</a></div>
</body>
</html>
下面是与之对应的UserServlet的service()方法,通过request.getParameter获取参数。
【注意】 编码问题在请求的过滤器中统一,响应内容也默认统一为HTML网页。
如下:该过滤器,对 /* 也就是全部请求都有效。
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 统一编码
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
// 默认响应网页,以u8编码
resp.setContentType("text/html;charset=utf-8");
chain.doFilter(req, resp);
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("Hello I am UserServlet!后面是你给我的参数:");
response.getWriter().write("request.getParameter: "+request.getParameter("account"));
response.getWriter().write("request.getParameter: "+request.getParameter("password"));
}
如下,响应结果:
当然,post方式一样可以通过request.getParameter方法获取到参数:
现在,我们再来看Ajax的POST请求,传递参数。
html啥的就省略了...
<script>
"use strict";
window.addEventListener('DOMContentLoaded', () => {
const form = document.querySelector("form");
// 阻止表单的默认提交行为
form.addEventListener('submit', (e) => e.preventDefault());
const submit = document.querySelector("input[type='image']");
const tel = document.querySelector("input[name='tel']");
const pwd = document.querySelector("input[name='pwd']");
const uname = document.querySelector("input[name='uname']");
// 自己封装的简易的ajax函数,也就是把具体逻辑、url、param、async通过外部传入了。
submit.addEventListener('click', () => ajax_post("../UserServlet", (xmlReq) => {
// 四次触发,readState === 4 时正常获取到Servlet响应结果
if (xmlReq.readyState === 4 && xmlReq.status === 200){
console.log(xmlReq.responseText);
}
// post参数应该作为send()的参数
}, "uname="+uname.value+"&tel="+tel.value+"&pwd="+pwd.value));
});
</script>
"../UserServlet" + "?uname="+uname.value+"&tel="+tel.value+"&pwd="+pwd.value
而POST方式则参数数据需要和url分离,作为send()方法的参数,你可以把它理解为数据需要打包。
ajax_post函数就不给出了,个人封装的,其实关键也就是最上面使用的三步流程,不难!!!
当然,这里我们的service方法也需要更改了:
response.getWriter().write("request.getParameter: "+request.getParameter("uname"));
response.getWriter().write("request.getParameter: "+request.getParameter("tel"));
response.getWriter().write("request.getParameter: "+request.getParameter("pwd"));
request.getParameter方法获取的参数全是null,拿不到参数!!!
解决方法
下面是service方法,通过请求对象request的字节输入进行IO操作读取!
具体原因好像是,非表单的POST请求不会加入request的参数Map中,所以无法直接通过request.getParameter(key);来获取参数值,但是在输入流中是存在我们所发送到servlet的参数数据的!
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("Hello I am UserServlet!后面是你给我的参数:");
StringBuilder sb = new StringBuilder();
try {
char[] buf = new char[1024];
int len = -1;
while ((len = request.getReader().read(buf)) != -1){
sb.append(buf, 0, len);
}
}catch(Exception e){
e.printStackTrace();
}
System.out.println(sb);
response.getWriter().write(sb.toString());
}
最后,需要注意的就是响应了,一般以JSON字符串作为响应,response对象也可以设置响应类型为JSON(我们上面的过滤器设置默认响应为html网页,当然根据需要后续可以更改设置resp的响应头)。
然后对于把数据拼成JSON格式的字符串,可以自己手动拼接,当然也有比较好用的工具,比如Google的Gson,阿里的JSONObject等。
我们后续一一体验使用。