背景:
服务端项目中通过全局异常拦截器进行异常处理,但进行单元测试发现用java原生HttpURLConnection方法访问服务端时,无法读取到处理异常后的响应体中的内容,直接报错!在此记录下解决方案!
先看看发生报错时的代码如何写的:
抛出自定义错误的代码:
try {
service = (MoerService) SpringContextHolder.getBean(serviceImpl);
} catch (Exception e) {
throw new SystemException(ErrorMessageConstants.NOT_BEAN_ERROR);
}
全局异常拦截器:
/**
* 全局异常拦截器
*
* @Author yuanj
* @Date: 2018/8/12 15:40
*/
@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即可!