【Http】关于Http请求头中的Content-type问题

问题描述:

    在Android中使用OkHttp向服务端post一个json时,服务端一直返回无法获取到参数。

具体表现:

    前两天参加一个移动互联网的比赛的时候,在代码中使用OkHttp向局域网内的服务器发送一个post请求时,服务器一直返回无法获取到参数。Android端中请求的代码如下:

        OkHttpClient client = new OkHttpClient();
        RequestBody body = new FormBody.Builder()
                .add("a", "a")
                .add("b", "1")
                .build();
        Request request = new Request.Builder()
                .url("http://...")
                .post(body)
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                ...
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                ...
            }
        });

由于技术比较菜,所以一直以来OkHttp发送json的时候是用FormBody来做的,而且在之前的与服务器的交互中都没有出现过类似的问题。所以在比赛的时候突然遇到,一直以为是服务端的原因。后来比赛完后,我的指导老师帮我分析时提到了:Content-type这个请求头的问题,我才心里有点清楚大概是什么原因。

通过查看FormBody的源码的时候发现,在其第一行就很明显的写明了Content-type的值:

public final class FormBody extends RequestBody {
  private static final MediaType CONTENT_TYPE = MediaType.get("application/x-www-form-urlencoded");

  private final List<String> encodedNames;
  private final List<String> encodedValues;

  FormBody(List<String> encodedNames, List<String> encodedValues) {
    this.encodedNames = Util.immutableList(encodedNames);
    this.encodedValues = Util.immutableList(encodedValues);
  }
  ...
}

那么"application/x-www-form-urlencoded"和"application/json"具体在服务端会出现什么问题呢?这里我用php来展示一下。

首先是"application/x-www-form-urlencoded":

    即Android端用FormBody的形式来传值,那么在php端如果需要获取对应的值,则需要这样子写:

if (isset($_POST['a'])) {
    $a = $_POST['a'];
} else {
    $a = "not found";
}
echo $a;

可以看到,这里直接用$_POST[键名]的形式就可以获取到由OkHttp发送来的值了。

如果是"application/json":

    在Android端的代码如下:

        OkHttpClient client = new OkHttpClient();
        JSONObject object = new JSONObject();
        try {
            object.put("a", "a");
            object.put("b", "b");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        RequestBody body = RequestBody.create(MediaType.parse("application/json"), object.toString());
        Request request = new Request.Builder()
                .url("https://...")
                .post(body)
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
               ...
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                ...
            }
        });

可以看到,这里的RequestBody使用了create的方式来创建,其中特意指明了Content-type为"application/json"的形式,然后对应所要传的值则用toString()的方法将json对象转换成json字符串的形式了。

那么如果是这样子的传值方式,在php端又如何获取到对应的值呢?其实原理很简单,只需要拿到请求体,然后将其转换成json对象即可。

$rawData = file_get_contents("php://input");
$a = "not found";
if (json_decode($rawData)->a){
    $a = json_decode($rawData)->a;
}
echo $a;

总结一下:

    其实到这里,我大概就明白为什么我在比赛的时候利用FormBody的方法来请求,但是却被告知无法获取到参数的原因了。因为比赛的时候服务端是接收的json字符串,也就是Content-type为application/json这样的。而我使用FormBody的时候,Content-type是application/x-www-form-urlencoded。不过到这里我其实还有点疑惑,那就是为什么我以前做过的许多与服务端的对接,都是用的FormBody的形式,但是能正常请求呢?我想大概是我接触的服务端做得比较完善,兼顾了不同的Content-type的情况?

    不过这次采坑也让我明白了我该去学习学习Http协议了,不然以后还有更多的坑等着我。。

发布了24 篇原创文章 · 获赞 7 · 访问量 8701

猜你喜欢

转载自blog.csdn.net/d745282469/article/details/88785984