SpringMVC的@Responsebody注解与拦截器冲突问题分析
- 背景
- 查找原因
- 测试分析
背景
在学习springMVC时想在前端使用ajax请求访问controller方法,该方法返回一个自定义实体类的数据作为响应传递给ajax,ajax弹出响应的数据。结果没有正确弹出想要的结果。
代码如下:
$("#testResponseBody2").click(function () {
$.ajax({
url: "user/testResponseBody2", // 请求路径
contentType: "application/json;charset=UTF-8",
data: '{"message":"通过前端ajax发送了一个字符串","length":23}',
dataType: "json",
type: "post",
success: function (data) {
alert(data); //显示[object Object],因为alert不能弹出对象
alert("message: "+data.message);
console.log(data); //F12在调试界面的控制台可以显示对象json表示的字符串
},
error: function () {
alert("出错啦...");
}
});
});
------------------------------------------------------------------------------------
<input type="button" value="testResponseBody2" id="testResponseBody2"><br><br>
------------------------------------------------------------------------------------
@RequestMapping("/testResponseBody2")
@ResponseBody
public Message testResponseBody2(@RequestBody Message message) {
//要想使用ResponseBody注解,不仅要导入jackson的依赖,还需要給自定义类加入get方法。
System.out.println(message);
message.setMessage("被java程序更改了传来的信息");
return message;
}
查找原因
经过分析和测试,发现是配置的拦截器导致的冲突。当有拦截器作用于使用上述的testResponseBody2方法时,就会导致无法的得到想要的效果:弹出正确的信息。
测试分析
下面通过实际测试来分析@ResponseBody注解和拦截器冲突的问题。
前端:
$("#testResponseBody3").click(function () {
$.ajax({
url: "user/testResponseBody3", // 请求路径
contentType: "application/json;charset=UTF-8",
data: '{"message":"通过前端ajax发送了一个字符串","length":23}',
dataType: "json",
type: "post",
success: function (data) {
alert(data); //显示[object Object],因为alert不能弹出对象
alert("message: "+data.message);
console.log(data); //F12在调试界面的控制台可以显示对象json表示的字符串
},
error: function () {
alert("出错啦...");
}
});
});
---------------------------------------------------------------------------------------------------
<h1>测试ajax请求和a标签请求@ResponseBody的响应时,能否成功响应,以及拦截器能否拦截下来转发到其他页面</h1>
<form action="user/testResponseBody2">
message: <input type="text" name="message"><br>
length: <input type="text" name="length"><br>
<input type="submit" value="submit">
</form>
<a href="user/testResponseBody2?message=aaa&length=111">testResponseBody:测试ResponseBody和拦截器的冲突问题</a><br><br>
<input type="button" value="testResponseBody3" id="testResponseBody3"><br><br>
Controller方法:
@RequestMapping("/testResponseBody2")
@ResponseBody
public Message testResponseBody2(Message message) {
System.out.println(message);
message.setMessage("被java程序更改了传来的信息");
return message;
}
@RequestMapping("/testResponseBody3")
@ResponseBody
public Message testResponseBody3(@RequestBody Message message) {
System.out.println(message);
message.setMessage("被java程序更改了传来的信息");
return message;
}
拦截器类:
//测试拦截器的前置方法在ajax和a标签两种访问情况下能否拦截@ResponseBody的响应跳转其他页面
public class TestInterceptorPreHandle_ResponseBody implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截成功,执行拦截器前置方法");
request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
return false;
}
}
//测试拦截器的后置方法在ajax和a标签两种访问情况下能否拦截@ResponseBody的响应跳转其他页面
public class TestInterceptorPostHandle_ResponseBody implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截成功,执行拦截器前置方法");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("执行拦截器后置方法");
modelAndView.addObject("attributeName1","拦截器后置方法设置此EL");
response.sendRedirect("/springMVC02/testForward&Redirect.jsp");
}
}
springMVC.xml中配置拦截器的作用方法:每次只引入一个拦截器类作用于testResponseBody2和3方法,即第一次测试引入TestInterceptorPreHandle_ResponseBody拦截器作用于testResponseBody2和3方法,第二次测试引入TestInterceptorPostHandle_ResponseBody拦截器作用于testResponseBody2和3方法。
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/testResponseBody2"/>
<bean class="liang.interceptor.TestInterceptorPreHandle_ResponseBody"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/user/testResponseBody3"/>
<bean class="liang.interceptor.TestInterceptorPreHandle_ResponseBody"></bean>
</mvc:interceptor>
<!-- 第一次使用 -->
<mvc:interceptor>
<mvc:mapping path="/user/testResponseBody2"/>
<bean class="liang.interceptor.TestInterceptorPostHandle_ResponseBody"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/user/testResponseBody3"/>
<bean class="liang.interceptor.TestInterceptorPostHandle_ResponseBody"></bean>
</mvc:interceptor>
<!-- 第二次使用 -->
</mvc:interceptors>
测试结果:
- 拦截器使用前置方法跳转其他页面
- 超链接a和表单访问可以被拦截器正常拦截下来,跳转到拦截器指定的跳转界面。
- ajax请求会弹出错误提示框,F12查看无错误信息;无法跳转到拦截器指定的跳转界面。
- 拦截器使用后置方法跳转其他页面
- 超链接a和表单第一次访问会直接显示响应信息,但F12控制台会显示:net::ERR_INCOMPLETE_CHUNKED_ENCODING 200错误。
返回访问页面再次访问,无法显示响应信息,刷新访问页面多次访问都无法显示响应信息,控制台会显示:net::ERR_INCOMPLETE_CHUNKED_ENCODING 200错误。
查看java的输出,发现已经进入拦截器的后置方法但是没有执行转发到其他页面的操作。
但是如果去掉拦截器,则访问可以得到响应信息,且返回再访问或刷新再访问重复多次,都可以正确显示响应信息。 - ajax请求会弹出错误错误提示框,F12查看显示:net::ERR_INCOMPLETE_CHUNKED_ENCODING 200;无法跳转到拦截器指定的跳转界面。
- 此外就算拦截器没有跳转其他页面的操作,只要有拦截器就会导致ajax无法接收到响应数据。而不加拦截器就可以正确弹出响应数据。
分析:
- 对于使用@ResonseBody修饰的方法
- ajax请求与拦截器冲突,不能同时使用,使用拦截器时注意排除响应ajax请求的方法。
- 普通请求和拦截器也冲突,不能同时使用,因为会导致无法获得响应数据(无论后置方法中是否跳转到其他页面),前置方法虽然可以跳转其他页面,但如果前置方法不执行跳转其他页面,则过滤器会导致最后无法获得响应数据。