关于http请求头Content-Type的基本知识

1、背景

最近在使用一个okhttp网络框架时,在拦截器中打印post数据发现数据均经过了编码,打印出来的是类似如下的字符

loginName%22%3A%22hello14%22%2C%22mymac%22%3B%2294%3Abe%3A46%3A15%3A15%3A20%22%2C%22pId%22%3A%225602923243413443840%22%2C%22

对比原数据发现,只有其中部分数据被编码过了。 为什么会这样?

2、Content-Type与数据编码

后来查看其源码发现有如下代码:

requestContentType = "application/x-www-form-urlencoded; charset=UTF-8"
if (Util.isNotEmptyMap(apiRequest.getFormParams())) {
    requestBody = RequestBody.create(MediaType.parse(requestContentType), buildParamString(apiRequest.getFormParams()));
}

private static String buildParamString(Map<String, String> params) {
    StringBuilder res = new StringBuilder();

    if (null != params && params.size() > 0) {    
        String key;
        try {
            // 改写一下
            Iterator<String> it = params.keySet().iterator();
            while (it.hasNext()) {
                key = (String)it.next();
                res.append(key).append("=").append(URLEncoder.encode((String)params.get(key), "UTF-8"));
                res.append("&");
            }
            
        } catch (UnsupportedEncodingException ex) {
            
        }
    }
    
    int len = res.toString().length();
    return res.toString().substring(0, len - 1);
}

代码中指定了Content-Type类型为 application/x-www-form-urlencoded。
上面的代码主要作用是将参数用key=value&的方式连接,其中value经过了URLEncoder.encode()方法进行编码。
URLEncoder.encode()方法的说明如下:

Translates a string into {@code application/x-www-form-urlencoded}
format using a specific encoding scheme. This method uses the
supplied encoding scheme to obtain the bytes for unsafe
characters.<p> <em><strong>Note:</strong> The <a href= "http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars"> World Wide Web Consortium Recommendation</a> states that
UTF-8 should be used. Not doing so may introduce
incompatibilities.</em>

该方法说明了会将字符串转化成application/x-www-form-urlencoded格式的。所以上面的编码是因为设置了http的Content-Type请求头而进行的格式转换。
在以表单形式提交数据时,默认的数据格式是 application/x-www-form-urlencoded 。

3、关于URL编码

主要的规则是将非ascii码编码为ascii码。具体的编码规则参见维基百科以及阮一峰的《关于URL编码》
部分引用如下:

当HTML表单中的数据被提交时,表单的域名与值被编码并通过HTTP的GET或者POST方法甚至更古远的email[2]把请求发送给服务器。这里的编码方法采用了一个非常早期的通用的URI百分号编码方法,并且有很多小的修改如换行规范化以及把空格符的编码"%20"替换为"+" 。按这套方法编码的数据的MIME类型是application/x-www-form-urlencoded,当前仍用于(虽然非常过时了)HTML与XForms规范中。此外,CGI规范包括了web服务器如何解码这类数据、利用这类数据的内容。
如果发送的是HTTP GET请求,application/x-www-form-urlencoded数据包含在所请求URI的查询成分中。如果发送的是HTTP POST请求或通过email,数据被放置在消息体中,媒体类型的名字被包含在消息的Content-Type头内部。

4、Okhttp的FormBody提交方式分析

在okhttp中提供了FormBody类来提交表单数据。 基本用法如下:

public void testPostFormData() {
    // 先定义请求体,是FormBody类型
    RequestBody body = new FormBody.Builder().add("name", "张三丰")
            .add("age", "99")
            .build();

    Request request = new Request.Builder()
            .url("http://httpbin.org/post")
            .post(body)
            .build();

    mOkHttpClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(@NotNull Call call, @NotNull IOException e) {
            Log.i(TAG, "onFailure: " + e.getMessage());
        }

        @Override
        public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
            Log.i(TAG, "onResponse: " + response.body().string());
        }
    });
}

在FormBody类中定义了Content-Type,如下图:
formbody-content-type
既然申明了数据类型,那么提交的数据就要转化为URL编码的。
继续查看FormBody.Builder()类的add方法源码,果然将数据进行了URL编码,如下:

// 其中在String上的扩展函数canonicalize实现了url规则编码
fun add(name: String, value: String) = apply {
  names += name.canonicalize(
      encodeSet = FORM_ENCODE_SET,
      plusIsSpace = true,
      charset = charset
  )
  values += value.canonicalize(
      encodeSet = FORM_ENCODE_SET,
      plusIsSpace = true,
      charset = charset
  )
}

故可以看出,okhttp的FormBody的Content-Type是application/x-www-form-urlencoded 且数据经过了URL编码。只是okhttp中的编码没有使用java中的URLEncoder类,而是框架自已处理的。

5、http的Content-Type类型

对于每个http请求都会有一个Content-Type,来告知服务端数据的类型。通过打印请求头我们也可以发现这一点。这个是http协议的规范内容。但是一般Content-Type的可取值有哪些呢?
根据数据类型的不同,Content-Type的取值也不同。 如提交一个纯文本文件时,值为text/plain。而提交图片时,则是image/jpeg等。
从该博客摘取部分类型如下,具体可以参考此篇文章
在这里插入图片描述
在这里插入图片描述

6、常见的提交方式

我们可以结合使用postman和charles查看不同提交方式的请求头及请求体相关的信息来加深理解。 在postman中提供了多种方式提交数据,如下:
在这里插入图片描述
如果选中以x-www-form-urlencoded方式提交数据,那么在header列会自动设置成 application/x-www-form-urlencoded。 如下:
postman-header
使用 application/x-www-form-urlencoded 提交数据时请求信息分别如下:
x-www-form-urlencode方式提交
通过修改提交类型,可以方便查看到不同的Content-Type类型。

7、参考

感谢作者分享。
1、http字段头参考
2、post使用form-data和x-www-form-urlencoded的本质区别
3、四种常见的 POST 提交数据方式
4、stackoverflow

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值