近期在写Springboot商城项目的时候,发现普通的URL地址请求可以被拦截,但是在用户未登录的情况下,添加收藏请求,拦截器能够成功拦截,但是无法返回结果给Ajax,从而无法进行请求拦截以后的页面跳转,报错内容:getWriter() has already been called for this response
通过查阅资料,找到解决办法,话不多说,解决过程如下:
(1)在拦截器中添加Ajax的拦截请求
这里我是在用户未登录的情况下,进行拦截特定的Ajax请求,仅针对用户未登录的情况下,且是Ajax请求才拦截。
完整的代码如下:
package com.study.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginHandlerInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(LoginHandlerInterceptor.class);
/**
* 拦截器
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String url = request.getServletPath();
logger.info("request请求地址path[{}] uri[{}]", request.getServletPath(),request.getRequestURI());
Object type = request.getSession().getAttribute("type");//用户登录以后的标志。
//如果是用户已经登录,或者请求部分功能如注册登录、首页查看等,均放行
if (type != null || url.contains("Login") || url.contains("login") || url.contains("register")
) {
return true;
}
//处理的是用户未登录的情况下,对于Ajax请求的拦截
if (type == null&&request.getHeader("x-requested-with") != null && request.getHeader("x-requested-with")
.equalsIgnoreCase("XMLHttpRequest")) {
//如果是ajax请求响应头会有x-requested-with
System.out.println("ajax请求被拦截");
response.setHeader("Access-Control-Expose-Headers", "REDIRECT,CONTEXTPATH");
//告诉ajax我是重定向
response.setHeader("REDIRECT", "REDIRECT");
//告诉ajax我重定向的路径
response.setHeader("CONTEXTPATH", "outLogin");
response.getWriter().write(1);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
}
//其余的一切请求均被拦截
else{
response.setCharacterEncoding("utf-8");
response.getWriter().write("<script type='text/javascript'>alert('请先登录!');"
+ "window.parent.location.href = 'outLogin'; </script>");
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {}
}
(2)在Ajax请求中添加complete方法
这里我是在ajax完成请求且接受到返回值后再做一个complete处理,对反馈的操作结果做处理,一般是success和error,error的情况下,做拦截,跳转到登录页面。
ajax部分的代码如下:
<script>
function collect(id) {
$.ajax({
cache: true,
url: "addCollect",
data: {"goods_id": id},
type: "post",
async: false,
success: function (data) {
if (data == 'yes') {
result.value && Swal.fire("收藏成功!", "操作完成", "success", ).then(function () {
document.location.reload()
})
} else if (data == 'no') {
result.value && Swal.fire("收藏失败!", "请先登录", "error", ).then(function () {
document.location.reload()
})
}else if (data == 'repeat') {
result.value && Swal.fire("收藏失败!", "您已收藏过了", "error", ).then(function () {
document.location.reload()
})
}
},
complete : function(xhr, status) {
if(status=='error'){
alert("请先登录!")
}
//拦截器实现超时跳转到登录页面
// 通过xhr取得响应头
var REDIRECT = xhr.getResponseHeader("REDIRECT");
//如果响应头中包含 REDIRECT 则说明是拦截器返回的需要重定向的请求
if (REDIRECT == "REDIRECT")
{
var win = window;
while (win != win.top)
{
win = win.top;
}
win.location.href = xhr.getResponseHeader("CONTEXTPATH");
}
}
});
}
}
</script>
(3)实现效果
在用户没有登录的情况下,点击收藏。
当用户点击确定时,提示登录,然后页面转发到登录页面。