vlambda博客
学习文章列表

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方式传参到Servlet的参数获取问题(原生js)


现在,我们再来看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等。

我们后续一一体验使用。