问题重现

在调试接口时get请求能够正常访问后端服务,post请求总是请求超时,在调高axios超时时间后,报错(信息如下)。大概意思是:在输入过程中IO错误, Socket连接超时。

报错信息

I/O error while reading input message; nested exception is org.apache.catalina.connector.ClientAbortException: java.net.SocketTimeoutException

问题分析

网上查资料也没能找到导致该错误的具体原因。首先要找到是客户端还是服务端出的问题,分别使用 postman 和前端应用去调同一个接口。结果如下:

  • 通过postman发送的请求,能够看到request请求的请求体;
  • 通过前端应用发送的请求,没有request请求体。

查资料得知,request请求体是流式数据,如果在其他地方消费过,不做特殊处理的话,后面的应用就消费不到了! 也就是说请求体在哪个地方被消费了,想到项目中配置了mock-serve。很有可能就是mock-server的问题了。

查看mock-server的配置发下如下代码:

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
    extended: true
}))

bodyParser.json 是用来解析json数据格式的。bodyParser.urlencoded则是用来解析我们通常的form表单提交的数据,也就是请求头中包含这样的信息: Content-Type: application/x-www-form-urlencoded

常见的四种Content-Type类型:

  • application/x-www-form-urlencoded 常见的form提交
  • multipart/form-data 文件提交
  • application/json 提交json格式的数据
  • text/xml 提交xml格式的数据

bodyParser.urlencoded 模块用于解析req.body的数据,解析成功后覆盖原来的req.body,如果解析失败则为 {}。该模块有一个属性extended,官方介绍如下:

The extended option allows to choose between parsing the URL-encoded data with the querystring library (when false) or the qs library (when true). Defaults to true, but using the default has been deprecated.

大致的意思就是:extended选项允许配置使用querystring(false)或qs(true)来解析数据,默认值是true,但这已经是不被赞成的了。

解决方法:

接口地址的配置:

    proxy: {
      '/api': {
        target: 'http://localhost:18000',
        changeOrigin: true,
        ws: true,
        secure: false,
        pathRewrite: {
          '^/api': '/'
        }
      }
    }
    before: require('./mock/mock-server.js')

其实有点想不明白,为什么在我项目中配置了代理的情况下还会走mock?

注释掉mock-server中关于body配置的相关代码。再次请求,问题解决。

image.png

总结

如果后端代码中设置了拦截器需要消费请求体,后端应该把请求体复制一份往下传递。服务端是基于微服务架构的。18000端口是网关端口,18002是具体的服务端口,网关服务以调试模式启动时,也会导致接口请求cancled。作为一名 web 开发,http 相关知识的储备还是有必要的。

参考文章:

https://www.jianshu.com/p/6d9a11f3c537

https://github.com/eclipse/jetty.project/issues/3672