场景
与供应商进行一次接口的对接,自己在postman中进行调试,发现可以正常调用,但是联调时对方却报请求方式错误,导致花费了较多时间。
- 请求类型
POST
- 请求的url
https://xxxxx/vehicleOperate/openapi/gateMachine/inRecord/518021_0054/parkcarin 请求参数
{ "carinlist": "[{ "id": "197", "entrance": "入口", "cardId": "BDF2413131313131", "cardNo": "津A11111", "carNo": "津A11111", "ownerName": "临时用户", "cardTypeId": "31", "cardTypeName": "临时卡A", "entranceTime": "2017-06-14 08:38:29", "entranceUserName": "系统管理员", "entranceWayId": "0", "entranceWayName": "正常入场", "small": "0", "entryPic": "", "MachNo":"1" }]", "signature":"SJDKLSJKLDJSKLSHJKSHDJKHJKHJK", "t":"1520416876" }
接口代码
```
@PostMapping("/inRecord/{parkId}/parkcarin")
public DoorRecordResult createInRecord(@RequestBody DoorGateMachineRecordInfoInVO doorGateMachineRecordInfoVO,
@PathVariable("parkId") String parkId) {
DoorRecordResult doorRecordResult = new DoorRecordResult();
SSOUser ssoUser = getCurrentUser();
log.info("parkId : {}, 入场消息消费:{}", parkId, JSONObject.toJSONString(doorGateMachineRecordInfoVO));
List<DoorGateMachineRecordInVO> carinlist = doorGateMachineRecordInfoVO.getCarinlist();
carinlist.stream().forEach(g -> {
g.setCreatorId(ssoUser.getId());
g.setCreatorName(ssoUser.getUsername() + "/" + ssoUser.getRealname());
g.setCreateTime(new Date());
});
doorRecordResult = gateMachineService.handleInRecord(carinlist, parkId);
log.info("入场消息消费处理结束 parkId : {}, 处理结果:{}", parkId, JSONObject.toJSONString(doorRecordResult));
return doorRecordResult;
}
```
通过postman调用接口可以调通返回数据,但是通过对方应用调用就是不行,其中试了几种方式都是不行,最终发现遗漏了一项,就是content-type,对方应用调用我们接口推送数据的content-type是application/x-www-form-urlencoded,而我这边接口支持的content-type实际上是application/json,导致接口无法调用,接口文档中content-type类型一定要标明,否则会严重影响接口的开发。
application/x-www-form-urlencoded和application/json有什么区别呢?首先要了解下一共有几种content-type
application/x-www-form-urlencoded
表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据,在springMVC里面接收此类型传递的数据要通过@RequestParam,如上面接口就可以修改成
application/x-www-form-urlencoded是最常见的 POST 提交数据的方式了。浏览器的原生@PostMapping(value = "/inRecord/{parkId}/parkcarin",consumes = "application/x-www-form-urlencoded") public DoorRecordResult createInRecord(@RequestParam (value = "carinlist") String carinlistStr, @PathVariable("parkId") String parkId) { ----- carinlistStr = parameterMap.get("carinlist")[0]; }
也可以不用springMVC的注解,直接从HttpServerlet中取值,如
@PostMapping(value = "/inRecord/{parkId}/parkcarin",consumes = "application/x-www-form-urlencoded") public DoorRecordResult createInRecord(HttpServletRequest request, @PathVariable("parkId") String parkId) { List<DoorGateMachineRecordInVO> carinlist = null; Map<String, String[]> parameterMap = request.getParameterMap(); try { String carinlistStr = parameterMap.get("carinlist")[0]; log.info("入场消息消费处理开始 parkId : {}, 收到数据:{}", parkId,carinlistStr); carinlist = JacksonUtil.toList(carinlistStr, DoorGateMachineRecordInVO.class);
之前对方系统报请求方式错误,就是因为代码中使用@RequestBody来接收参数,那什么时候要用@RequestBody来接收参数呢,这就要说第二种content-type
application/json
这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。 这种类型是现在很常见的响应头,而接收她的参数就是@RequestBody,之前接口不通的原因也是我这边使用的响应头其实是application/json方式
- multipart/form-data
这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让
表单的 enctype 等于 multipart/form-data - text/xml
它是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范。典型的 XML-RPC 请求是这样的:
POST http://www.example.com HTTP/1.1 Content-Type: text/xml <?xml version="1.0"?> examples.getStateName41
除了content-type类型外,还要注意的就是如何在postman上测试application/x-www-form-urlencoded请求。由于入参是复杂的数组,所以要考虑postman如何传递数组。
起初以为是这样传递
一直调不通,后来才发现是这样的
由此可见postman默认是一行对应一个key-value,当时联调时由于对postman的这种调用不熟悉,所以使用通过java代码的调用形式,这两种也是异曲同工的。了解这些后我也对http的请求头进行了一些了解
Accept:客户端能接受的内容类型 text/plain, text/html
User-Agent :发起请求方的信息
结语
Http接口算是诸多接口中最简单的接口,但是如果不熟悉他的请求头,请求体,及相应框架的接收处理,也会产生一系列的问题,只有再实践中发掘其中问题,才能再下次处理接口时更好的处理功能的开发,更快的处理相关问题,提升开发的效率。