简体   繁体   English

处理 Spring 引导中的嵌入式 Tomcat 异常

[英]Handle Embedded Tomcat Exception in Spring Boot

We have an issue where embedded Tomcat is throwing IllegalArgumentException from the LegacyCookieProcessor .我们遇到了嵌入式 Tomcat 从LegacyCookieProcessor抛出IllegalArgumentException的问题。 It throws a 500 HTTP response code.它抛出一个 500 HTTP 响应代码。

We need to handle the exception and do something with it (specifically, send it as a 400 instead).我们需要处理异常并对其进行处理(具体而言,将其作为 400 发送)。

The typical @ExceptionHandler(IllegalArgumentException.class) doesn't seem to get triggered and Google only seems to give results for dealing with Spring Boot specific exceptions.典型的@ExceptionHandler(IllegalArgumentException.class)似乎没有被触发,谷歌似乎只给出了处理 Spring 引导特定异常的结果。


Example:例子:

Here is an example to reproduce the behavior.这是重现该行为的示例。 You can execute the example by downloading the initial project including spring-web ( https://start.spring.io/ ) in version 2.1.5.RELEASE.您可以通过下载版本 2.1.5.RELEASE 中包含 spring-web ( https://start.spring.io/ ) 的初始项目来执行该示例。 Then add the following two classes to your project.然后将以下两个类添加到您的项目中。

DemoControllerAdvice.java 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;
    }

}

DemoRestController.java 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.";
    }

}

Then, start the server and request the following URLs in the browser:然后,启动服务器并在浏览器中请求以下 URL:

  • http://localhost:8080/working An IllegalArgumentException is thrown manually in the controller. http://localhost:8080/working在 controller 中手动抛出IllegalArgumentException It is then caught by the ControllerAdvice and will therefore produce a JSON string containing the information defined in the DemoControllerAdvice然后它被 ControllerAdvice 捕获,因此将生成一个 JSON 字符串,其中包含DemoControllerAdvice中定义的信息
  • http://localhost:8080/not-working?demo=test^123 An IllegalArgumentException is thrown by the Tomcat, because the request param cannot be parsed (because of the invalid character ^ ). http://localhost:8080/not-working?demo=test^123 Tomcat 抛出IllegalArgumentException ,因为请求参数无法解析(因为无效字符^ )。 The exception however is not caught by the ControllerAdvice.但是,ControllerAdvice 不会捕获该异常。 It shows the default HTML page provided by Tomcat.它显示了 Tomcat 提供的默认 HTML 页面。 It also provides a different error code than defined in the DemoControllerAdvice .它还提供了不同于DemoControllerAdvice中定义的错误代码。

In the logs the following message is shown:在日志中显示以下消息:

o.apache.coyote.http11.Http11Processor: Error parsing HTTP request header Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level. o.apache.coyote.http11.Http11Processor: Error parsing HTTP request header Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.

java.lang.IllegalArgumentException: Invalid character found in the request target. java.lang.IllegalArgumentException:在请求目标中发现无效字符。 The valid characters are defined in RFC 7230 and RFC 3986 at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:467) ~[tomcat-embed-core-9.0.19.jar:9.0.19] The valid characters are defined in RFC 7230 and RFC 3986 at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:467) ~[tomcat-embed-core-9.0.19.jar:9.0.19]

This is a feature of Tomcat itself as mentioned in this answer. 这是答案中提到的Tomcat本身的一个特性。

However, you can do something like this by allowing the special characters that you are expecting as part of your request and handle them yourself. 但是,您可以通过允许您期望的特殊字符作为请求的一部分并自己处理它们来执行此类操作。

First, you need to allow the special characters that you would need to handle by setting up the relaxedQueryChars like this. 首先,你需要通过设置这样的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", "^");
    });
 }
}

and later handle the special characters in each of your requests or create an interceptor and handle it in a common place. 然后处理每个请求中的特殊字符或创建一个拦截器并在一个公共位置处理它。

To handle it in the request individually you can do something like this. 要在请求中单独处理它,您可以执行类似的操作。

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.";
}

}

You might also want to refer this answer to decide if you really need this fix. 您可能还想参考答案来确定您是否真的需要此修复程序。

You may not interested in the solution any more.Just in case others do: try to catch IllegalArgumentException in your filter, then call HttpServletResponse.sendError(int sc, String msg);This may catch the IllegalArgumentExceptions that do not come from tomcat though.But suppose you already handle them properly.您可能不再对解决方案感兴趣。万一其他人这样做:尝试在您的过滤器中捕获 IllegalArgumentException,然后调用 HttpServletResponse.sendError(int sc, String msg);这可能会捕获不是来自 tomcat 的 IllegalArgumentExceptions。但是假设您已经正确处理它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM