AJAX day2--form 表单 & 文件上传

762 阅读10分钟

1、form表单

1.1 表单的三个组成部分

网页中采集数据的表单由三个部分组成,分别是:表单标签、表单域、表单按钮。

1.1.1 表单的三个组成部分 - 表单标签

  • HTML 的 就是表单标签,它是一个“容器”,用来将页面上指定的区域划定为表单区域。 image.png

1.1.2 表单的三个组成部分 - 表单域

  • 表单域提供了采集用户信息的渠道,常见的表单域有:input、textarea、select等。 image.png

1.1.3 表单的三个组成部分 - 表单按钮

  • 当表单数据填写完毕后,用户点击表单按钮,会触发表单的提交操作,从而把采集到的数据提交给服务器。

image.png

1.2 form标签的属性

  • form 标签最重要的 3 个属性分别是 action、method 和 enctype。
属性可选值说明
action接口的 url 地址把表单采集到的数据,提交到哪个接口中
methodGET 或 POST数据的提交方式(默认值为 GET)
enctypeapplication/x-www-form-urlencoded multipart/form-data text/plain(很少用)数据的编码格式。具体指的是:把表单数据提交给服务器之前,如何对将要提交的数据进行编码 (默认值 application/x-www-form-urlencoded)

1.3 以 GET 方式提交表单数据

  • 在 form 标签上,通过 action 属性指定提交的 URL 地址,通过 method 属性指定提交的方式为 GET:

image.png

注意:由于method属性的默认值就是GET,因此上述的method="GET" 可以被省略!

1.4 以 POST 方式提交表单数据

  • 在 form 标签上,通过 action 属性指定提交的 URL 地址,通过 method 属性指定提交的方式为 POST,并通过 enctype 属性指定数据的编码方式为 application/x-www-form-urlencoded

image.png

注意:由于 enctype 的默认值就是 application/x-www-form-urlencoded,因此上述的 enctype 可以被省略!

1.5 表单提交的问题

  • form 表单既负责采集数据,又负责把数据提交到服务器!表单的默认提交行为会导致页面的跳转。

解决方案:

  1. form 表单只负责采集数据;
  2. Ajax 负责将数据提交到服务器。(符合:职能单一的原则)

1.6 通过 Ajax 提交表单数据

  • 通过 Ajax 提交表单采集到的数据,可以防止表单默认提交行为导致的页面跳转问题,提高用户的体验。

      1. 监听表单的 submit 提交事件
      1. 阻止默认提交行为
      1. 基于 axios 发起请求
      1. 指定请求方式、请求地址
      1. 指定请求体数据
axios({
          url: 'http://www.itcbc.com:3006/api/addbook',
          method: 'post',
          data: {
            bookname: bookname.value,
            author: author.value,
            publisher: publisher.value
          }
        }).then(result => {
        })
})
复制代码

1.7 jQuery 的 serialize() 函数

  • jQuery 的 serialize() 函数能够一次性获取到表单中采集的数据

    • 1、作用:收集指定表单中有name属性的表单元素的value值

    • 2、生成的格式为:key=value&key=value...

    • 3、问题:

      • 3.1 jq的方法,需要引入外部资源
      • 3.2 jq元素获取文件数据
    • 4、细节:name属性的值 需要和后台接口的参数对应

    • 5、其他特点:

      • 5.1 该方法 能够 获取 隐藏域的值
      • 5.2 该方法不能得到禁用状态的值
      • 5.3 该方法不能得到文件域中的文件信息,所以不能完成文件上传
  • 语法格式如下:

$('表单元素的选择器').serialize();
复制代码
  • 示例代码如下:
$('form').on('submit', function (e) {
    e.preventDefault()
    axios({
        method: 'POST',
        url: 'http://www.itcbc.com:3006/api/form',
        data: $('form').serialize()
    }).then(result => {
        console.log(result.data)
    })
})

2、axios 请求方法的别名

在实际开发中,常用的 5 种请求方式分别是: GET、POST、PUT、PATCH、DELETE

  • 为了简化开发者的使用过程,axios 为所有支持的请求方法提供了别名:
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])

image.png

2.1 axios.get() 的用法

  • 使用 axios.get() 可以方便快捷地发起 GET 请求:
<body>
    <input type="text">
    <button class="all">查询全部数据</button>
    <button class="search">查询指定名称的数据</button>
    <script>
      let all = document.querySelector('.all')
      let search = document.querySelector('.search')
      let input = document.querySelector('input')
      all.addEventListener('click',function(){
        // 不带查询参数
        axios.get(`http://www.itcbc.com:3006/api/getbooks`).then(res => {
          console.log(res);
        })
      })
      search.addEventListener('click',function(){
        let v = input.value
        // 带查询参数
        // 在url中拼接参数
        /* axios
        .get(`http://www.itcbc.com:3006/api/getbooks?bookname=${v}`)
        .then(res => {
          console.log(res);
        }) */
        // 通过params传递参数
        axios
        .get('http://www.itcbc.com:3006/api/getbooks',{
          params: { bookname: v }
        })
        .then(res =>{
          console.log(res)
        })
      })
    </script>
</body>

2.2 axios.post() 的用法

  • 使用 axios.post() 可以方便快捷地发起 POST 请求:
// 不带请求体
axios.post('http://www.itcbc.com:3006/api/post').then(result => {
    console.log(result);
})
// 带请求体
axios.post('http://www.itcbc.com:3006/api/post', {
    username: 'zhangsan',
    password: '123456'
}).then(result => {
    console.log(result);
}

3、axios 全局配置 & 拦截器

3.1 全局配置请求根路径

  • 3.1.1 在 url 地址中,协议://域名:端口 对应的部分叫做“请求根路径”。

  • 3.1.2 全局配置请求根路径的好处:

    • 提高项目的可维护性。全局配置根路径后,后续所有的请求都可以使用全局配置的根路径
  • 3.1.3 假设:端口号从 3009 变成了 3006

    • 没有全局配置根路径,则每个请求的URL中的端口号都需要被修改!
    • 有全局配置根路径,则只需要修改全局配置即可

3.2 全局配置请求根路径 - 语法格式

基于 axios 提供的固定配置,即可轻松配置请求的根路径。语法格式如下:axios.defaults.baseURL = '请求根路径'

image.png

3.3 什么是拦截器

  • 拦截器(interceptors)用来全局拦截 axios 的每一次请求与响应。
  • 好处:可以把每个请求中,某些重复性的业务代码封装到拦截器中,提高代码的复用性。

image.png

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  img.style.display = 'block'
  return config;
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  img.style.display = 'none'
  return response;
}, function (error) {
  // 对响应错误做点什么
  img.style.display = 'block'
  return Promise.reject(error);
});

4、FormData

4.1 FormData 介绍

  • 4.1.1 FormData 作用

    • 收集常规的表单元素的数据
    • 收集文件数据,只有formdata可以实现
  • 4.1.2 axios所支持的参数格式

    • key=value&key=value
    • {key:value,key1:value1}
    • formdata
  • 4.1.3 收集常规表单元素的数据

    • 要求表单元素一定要有name属性,name属性的值就是参数的键名

    • 收集 过程:

      • 获取表单dom对象
      • let formdata = new FormData(表单dom对象)
  • 4.1.4 重点说明

    • 理论上axios支持传递formdata
    • 但是后台往往不会以formdata参数格式进行常规的业务处理,后台只有在有文件数据参与的时候接收formdata数据
    • 结论:能不能传递formdata由后台接口的处理方式来决定

4.2 FormData的API方法

  • 在提交数据前,可以使用下列API方法对数据进行查看和修改
append('key', 'value'); -- 向对象中追加数据
set('key', 'value');    -- 修改对象中的数据
delete ('key');         -- 从对象中删除数据
get('key')              -- 获取指定key的一项数据
getAll('key')           -- 获取指定key的全部数据
forEach()               -- 遍历对象中的数据
复制代码

4.3 FormData和serialize的区别

  • 共同点:

    • 都需要设置表单各项的name属性。
    • 都能快速收集表单数据
    • 都能够获取到隐藏域(<input type="hidden" />)的值
    • 都不能获取禁用状态(disabled)的值
  • 不同点:

    • FormData属于原生的代码;serialiaze是jQuery封装的方法
    • FormData可以收集文件域(<input type="file"/>)的值,而serialize不能。如果有文件上传,则必须使用FormData。
    • 得到的结果的数据类型不一样(知道即可)

4.4 FormData 实现头像上传

  • 主要的实现步骤:

    • 使用文件选择器选择图片文件
    • 把用户选择的文件存入 FormData 对象
    • 使用 axios 把 FormData 发送给服务器
    • 模拟文件选择器的点击事件
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>案例-头像上传</title>
    <link rel="stylesheet" href="./lib/bootstrap-v4.6.0.css" />
    <style>
      .thumb-box {
        text-align: center;
        margin-top: 50px;
      }

      .thumb {
        width: 250px;
        height: 250px;
        object-fit: cover;
        border-radius: 50%;
      }
    </style>
  </head>

  <body>
    <div class="thumb-box">
      <!-- 头像 -->
      <img src="./images/cover.jpg" class="img-thumbnail thumb" alt="" />
      <div class="mt-2">
        <!-- 文件选择框 -->
        <!-- accept 属性表示可选择的文件类型 -->
        <!-- image/* 表示只允许选择图片类型的文件 -->
        <form action="">
          <input name="avatar" type="file" id="iptFile" accept="image/*" />
        </form>
      </div>
    </div>
    <script src="./lib/jquery-v3.6.0.js"></script>
    <script src="./lib/axios.js"></script>
    <script src="../utils/test.js"></script>
    <script>
      let iptFile = document.querySelector('#iptFile')
      // 当用户选择完文件之后触发事件
      iptFile.addEventListener('change',function(){
        // 使用axios结合formdata实现文件上传
        let form = document.querySelector('form')
        let formdata = new FormData(form)
        console.log(formdata.get('avatar'));
        axios({
          method: 'post',
          url: '/api/formdata',
          data: formdata
        }).then(res => {
          console.log(res);
        })
      })
    </script>
  </body>
</html>

5、图片预览

5.1 本地预览

  • 图片并不是单独进行上传的,意思就是说用户选择图片之后,并没有进行图片的上传,这个时候的预览和服务器是没有任何关系
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>案例-头像上传</title>
    <link rel="stylesheet" href="./lib/bootstrap-v4.6.0.css" />
    <style>
      .thumb-box {
        text-align: center;
        margin-top: 50px;
      }

      .thumb {
        width: 250px;
        height: 250px;
        object-fit: cover;
        border-radius: 50%;
      }
    </style>
  </head>

  <body>
    <div class="thumb-box">
      <!-- 头像 -->
      <img src="./images/cover.jpg" class="img-thumbnail thumb" alt="" />
      <div class="mt-2">
        <!-- 文件选择框 -->
        <!-- accept 属性表示可选择的文件类型 -->
        <!-- image/* 表示只允许选择图片类型的文件 -->
        <form action="">
          <!-- 真正的页面中,可能不止只有文件域,还有其它的表单元素 -->
          <input type="text" name="username" />
          <input type="text" name="password" />
          <input
            name="avatar"
            type="file"
            id="iptFile"
            accept="image/png,image/jpeg"
          />
        </form>
      </div>
    </div>
    <script src="./lib/axios.js"></script>
    <script src="../utils/test.js"></script>
    <script>
      let iptFile = document.querySelector('#iptFile')
      let form = document.querySelector('form')
      let thumb = document.querySelector('.thumb')
      // 当用户选择完文件之后触发事件
      iptFile.addEventListener('change',function(){
        // 使用axios结合formdata实现文件上传
        let formdata = new FormData(form)

        // 实现本地预览:加载本地图片的数据进行渲染
        // 1.获取用户所选择的文件对象
        // files:当前用户所选择的文件列表
        let myfile = iptFile.files[0]
        // 2.根据用户选择的文件对象生成一个路径并返回
        let myurl = URL.createObjectURL(myfile)
        // 3.将地址赋值给img标签的src属性
        thumb.src = myurl
        console.log(formdata);
         
        console.log(formdata.get('avatar'));
        axios({
          method: 'post',
          url: '/api/formdata',
          data: formdata
        }).then(res => {
          console.log(res);
          // 直接通过返回的formdata对象中的filename属性值赋值给img标签的src属性,实现头像的上传
          // 缺点:需要等待响应时间
          // thumb.src = res.data.data.filename
        })
      })
    </script>
  </body>
</html>

6、文件上传

  • 只要提交的数据中有文件,则必须使用formdata收集数据进行传递

  • 将所有数据(包含文件)统一上传

    let form = document.querySelector('form')
    let formdata = new FormData(form)
    复制代码
    
    • 实现图片的本地预览

      • 获取用户所选择的文件对象
      file表单元素.files[0]
      复制代码
      
      • 根据文件对象生成一个托管路径
      let url = URL.createObjectUrl(file对象)
      复制代码
      
      • 将路径赋值给img的src属性
      img.src = url
      复制代码
      
      • 为什么要这么做???

        • 1、没有上传给服务器,所以也没有服务器返回路径
        • 2、我们也拿不到当前用户所选择的文件路径
        • 3、所以我们只能根据文件对象生成一个路径,并赋值给img的src属性
<script>
let iptFile = document.querySelector('#iptFile')
let thumb = document.querySelector('.thumb')
// 当用户选择完文件之后触发事件
iptFile.addEventListener('change', function() {
    // 使用axios结合formdata实现文件上传
    let form = document.querySelector('form')
    let formdata = new FormData(form)

    // 实现本地预览:加载本地图片的数据进行渲染
    // 1.获取用户所选择的文件对象
    // files:当前用户所选择的文件列表
    let myfile = iptFile.files[0]
    // 2.根据用户所选择的文件对象生成一个路径并返回
    let myurl = URL.createObjectURL(myfile)
    // 3.将地址赋值给img标签的src属性
    thumb.src = myurl
})
</script>
复制代码
  • 只上传文件数据

    • 基本步骤

      • 1、获取文件对象

      • 2、创建formdata

      • 3、将文件对象做为参数追加到formdata中

        • formdata.append('后台接口规定的key',用户所选择文件对象)
      • 4、将formdata做为参数传递给后台服务器

    • 预览

      • 将后台的返回路径赋值给img的src属性
<script>
let iptFile = document.querySelector('#iptFile')
  // 当用户选择完文件之后触发事件
iptFile.addEventListener('change', function() {
    // 使用axios结合formdata实现文件上传
    let formdata = new FormData()
    // 追加文件参数,可以追加任意的参数
    formdata.append('avatar', iptFile.files[0])
    axios({
      url: '/api/formdata',
      method: 'post',
      data: formdata
    }).then(res => {
      console.log(res)
    })
})
</script>

7.请求报文 & 响应报文

  • 客户端与服务器通信的过程是基于请求与响应的。其中:
    • 请求报文规定了客户端以什么格式把数据发送给服务器
    • 响应报文规定了服务器以什么格式把数据响应给客户端

7.1 请求报文 - 格式

请求报文由请求行(request line)、请求头部( header )、空行 和 请求体 4 个部分组成。图示如下:

image.png

注意:

在浏览器中,GET 请求比较特殊,它只有请求头,没有请求体。

在浏览器中,POST、PUT、PATCH、DELETE 请求既有请求头,又有请求体。

7.2 响应报文 - 格式

响应报文由状态行、响应头部、空行 和 响应体 4 个部分组成。图示如下:

image.png

7.3 URL参数

  • 常用的5种请求方式,都可以在URL后面携带请求参数。
  • 由于URL的长度有限制,所以请求参数一般都比较小,比如不能做文件上传
  • 常用的请求参数有两种写法
    • /api/xxx?参数=值&参数=值 (这种格式的字符串叫做查询字符串,所以这样的参数叫做查询参数)
    • /api/xxx/值/值 (Restful 风格的接口用这种格式的参数)

axios中 如何携带不同格式的请求参数 具体用哪种格式得看接口文档

第一种格式的参数:(/api/xxx?参数=值&参数=值)

image.png

第二种格式的参数:( /api/xxx/值/值)

image.png

7.4 请求体

  • 除GET请求以外,其他4种常用的请求方式,都可以设置请求体。
  • 请求体的大小没有限制,所以可以提交大量的数据
  • 常用的请求体格式有如下三种:
    • 参数=值&参数=值 (查询字符串格式)
    • '{ "id": 1, "name": "zs" }' (JSON格式)
    • new FormData() (FormData对象格式)

axios中,如何设置不同格式的请求体

  • 第一种格式:参数=值&参数=值

image.png

  • 第二种格式: '{ "id": 1, "name": "zs" }'

image.png

  • 第三种格式: new FormData()

image.png

请求的时候,设置了不同格式的请求体,需要一个对应的请求头

  • 第一种格式:参数=值&参数=值
    • Content-Type: application/x-www-form-urlencoded
  • 第二种格式: '{ "id": 1, "name": "zs" }'
    • Content-Type: application/json
  • 第三种格式: new FormData()
    • Content-Type: multipart/form-data; xxsd随机字符

7.5 http 响应状态码

概念:http 响应状态码(Status Code)由三位数字组成,用来标识响应成功与否的状态。

作用:客户端浏览器根据响应状态码,即可判断出这次 http 请求是成功还是失败了。

image.png

7.6 常见的 http 响应状态码

状态码状态码描述说明
200OK请求成功。
201Created资源在服务器端已成功创建。
304Not Modified资源在客户端被缓存,响应体中不包含任何资源内容!
400Bad Request客户端的请求方式、或请求参数有误导致的请求失败!
401Unauthorized客户端的用户身份认证未通过,导致的此次请求失败!
404Not Found客户端请求的资源地址错误,导致服务器无法找到资源!
500Internal Server Error服务器内部错误,导致的本次请求失败!

7.7 http 响应状态码 Vs 业务状态码

正确区分响应状态码和业务状态码的不同,是保证使用 Ajax 不迷茫的必要前提。从如下 3 个方面进行区分:1.所处的位置 2.表示的结果 3.通用性

1.所处的位置不同:

  • 在响应头的状态行中所包含的状态码,或者请求列表中的Status,叫做“响应状态码”

image.png

  • 在响应体的数据中所包含的状态码(案例中叫做code),叫做“业务状态码”

image.png

表示的结果不同:

  • 响应状态码只能表示这次请求的成功与否(成功地失败了)
  • 业务状态码用来表示这次业务处理的成功与否

通用性不同:

  • 响应状态码是由 http 协议规定的,具有通用性。每个不同的状态码都有其标准的含义,不能乱用。
  • 业务状态码是后端程序员自定义的,不具有通用性。

image.png