@ControllerAdvice 全局异常处理

使用@ControllerAdvice 定义 全局异常处理

 

package com.app;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

/**
 * ContorllerAdvice 最常见的使用场景是全局异常处理
 * 一般搭配 @ExceptionHandler @ModelAttribute 以及 @InitBinder 使用
 * 如下是, 当单个文件超出最大size时 对应的自定义处理方法
 * 
 *  <p>By default the methods in an {@code @ControllerAdvice} apply globally to
 * all Controllers.
 * 
 * spring:
  servlet:
    multipart:
      max-file-size: 50KB  
 *
 */
@ControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public void uploadException(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException {
    resp.setContentType("text/html;charset=utf-8");
    PrintWriter out = resp.getWriter();
    out.write("文件大小超出限制!");
    out.flush();
    out.close();
    }
}

 

当需要将自定义结果写入Response时,有更好的选择:ResponseEntityExceptionHandler( 作为 @ControllerAdvice的基类)

Open Declaration org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler


A convenient base class for @ControllerAdvice classes that wish to provide centralized exception handling across all @RequestMapping methods through @ExceptionHandler methods. 

This base class provides an @ExceptionHandler method for handling internal Spring MVC exceptions. This method returns a ResponseEntity for writing to the response with a message converter, in contrast to DefaultHandlerExceptionResolver which returns a ModelAndView. 

If there is no need to write error content to the response body, or when using view resolution (e.g., via ContentNegotiatingViewResolver), then DefaultHandlerExceptionResolver is good enough. 

如下,在Service中抛出 Exception

package com.app.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.app.dao.CityRepository;
import com.app.exceptions.CityNotFoundException;
import com.app.exceptions.NoDataFoundException;
import com.app.model.City;

import java.util.List;

@Service
public class CityService implements ICityService {

    @Autowired
    private CityRepository cityRepository;

    @Override
    public City findById(Long id) {

        return cityRepository.findById(id)
                .orElseThrow(() -> new CityNotFoundException(id));
    }

    @Override
    public City save(City city) {

        return cityRepository.save(city);
    }

    @Override
    public List<City> findAll() {

        List<City> cities = (List<City>) cityRepository.findAll();

        if (cities.isEmpty()) {

            throw new NoDataFoundException();
        }

        return cities;
    }

}

并使用ControllerAdvice 标注的类做全局处理:(使用ResponseEntity 返回定制信息以及HttpStatus Code)

package com.app.exceptions;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@ControllerAdvice
public class ControllerAdvisor extends ResponseEntityExceptionHandler {

    @ExceptionHandler(CityNotFoundException.class)
    public ResponseEntity<Object> handleCityNotFoundException(
        CityNotFoundException ex, WebRequest request) {

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("timestamp", LocalDateTime.now());
        body.put("message", "City not found");

        return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(NoDataFoundException.class)
    public ResponseEntity<Object> handleNodataFoundException(
        NoDataFoundException ex, WebRequest request) {

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("timestamp", LocalDateTime.now());
        body.put("message", "No cities data found");

        return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
    }

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
        MethodArgumentNotValidException ex, HttpHeaders headers, 
        HttpStatus status, WebRequest request) {

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("timestamp", LocalDate.now());
        body.put("status", status.value());

        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(x -> x.getField() + x.getDefaultMessage())
                .collect(Collectors.toList());

        body.put("errors", errors);

        return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
    }
}

 准备测试数据方法1:

使用MyRunner

package com.app;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import com.app.dao.CityRepository;
import com.app.model.City;

@Component
public class MyRunner implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);

    @Autowired
    private CityRepository cityRepository;

    @Override
    public void run(String... args) throws Exception {

        logger.info("Saving cities");

        cityRepository.save(new City("Bratislava", 432000));
        cityRepository.save(new City("Budapest", 1759000));
        cityRepository.save(new City("Prague", 1280000));
        cityRepository.save(new City("Warsaw", 1748000));
        cityRepository.save(new City("Los Angeles", 3971000));
        cityRepository.save(new City("New York", 8550000));
        cityRepository.save(new City("Edinburgh", 464000));
        cityRepository.save(new City("Suzhou", 4327066));
        cityRepository.save(new City("Zhengzhou", 4122087));
        cityRepository.save(new City("Berlin", 3671000));
    }
}

MyRunner 类会在项目启动之后执行:

 

 准备测试数据方法2:

在src/main/resource 下 create 一个 data.sql ,如下:

INSERT INTO cities(name, population) values('Hello',100);
INSERT INTO cities(name, population) values('World',200);

 

 

 

发生异常时的返回值:

 

 

Reference:http://zetcode.com/springboot/controlleradvice/

Code: https://github.com/XLuffyStory/SpringBootStudy/tree/master/springboot-controlleradvice

 

posted @ 2019-12-09 13:38  Happy2Share  阅读(3406)  评论(0编辑  收藏  举报