spring 全局请求,根据请求类型返回json或html

转 : https://blog.csdn.net/Dongguabai/article/details/110510818

@ControllerAdvice 全局异常响应页面和 JSON

我这里页面以 Thymeleaf 为例子,相关配置:

spring:
  thymeleaf:
    cache: false
    mode: HTML5
    encoding: UTF-8
    prefix: classpath:/templates/

判断是否是 AJAX:

public static boolean isAjaxRequest(HttpServletRequest request) {
      return request.getHeader("x-requested-with") != null;
}

异常拦截器:

/**
 * @author Dongguabai
 * @Description
 * @Date 创建于 2020-12-02 10:43
 */
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    private static final String ERROR_PAGE = "error";

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Object handleException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Exception e) throws UnsupportedEncodingException {
        if (isAjaxRequest(httpServletRequest)){
             //这里返回项目中自定义的统一响应对象即可
            return json(e);
        }
        return view(e);
    }

  这里返回项目中自定义的统一响应对象即可
    private ResponseX json(Exception e) {
        return new ResponseX();
    }
 
        //这里返回 ModelAndView 
    public ModelAndView view(Exception e) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setStatus(HttpStatus.BAD_REQUEST);
        modelAndView.setViewName(ERROR_PAGE);
        modelAndView.addObject("msg","msg");
        modelAndView.addObject("code","code");
        return modelAndView;
    }
}

error.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <head th:insert="fragments/header"/>
    <title>error</title>
</head>
<body>
  <span th:text="${code}" ></span> - <span th:text="${msg}" ></span>
</body>
</html>

这里补充说明下,建议不要使用拦截器做,当然不是说拦截器不行(处理响应流即可),主要是拦截器其实是无法处理 Controller 中的异常的:

org.springframework.web.servlet.DispatcherServlet#triggerAfterCompletion:

    private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception {
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, ex);
        }

        throw ex;
    }

 

 

 

 

转: https://www.jianshu.com/p/a9bcfe733713

SpringBoot:Web项目中如何优雅的同时处理Json和Html请求的异常

在一个web项目开发中,通常都会涉及到Html和Json请求。当出现异常的时候,我们需要根据请求类型返回不同的信息。如果是Json请求,那么就返回String或者ReponseEntity类型;如果是html请求,就要返回ModelAndView的错误页面。

 

我们当然可以对Controller的每个接口方法抛出的异常单独处理。但这样做会导致大量的重复工作。Spring MVC为我们提供了@ControllerAdvice和@ExceptionHandler`两个注解来实现全局的异常处理。关于这两个注解的用法可以参考这里

这解决了大部分的问题,但是如果同一个Controller中既有html又有json接口方法怎么办呢?我们当然可以拆分成两个Controller,一个包含html接口,另一个包含json接口。但是这样做不够灵活,而且我更习惯根据业务逻辑来归类接口。有没有更好的方法呢?

其实在写异常处理方法时,我们可以将请求信息作为参数传入,并根据请求类型来返回不同的数据。


public class BaseController{
    private Boolean isJson(HttpServletRequest request){
        String header = request.getHeader("content-type");
        return header != null && header.contains("json");
    }

    @Override
    @ExceptionHandler(BaseException.class)
    public Object handleBaseException(HttpServletRequest request, baseException e) {
        if(isJson(request)) {
            return ResponseUtils.restResponse(
                    e.getCode(),
                    e.getMessage(),
                    e.getStatus()
            );
        } else {
            ModelAndView modelAndView = initModelAndView();
            if (e.getCode().equalsIgnoreCase("login_first")) {
                modelAndView.setViewName("redirect:/list");
            }
            if (e.getCode().equalsIgnoreCase("real_name_not_set")) {
                modelAndView.setViewName("redirect:/account");
            }else{
                modelAndView.setViewName("/404");
            }
            modelAndView.addObject("exception", e);
            return modelAndView;
        }
    }
}

这里我们写了一个BaseController,并在Controller中实现了异常捕获的逻辑。isJson()通过判断请求的Content-Type是否包含json字符串来判断该请求类型。当然,更好更合适的方式是通过包头中的Accept中的信息类判断。需要注意的是handleBaseException()方法返回了Object类型,这样我们就可以根据需要返回不同类型的数据了。以后只要Contrller继承BaseController就不用再考虑异常的问题了。

但是,如果异常是在进入接口方法之前被抛出的呢。比如404,406错误,根本不会执行接口方法,因此也无法被ExceptionHandler捕获。这部分异常如何处理呢?

Spring Boot提供了一个统一的/error地址用于所有未被捕获的异常抛出。默认设置下显示的是一个whitelabel error page


通过实现ErrorController,我们可以定制这个错误页面。
@Controller
public class MpErrorController extends BaseController implements ErrorController {
    private static final String PATH = "/error";

    @RequestMapping(value = PATH)
    public Object error() throws Exception {
        throw new BaseException();
    }

    @Override
    public String getErrorPath() {
        return PATH;
    }
}

我们希望这个error页面也根据请求的类型做出不同的逻辑处理。因此,可以直接在error()抛出BaseException异常,并且让这个Controller继承于BaseController。这样,被抛出的异常也会被handleBaseException()捕获了。

至此,我们比较优雅的实现了全局的异常处理。所有的BaseException异常处理逻辑都集中在handleBaseException()方法中。




 

posted @ 2021-03-25 11:26  与f  阅读(505)  评论(0编辑  收藏  举报