春天的靴子。如何向用户返回错误消息?

时间:2017-10-26 17:21:14

标签: spring spring-boot exception-handling

我故意下载一个大文件以获得异常。处理控制器:

@RequestMapping(value = "/uploadFile", method = RequestMethod.POST, produces={"application/text; charset=UTF-8"})
@ResponseBody
public ResponseEntity<?> uploadFile(
        @RequestParam("uploadfile") MultipartFile uploadfile) throws Exception {
    String fileName =  storageService.uploadFile(uploadfile);
    return new ResponseEntity<String>(fileName, HttpStatus.OK);
}

我有一个全局拦截器:

@ControllerAdvice
public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler implements ErrorViewResolver {
    private static final Logger LOG = LoggerFactory.getLogger(GlobalControllerExceptionHandler.class);

    private static final String DEFAULT_ERROR_VIEW = "error";

    @ExceptionHandler(value = {MultipartException.class})
    @ResponseBody
    ResponseEntity<?> uploadFile(HttpServletRequest request, MultipartException ex) {
        LOG.error("Exception in uploadFile at request " + request.getRequestURL(), ex);
        return new ResponseEntity<String>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }


    @ExceptionHandler(value = {Exception.class})
    public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) {
        ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);
        LOG.error("Exception at request " + request.getRequestURL(), e);
        mav.addObject("timestamp", new Date());
        mav.addObject("error", e.getMessage());
        mav.addObject("path", request.getRequestURL());
        return mav;
    }

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus httpStatus, Map<String, Object> map) {
        ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);
        LOG.error(map.toString());
        /**
        timestamp --- Fri Oct 20 09:46:58 MSK 2017
        status --- 404
        error --- Not Found
        message --- /hudo
        path --- /hudo
         **/
        map.forEach(mav::addObject);
        return mav;
    }
}

捕获Exception MultipartException

2017-10-23 14:44:13.635 ERROR 15680 --- [nio-8080-exec-2] s.web.GlobalControllerExceptionHandler   : Exception in uploadFile at request http://localhost:8080/painter/uploadFile

org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$FileSizeLimitExceededException: The field uploadfile exceeds its maximum permitted size of 3145728 bytes.

所以,传递给客户端的消息和状态(调用是在ajax)。答案是:

POST http://localhost:8080/painter/uploadFile net::ERR_CONNECTION_ABORTED
 status - 0  error - undefined 

如何从拦截器获得响应?

1 个答案:

答案 0 :(得分:0)

我为ExceptionHandler的问题做了一个小实验;为了简单起见,我只使用POJO作为方法输入,这样我就可以使用Postman或curl轻松测试。您可以尝试将我的方法应用于MultiPartFile输入。以下是部分:

**pom.xml**

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.7.RELEASE</version>
    </parent>

    <groupId>com.test</groupId>
    <artifactId>test-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>

            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.11.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>
</project>

Spring Boot App @SpringBootApplication 公共类TestApplication扩展了SpringBootServletInitializer {

private static final Logger log = LoggerFactory.getLogger(TestApplication.class);

@Autowired
Environment env;

public static void main(String[] args) {
    SpringApplication.run(TestApplication.class, args);
}

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(TestApplication.class);
}

@Bean
public ConversionService conversionService() {
    return new DefaultConversionService();
}

}

控制器

@RestController
@RequestMapping("/api")
public class TestController {

    @RequestMapping(value = "/upload", method = RequestMethod.POST, produces={"application/json"})
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public TestResponse uploadFile(@RequestBody TestRequest request) throws MultipartException {
        if ("danger.txt".equalsIgnoreCase(request.getFileName())) {
            throw new MultipartException("Danger. Get outta there now!");
        } else {
            return new TestResponse("all good");
        }
    }
}

异常处理程序

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(value = {MultipartException.class})
    @ResponseBody
    ResponseEntity<ErrorResponse> handleFileUploadException(HttpServletRequest request, MultipartException ex) {
        LOG.error("Exception in uploadFile at request " + request.getRequestURL(), ex);
        ErrorResponse response = new ErrorResponse("Dangerous file", "The file was pretty scary.");
        return new ResponseEntity<ErrorResponse>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

请求/响应POJO

public class TestRequest {

    private String fileName;

    public TestRequest() {

    }

    public TestRequest(String fileName) {
        this.fileName = fileName;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }
}

public class TestResponse {

    private String message;

    public TestResponse() {

    }

    public TestResponse(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

错误回应POJO

import com.fasterxml.jackson.annotation.JsonProperty;

public class ErrorResponse {

    @JsonProperty("error")
    private String error;

    @JsonProperty("error_description")
    private String errorDescription;

    public ErrorResponse() {

    }

    public ErrorResponse(String error, String errorDescription) {
        super();
        this.error = error;
        this.errorDescription = errorDescription;
    }

    public String getError() {
        return error;
    }

    public String getErrorDescription() {
        return errorDescription;
    }

}

我用mvn spring-boot:run运行应用程序。然后使用curl来测试API。

卷曲请求

curl -d '{"fileName":"danger.txt"}' -H "Content-Type: application/json" -X POST http://localhost:8080/api/upload -v

卷曲回复

Note: Unnecessary use of -X or --request, POST is already inferred.
* timeout on name lookup is not supported
*   Trying ::1...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to localhost (::1) port 8080 (#0)
> POST /api/upload HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.49.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 25
>
} [25 bytes data]
* upload completely sent off: 25 out of 25 bytes
< HTTP/1.1 500
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Fri, 27 Oct 2017 07:07:26 GMT
< Connection: close
<
{ [81 bytes data]
100   100    0    75  100    25   2343    781 --:--:-- --:--:-- --:--:--  2343{"error":"Dangerous file","error_description":"The file was pretty scary."}
* Closing connection 0

最后,我根据需要获得了正确的HTTP 500以及客户端可以轻松解析的自定义响应消息。