在Spring Boot中处理嵌入式Tomcat异常

sam*_*ime 18 java spring tomcat spring-boot

我们有一个问题,其中嵌入式Tomcat正在IllegalArgumentException从抛出LegacyCookieProcessor。它抛出500 HTTP响应代码。

我们需要处理异常并对其进行处理(特别是将其发送为400)。

典型的@ExceptionHandler(IllegalArgumentException.class)似乎没有被触发,Google似乎只给出了处理Spring Boot特定异常的结果。


例:

这是重现该行为的示例。您可以通过下载包含2.1.5.RELEASE版本中的spring-web(https://start.spring.io/)的初始项目来执行示例。然后将以下两个类添加到您的项目。

DemoControllerAdvice.java

package com.example.demo;

import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class DemoControllerAdvice {

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Map<String, String> forbiddenHandler() {
        Map<String, String> map = new HashMap<>();
        map.put("error", "An error occurred.");
        map.put("status", HttpStatus.FORBIDDEN.value() + " " + HttpStatus.FORBIDDEN.name());
        return map;
    }

}
Run Code Online (Sandbox Code Playgroud)

DemoRestController.java

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoRestController {

    @GetMapping(value = "/working")
    public void working() {
        throw new java.lang.IllegalArgumentException();
    }

    @GetMapping(value = "/not-working")
    public String notWorking(@RequestParam String demo) {
        return "You need to pass e.g. the character ^ as a request param to test this.";
    }

}
Run Code Online (Sandbox Code Playgroud)

然后,启动服务器并在浏览器中请求以下URL:

  • http://localhost:8080/working一个IllegalArgumentException在控制器手动抛出。然后,它会被ControllerAdvice捕获,因此将产生一个JSON字符串,其中包含在DemoControllerAdvice
  • http://localhost:8080/not-working?demo=test^123IllegalArgumentException由于无法解析请求参数(由于无效字符^),因此Tomcat抛出了an 。但是,该异常不会被ControllerAdvice捕获。它显示了Tomcat提供的默认HTML页面。它还提供了不同于中定义的错误代码DemoControllerAdvice

在日志中,显示以下消息:

o.apache.coyote.http11.Http11Processor:解析HTTP请求标头时出错注:进一步发生的HTTP请求解析错误将记录在DEBUG级别。

java.lang.IllegalArgumentException:在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中的org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:467)〜[tomcat-embed-core-9.0.19.jar:9.0.19]中定义

Rah*_*alu 6

这是答案中提到的Tomcat本身的功能。

但是,您可以通过允许期望的特殊字符作为请求的一部分来自己做,从而做到这一点。

首先,您需要通过这样设置relaxedQueryChars来允许需要处理的特殊字符。

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class TomcatCustomizer implements 
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

@Override
public void customize(TomcatServletWebServerFactory factory) {
    factory.addConnectorCustomizers((connector) -> {
        connector.setAttribute("relaxedQueryChars", "^");
    });
 }
}
Run Code Online (Sandbox Code Playgroud)

然后处理每个请求中的特殊字符,或创建一个拦截器并在一个公共位置处理它。

要在请求中单独处理它,您可以执行以下操作。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoRestController {

@GetMapping(value = "/working")
public void working() {
    throw new java.lang.IllegalArgumentException();
}

@GetMapping(value = "/not-working")
public String notWorking(@RequestParam String demo) {

    if (demo.contains("^")) {
        throw new java.lang.IllegalArgumentException("^");
    }
    return "You need to pass e.g. the character ^ as a request param to test this.";
}

}
Run Code Online (Sandbox Code Playgroud)

您可能还希望参考答案来确定是否确实需要此修复程序。

  • 虽然问题已经演变成谈论查询参数,但最初的问题实际上是关于Cookie中的怪异值,而不是查询参数。这对两种情况都有效吗? (3认同)