zoukankan      html  css  js  c++  java
  • Post请求的两种编码格式:application/x-www-form-urlencoded和multipart/form-data【转】


    在常见业务开发中,POST请求常常在这些地方使用:前端表单提交时、调用接口代码时和使用Postman测试接口时。我们下面来一一了解:

    一、前端表单提交时

    application/x-www-form-urlencoded

    表单代码:

    <form action="http://localhost:8888/task/" method="POST">
    First name: <input type="text" name="firstName" value="Mickey&"><br>
    Last name: <input type="text" name="lastName" value="Mouse "><br>
    <input type="submit" value="提交">
    </form>
    

    通过测试发现可以正常访问接口,在Chrome的开发者工具中可以看出,表单上传编码格式为application/x-www-form-urlencoded(Request Headers中),参数的格式为key=value&key=value

    我们可以看出,服务器知道参数用符号&间隔,如果参数值中需要&,则必须对其进行编码。编码格式就是application/x-www-form-urlencoded将键值对的参数用&连接起来,如果有空格,将空格转换为+加号;有特殊符号,将特殊符号转换为ASCII HEX)。

    application/x-www-form-urlencoded是浏览器默认的编码格式。对于Get请求,是将参数转换?key=value&key=value格式,连接到url后

    ps:可以在这个网址测试表单:http://www.runoob.com/try/try.php?filename=tryhtml_form_submit

    multipart/form-data

    那么当服务器使用multipart/form-data接收POST请求时,服务器怎么知道每个参数的开始位置和结束位置呢?

    <form action="http://localhost:8888/task/" method="POST" enctype="multipart/form-data">
    First name: <input type="text" name="firstName" value="Mickey&"><br>
    Last name: <input type="text" name="lastName" value="Mouse "><br>
    <input type="submit" value="提交">
    </form>
    

    我们在开发者工具中可以看出multipart/form-data不会对参数编码,使用的boundary(分割线),相当于&boundary的值是----Web**AJv3

    文件上传

    上传文件也要指定编码格式为multipart/form-data

    <form action="http://localhost:8888/testFile" enctype="multipart/form-data" method="POST">
    <input type="file" name="file">
    <input type="submit" value="提交">
    </form>
    

    如果是SpringMVC项目,要服务器能接受multipart/form-data类型参数,还要在spring上下文配置以下内容,SpringBoot项目则不需要

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="utf-8"></property>
    </bean>
    

    我们可以通过FormData对象模拟表单提交,用原始的XMLHttpRequest来发送数据,让我们可以在Chrome开发工具中查看到具体格式:

    <form id="form">
        First name: <input type="text" name="firstName" value="Mickey"><br>
        Last name: <input type="text" name="lastName" value="Mouse"><br>
        <input type="file" name="file"><br>
    </form>
    
    <button onclick="submitForm()">提交</button>
    
    <script>
        function submitForm() {
            var formElement = document.getElementById("form");
    
            var xhr = new XMLHttpRequest();
            xhr.open("POST", "/task/testFile");
            xhr.send(new FormData(formElement));
        }
    </script>
    

    二、调用接口代码时

    1、在代码中使用application/x-www-form-urlencoded编码格式设置Request属性调用接口,可以如下实现:

    private static String doPost(String strUrl, String content) {
            String result = "";
    
            try {
                URL url = new URL(strUrl);
                //通过调用url.openConnection()来获得一个新的URLConnection对象,并且将其结果强制转换为HttpURLConnection.
                HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("POST");
                //设置连接的超时值为30000毫秒,超时将抛出SocketTimeoutException异常
                urlConnection.setConnectTimeout(30000);
                //设置读取的超时值为30000毫秒,超时将抛出SocketTimeoutException异常
                urlConnection.setReadTimeout(30000);
                //将url连接用于输出,这样才能使用getOutputStream()。getOutputStream()返回的输出流用于传输数据
                urlConnection.setDoOutput(true);
                //设置通用请求属性为默认浏览器编码类型
                urlConnection.setRequestProperty("content-type", "application/x-www-form-urlencoded");
                //getOutputStream()返回的输出流,用于写入参数数据。
                OutputStream outputStream = urlConnection.getOutputStream();
                outputStream.write(content.getBytes());
                outputStream.flush();
                outputStream.close();
                //此时将调用接口方法。getInputStream()返回的输入流可以读取返回的数据。
                InputStream inputStream = urlConnection.getInputStream();
                byte[] data = new byte[1024];
                StringBuilder sb = new StringBuilder();
                //inputStream每次就会将读取1024个byte到data中,当inputSteam中没有数据时,inputStream.read(data)值为-1
                while (inputStream.read(data) != -1) {
                    String s = new String(data, Charset.forName("utf-8"));
                    sb.append(s);
                }
                result = sb.toString();
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return result;
        }
    
        public static void main(String[] args) {
            String str = doPost("http://localhost:8888/task/", "firstName=Mickey%26&lastName=Mouse ");
            System.out.println(str);
        }
    

    2、在代码中使用multipart/form-data编码格式设置Request属性调用接口时,其中boundary的值可以在设置Content-Type时指定,让服务器知道如何拆分它接受的参数。通过以下代码的调用接口:

    private static String doPost(String strUrl, Map<String, String> params, String boundary) {
      String result = "";
    
      try {
          URL url = new URL(strUrl);
          HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
          urlConnection.setRequestMethod("POST");
          urlConnection.setConnectTimeout(30000);
          urlConnection.setReadTimeout(30000);
          urlConnection.setDoOutput(true);
          //设置通用请求属性为multipart/form-data
          urlConnection.setRequestProperty("content-type", "multipart/form-data;boundary=" + boundary);
          DataOutputStream dataOutputStream = new DataOutputStream(urlConnection.getOutputStream());
    
          for (String key : params.keySet()) {
              String value = params.get(key);
              //注意!此处是
    (回车:将当前位置移到本行开头)、
    (换行:将当前位置移到下行开头)要一起使用
              dataOutputStream.writeBytes("--" + boundary + "
    ");
              dataOutputStream.writeBytes("Content-Disposition: form-data; name="" + encode(key) + ""
    ");
              dataOutputStream.writeBytes("
    ");
              dataOutputStream.writeBytes(encode(value) + "
    ");
          }
          //最后一个分隔符的结尾后面要跟"--"
          dataOutputStream.writeBytes("--" + boundary + "--");
          dataOutputStream.flush();
          dataOutputStream.close();
          InputStream inputStream = urlConnection.getInputStream();
          byte[] data = new byte[1024];
          StringBuilder sb = new StringBuilder();
          while (inputStream.read(data) != -1) {
              String s = new String(data, Charset.forName("utf-8"));
              sb.append(s);
          }
          result = sb.toString();
          inputStream.close();
      } catch (Exception e) {
          e.printStackTrace();
      }
    
      return result;
    }
    
    private static String encode(String value) throws UnsupportedEncodingException {
      return URLEncoder.encode(value, "UTF-8");
    }
    
    public static void main(String[] args) {
      Map<String, String> params = new HashMap<>();
      params.put("firstName", "Mickey");
      params.put("lastName", "Mouse");
    
      //自定义boundary,有两个要求:使用不会出现在发送到服务器的HTTP数据中的值;并在请求消息中的分割位置都使用相同的值
      String boundary = "abcdefg";
      String str = doPost("http://localhost:8888/testFile", params, boundary);
      System.out.println(str);
    }
    

    通过debug,可以看出dataOutputStream的值如下:

    三、使用Postman测试接口时

    1、POST请求 -> Body -> x-www-form-urlencoded

    当切换为x-www-form-urlencoded时,Headers会自动添加Content-Type:application/x-www-form-urlencoded

    当请求Send后,此时点Code,可以查看到和Chrome开发工具中(Request Headers处的Content-Type和Form Data)一样的数据

    2、POST请求 -> Body -> form-data

    相当于html表单请求,value可为Text或文件。

    可以不用手动指定编码格式,也可以指定编码为multipart/form-data

    划线处的分割线应该是被省略了。

    可以更改左上角的类型,来查看相应的Headers代码,常见的是下面三种:

    Java OK HTTP

    JavaScript Jquery AJAX

    JavaScript XHR

    总结

    POST请求的两种编码格式:application/x-www-urlencoded是浏览器默认的编码格式,用于键值对参数,参数之间用&间隔;multipart/form-data常用于文件等二进制,也可用于键值对参数,最后连接成一串字符传输(参考Java OK HTTP)。除了这两个编码格式,还有application/json也经常使用。

    接口代码

    @RequestMapping("/task")
    public class TaskController {
    
       @RequestMapping("/")
       public String index(String firstName, String lastName) {
           return firstName + lastName;
       }
    
       @RequestMapping("/testFile")
       public String testFile(String firstName, String lastName, MultipartFile file) {
           String result = firstName + lastName;
           if (file != null) {
               result += (file.getOriginalFilename() != null) ? file.getOriginalFilename() : "";
           }
           return result;
       }
    }
    

    参考网址

    HTML <form> 标签的 enctype 属性:http://www.w3school.com.cn/tags/att_form_enctype.asp
    
    HttpUrlConnection 基础使用:https://www.cnblogs.com/libertycode/p/5979260.html
    
    SpringMvc接收multipart/form-data 传输的数据 及 PostMan各类数据类型的区别:https://www.cnblogs.com/ifindu-san/p/8251370.html#undefined
    
    What is the boundary in multipart/form-data?:https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data
    

    文章转自:https://www.jianshu.com/p/53b5bd0f1d44

  • 相关阅读:
    python: 第三方时间库 arrow
    PyQt5程序打包的2种方式
    python:多任务(线程、进程、协程)
    python:网络编程(udp 和 tcp)
    python:使用matplotlib画图时,中文乱码的问题
    python:浅拷贝和深拷贝
    使用scrapy编写爬虫:爬取豆瓣Top250读书的评论
    爬虫小案例:多协程工作
    selenium:指挥浏览器工作
    爬虫小案例:联想词汇搜索
  • 原文地址:https://www.cnblogs.com/KillBugMe/p/13115919.html
Copyright © 2011-2022 走看看