记录一次线上事故 requestbody is missing 和 not supports

145 阅读4分钟

周虽旧邦,其命维新。

1 前言

在最近的一次上线过程中,遇到了 requestbody is missing 的问题,修复问题之后又遇到了 Content type 'application/x-www-form-urlencoded;charset=utf-8'not supported 的问题,整个问题的解决和修复过程紧张且刺激,在本文中将分享其问题的发现和解决的过程。

2 Requestbody is missing

通常公司的开发技术规范规定,API 接口都需要使用 post 方法,且使用 json 的格式传输和请求数据。问题是在测试环境没有复现这样的问题,生产环境服务请求出现了异常,十分的诡异,还好这个服务是一个新的服务,暂时没有对外提供服务的能力,要不然就 GC 了。话不多说,直接开始分析问题的发生原因,根据错误的堆栈信息,追踪到了是过滤器的问题,新的项目为了统一实现在接口的入参和出参日志打印,在过滤器内进行了数据流的读写,造成了此问题。

开发者都只知道,对于IO 流,只能读取一次,如果需要二次操作读写,需要使用 ByteArrayInputStream 进行操作,可以实现内容的重复读取。如下图所示,需要在项目中重新定义请求和响应,然后在过滤器中使用自定义的请求和响应,通过这种方式可以实现数据流的重复读取。

1727494480338.png

如下图所示,可以在 Filter 中使用自定义的请求和响应。

1727494558513.png

通过上述的情况,即可解决前述的报错问题。

3 not supports 报错

在开始之前,需要先介绍一个实用的工具,如下图所示,可以通过集成 RequestMappingHandlerMapping 的方式来实现方法 registerHandlerMethod, 即可实现在项目启动时打印项目所有的接口信息,可以方便我们寻找项目中所有的显示和隐藏的接口。

图片.png

如下图所示,即在项目中体现的报错信息,通常接口定义都是 post + json 的格式,而请求的方式却是 post + form 的格式,所以就是请求方式的问题。 图片.png

这是一个小问题,而调用方会在下次上线修改,那么本次我方服务该怎么修改呢?因为请求头的问题而新增一个接口或者改动接口都是不合适的,于是有了如下的操作。

1727500223809.png

spring 容器中,判断接口重复定义是通过接口的 uri + method + consumes + produces + header + params 来确定的。所以我们可以通过定义不同的 consumes 来实现一个接口的两种请求方式。

headers 即传输的请求头,可以是请求key 或者是键值对
consumes 即可以接收的请求方式
produces 即返回的数据格式
params 请求参数中含有某个固定值或者参数名称,可以是键值对

4 RequestBody 作用域

如下图所示,在右侧是定义了一个 Feign 的接口,但是其实现方法在左侧的控制器中,通常情况下在右侧的接口中应该定义其完整的请求信息才对。但是在实际的项目开发中,尤其是在旧的项目维护中,常常会遇到一些奇怪的写法,总是让调用者猝不及防。如下图就是在其实现类中的方法上加了 @RequestBody 注解,但是在其接口是没有添加,如果是项目的使用方看到接口的定义,直接按照常规思路发起调用,那么肯定会出现这样的错误。解决这样的问题,首先要规范接口的定义和开发规范,其次是要抓住主要矛盾,及时的修复和解决问题。

1727501156552.png

5 总结

在本文中,主要分享了最近一次上线出现的问题和解决方法。对接口的定义和使用,以及问题的发现、解决积累了实践经验。在后续的开发过程中,要养成良好的接口设计和开发规范,更高效的交付工作。