PHP、JAVA 关于 post 请求数据接收的处理

背景

最近在做项目时遇到这样一个问题,一个客户公司的智能柜管理系统想集成我们公司开发的电子锁系统,我负责相关接口的对接及开发,在一切准备工作就绪之后,进行接口联调时遇到传参问题的困扰。对方系统是采用 PHP 语言开发,我方系统是采用 JAVA 语言开发,接口约定采用 post 请求方式,以 json 格式传输数据,但是在实际接口联调中,PHP 端使用 curl 请求 JAVA 接口时的传参 JAVA 端接口始终无法获取到,对方经过检查 json 数据格式没问题,但是我采用 ajax 请求 JAVA 端接口时可以获取到参数,于是推断是不同语言传输和接受参数的方式问题!

分析

JAVA 接受 post 请求数据方式

Java 的 servlet 中接收 Post 请求数据主要采用两种方式:

(1) request.getParameter();
(2) request.getInputStream();

Content-Type 仅在取值为 application/x-www-data-urlencoded 和 multipart/form-data 两种情况下,request.getParameter () 才能获取到值,否则返回空。Content-Type 为其他类型时,一般采用 request.getInputStream () 方式获取,如下:

1
2
String json = org.apache.commons.io.IOUtils.toString(request.getInputStream());
System.out.println(json);

于是改用 request.getInputStream () 方式,问题得到解决,该方式返回 request 请求内容的字节流,转为字符串之后对取到的值进行处理,如果需要改变请求参数的值或者改变获取参数的方式满足 Controller 层的需求,可以通过使用 HttpServletRequestWrapper 重写 Request 请求参数来达到目的。

request 的 Content-Type 小结

application/x- www-form-urlencoded 是 Post 请求默认的请求体内容类型,也是 form 表单默认的类型。Servlet API 规范中对该类型的请求内容提供了 request.getParameter () 方法来获取请求参数值。但当请求内容不是该类型时,需要调用 request.getInputStream () 或 request.getReader () 方法来获取请求内容值。
当请求体内容(注意:get 请求没有请求体)类型是 application/x- www-form-urlencoded 时也可以直接调用 request.getInputStream () 或 request.getReader () 方法获取到请求内容再解析出具体都参数,但前提是还没调用 request.getParameter () 方法。此时当 request.getInputStream () 或 request.getReader () 获取到请求内容后,无法再调 request.getParameter () 获取请求内容。即对该类型的请求,三个方法互斥,只能调其中一个。今天遇到一个 Controller 请求经过 Spring MVC 的 RequestMapping 处理后,只能通过 request.getParameter () 获取到参数、无法通过 request.getInputStream () 和 request.getReader () 读取内容很可能就是因为在请求经过 Spring MVC 时已调用过 request.getParameter () 方法的原因。

注意:在一个请求链中,请求对象被前面对象方法中调用 request.getInputStream () 或 request.getReader () 获取过内容后,后面的对象方法里再调用这两个方法也无法获取到客户端请求的内容,但是调用 request.getParameter () 方法获取过内容后,后面的对象方法里依然可以调用它获取到参数的内容。

当请求体内容是其它类型时,比如 multipart/form-data 或 application/json 时,无法通过 request.getParameter () 获取到请求内容,此时只能通过 request.getInputStream () 和 request.getReader () 方法获取请求内容,此时调用 request.getParameter () 也不会影响第一次调用 request.getInputStream () 或 request.getReader () 获取到请求内容。request.getInputStream () 返回请求内容字节流,多用于文件上传,request.getReader () 是对前者返回内容的封装,可以让调用者更方便字符内容的处理(不用自己先获取字节流再做字符流的转换操作)。

普及 PHP 知识

PHP 接收 post 请求数据方式

PHP 接收 post 请求数据主要采用两种方式:

(1) $_POST [index] 方式
(2) $data = file_get_contents(“php://input”);

Content-Type 仅在取值为 application/x-www-data-urlencoded 和 multipart/form-data 两种情况下,PHP 才会将 http 请求数据包中相应的数据填入全局变量 $_POST。因此,如果使用 $_POST 来获取 post 过来的数据时,需要注意 Content-Type 类型,如果不是 application/x-www-data-urlencoded 和 multipart/form-data,则采用 file_get_contents (“php://input”); 进行获取。
php://input 可以读取 http entity body 中指定长度的值,由 Content-Length 指定长度,不管是 POST 方式或者 GET 方法提交过来的数据。但是,一般 GET 方法提交数据 时,http request entity body 部分都为空。例如在传递 json 串的时候,通过 file_get_contents (“php://input”); 获取原始串,然后通过 json_decode () 进行解析。