HttpURLConnection InputStream 读取非200响应码,错误的getInputStream

Android Test、WEB test 同时被 2 个专栏收录
4 篇文章 0 订阅
10 篇文章 0 订阅

更多精彩

更多精彩请访问:http://www.gzdangfu.com

背景:

服务端项目中通过全局异常拦截器进行异常处理,但进行单元测试发现用java原生HttpURLConnection方法访问服务端时,无法读取到处理异常后的响应体中的内容,直接报错!在此记录下解决方案!

一、错误场景还原

先看看发生报错时的代码如何写的:

抛出自定义错误的代码:

try {
    service = (MoerService) SpringContextHolder.getBean(serviceImpl);
} catch (Exception e) {
    throw new SystemException(ErrorMessageConstants.NOT_BEAN_ERROR);
}

全局异常拦截器:

/**
 * 全局异常拦截器
 *
 * @Author ---
 * @Date: --
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);


    /**
     * 拦截全局异常
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public Result exception(Exception e) {
        logger.error("异常错误:", e);
        return Result.fail(ErrorMessageConstants.ABNORMAL_ERROR);
    }

    /**
     * 拦截系统异常
     */
    @ExceptionHandler(SystemException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public Result systemException(SystemException e) {
        logger.error("系统异常:", e);
        return Result.fail(e.getMessage());
    }

    /**
     * 拦截业务异常
     */
    @ExceptionHandler(BussinessException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public Result bussinessException(BussinessException e) {
        logger.error("业务异常:", e);
        return Result.fail(e.getMessage());
    }

HttpRequestUtil工具类方法(用来向服务端发起请求):

public static String postJsonParams(String requestUrl, String requestMethod, String params) {
   StringBuilder sb = new StringBuilder();
   try {
      URL url = new URL(requestUrl);// 创建连接
      HttpURLConnection connection = (HttpURLConnection) url
            .openConnection();
      connection.setDoOutput(true);
      connection.setDoInput(true);
      connection.setUseCaches(false);
      connection.setInstanceFollowRedirects(true);
      connection.setRequestMethod(requestMethod); // 设置请求方式
      connection.setRequestProperty("Accept", "application/json"); // 设置接收数据的格式
      connection.setRequestProperty("Content-Type", "application/json"); // 设置发送数据的格式
      connection.connect();
      OutputStreamWriter out = new OutputStreamWriter(
            connection.getOutputStream(), "UTF-8"); // utf-8编码
      out.append(params);
      out.flush();
      out.close();
      if (connection != null) {
         BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
         String line = null;
         while ((line = br.readLine()) != null) {
            sb.append(line);
            sb.append("\r\n");
         }
         br.close();
      }
      connection.disconnect();
   } catch (IOException e) {
      e.printStackTrace();
   }
   return sb.toString();
}

当服务端抛出 SystemException() 时,会被异常拦截器 GlobalExceptionHandler 所处理,然后return
Result.fail(e.getMessage()),返回自定义友好错误提示;

但通过postJsonParams方法发起请求时,却无法获取服务端响应内容,报错信息如下:

java.io.IOException: Server returned HTTP response code: 500 for URL: http://localhost:8885/moerService/access
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1839)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440)
	at com.moerlong.service_authorize.util.HttpRequest.postJsonParams(HttpRequest.java:94)
	at com.moerlong.service_authorize.controller.AccessController.test(AccessController.java:91)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
......

二、错误定位

由错误信息可以看到:Server returned HTTP response code: 500,说明服务端响应码为500是错误的源头;

我们再看异常拦截器中的 @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 正好为response设置响应码为500,难道是这个问题?

之前通过HttpClient发送请求时没遇到这种问题,难道是HttpURLConnection API不能读取responseCode为500时的响应体内容么?

再看读取响应体的方法:

if (connection != null) {
         BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
         String line = null;
         while ((line = br.readLine()) != null) {
            sb.append(line);
            sb.append("\r\n");
         }
         br.close();
      }

我们是通过connection.getInputStream()方法获取输入流来读取响应体内容的!但这个API只能读取
responseCode=200 时的响应内容!

我们翻阅 HttpURLConnection 的API文档,可以看到十分靠前的一个方法getErrorStream()

在这里插入图片描述

Returns the error stream if the connection failed but the server sent
useful data nonetheless.

原来通过这个方法我们才能获取到responseCode不为200(connection failed)的响应内容(useful data)

三、解决方案

修改下HttpRequestUtil发送请求的工具类:

public static String postJsonParams(String requestUrl, String requestMethod, String params) {
   StringBuilder sb = new StringBuilder();
   try {
      URL url = new URL(requestUrl);// 创建连接
      HttpURLConnection connection = (HttpURLConnection) url
            .openConnection();
      connection.setDoOutput(true);
      connection.setDoInput(true);
      connection.setUseCaches(false);
      connection.setInstanceFollowRedirects(true);
      connection.setRequestMethod(requestMethod); // 设置请求方式
      connection.setRequestProperty("Accept", "application/json"); // 设置接收数据的格式
      connection.setRequestProperty("Content-Type", "application/json"); // 设置发送数据的格式
      connection.connect();
      OutputStreamWriter out = new OutputStreamWriter(
            connection.getOutputStream(), "UTF-8"); // utf-8编码
      out.append(params);
      out.flush();
      out.close();
      if (connection != null) {
         InputStream inputStream = null;
         //根据responseCode来获取输入流,此处错误响应码的响应体内容也要获取(看服务端的返回结果形式决定)
         if (HttpURLConnection.HTTP_OK == connection.getResponseCode()) {
            inputStream = connection.getInputStream();
         } else {
            inputStream = connection.getErrorStream();
         }
         BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
         String line = null;
         while ((line = br.readLine()) != null) {
            sb.append(line);
            sb.append("\r\n");
         }
         br.close();
      }
      connection.disconnect();
   } catch (IOException e) {
      e.printStackTrace();
   }
   return sb.toString();
}

核心:

if (HttpURLConnection.HTTP_OK == connection.getResponseCode()) {
  inputStream = connection.getInputStream();
} else {
   inputStream = connection.getErrorStream();
}

根据 ResponseCode来选择getInputStream/getErrorStream API即可!

作者免费源码福利,更多推荐,欢迎下载学习

  • 2
    点赞
  • 1
    评论
  • 3
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:酷酷鲨 设计师:CSDN官方博客 返回首页

打赏作者

当富网络

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值