【问题标题】:How to get request's URI from WebRequest in Spring?如何在 Spring 中从 WebRequest 获取请求的 URI?
【发布时间】:2019-01-26 01:05:46
【问题描述】:

我在 Spring Rest web 服务中使用 @ControllerAdviceResponseEntityExceptionHandler 处理 REST 异常。到目前为止一切正常,直到我决定将 URI 路径(发生异常)添加到 BAD_REQUEST 响应中。

@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
        HttpHeaders headers, HttpStatus status, WebRequest request) {
    logger.info(request.toString());
    return handleExceptionInternal(ex, errorMessage(HttpStatus.BAD_REQUEST, ex, request), headers, HttpStatus.BAD_REQUEST, request);
}

private ApiError errorMessage(HttpStatus httpStatus, Exception ex, WebRequest request) {
    final String message = ex.getMessage() == null ? ex.getClass().getName() : ex.getMessage();
    final String developerMessage = ex.getCause() == null ? ex.toString() : ex.getCause().getMessage();
    return new ApiError(httpStatus.value(), message, developerMessage, System.currentTimeMillis(), request.getDescription(false));
}

ApiError 只是一个 Pojo 类:

public class ApiError {

    private Long timeStamp;
    private int status;
    private String message;
    private String developerMessage;
    private String path;
}

但是 WebRequest 并没有给出任何 api 来获取请求失败的路径。我试过了: request.toString() 返回 -> ServletWebRequest: uri=/signup;client=0:0:0:0:0:0:0:1
request.getDescription(false) 返回 -> uri =/signup
getDescription 非常接近要求,但不符合要求。有没有办法只获取 uri 部分?

【问题讨论】:

    标签: spring spring-mvc spring-rest


    【解决方案1】:

    找到了解决办法。将WebRequest 转换为ServletWebRequest 解决了目的。

    ((ServletWebRequest)request).getRequest().getRequestURI().toString()
    

    返回完整路径 - http://localhost:8080/signup

    【讨论】:

    • getRequestURL()代替getRequestURI()来获取问题中的URI。
    • getRequestURI() 已经返回String
    • 您确定要返回完整路径protocol://host:port:uri吗?在容器化环境中它可能返回容器的host:port,它不等于实际调用的host:port
    【解决方案2】:

    这个问题有多种解决方案。

    1) 可以使用 WebRequest 从 WebRequest 获取请求 URI 和客户端信息 webRequest.getDescription(true)。

    true 将显示用户的信息,例如客户端 ID,false 将仅打印 URI。

    2) 在方法定义中直接使用HttpServletRequest代替WebRequest作为

    @Override
    protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
            HttpHeaders headers, HttpStatus status, WebRequest request, HttpServletRequest httpRequest) {
        logger.info(httpRequest.getRequestURI());
        return handleExceptionInternal(ex, errorMessage(HttpStatus.BAD_REQUEST, ex, request), headers, HttpStatus.BAD_REQUEST, request);
    }
    

    【讨论】:

      【解决方案3】:

      ResponseEntityExceptionHandler 解释了一个方便的@ControllerAdvice 类基类,这些类希望通过@ExceptionHandler 方法在所有@RequestMapping 方法中提供集中的异常处理。 here

      在 Spring Boot 2.1.6 中,可以这样写:

      RestExceptionHandler.java

      @Order(Ordered.HIGHEST_PRECEDENCE)
      @RestControllerAdvice
      public class RestExceptionHandler extends ResponseEntityExceptionHandler {
       
      private static final Logger logger = LoggerFactory.getLogger(RestExceptionHandler.class);
      
      @ExceptionHandler(ResourceNotFoundException.class)
      protected ResponseEntity<Object> handleEntityNotFound(ResourceNotFoundException ex, final HttpServletRequest httpServletRequest) {
          ApiError apiError = new ApiError(HttpStatus.NOT_FOUND);
          apiError.setMessage("Resource not found");
          apiError.setDebugMessage(ex.getMessage());
          apiError.setPath(httpServletRequest.getRequestURI());
          return buildResponseEntity(apiError);
      }
      
      private ResponseEntity<Object> buildResponseEntity(ApiError apiError) {
          return new ResponseEntity<>(apiError, apiError.getStatus());
      }
      
      
       
       @Override
      protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
              ApiError apiError = new ApiError(HttpStatus.METHOD_NOT_ALLOWED);
              apiError.setMessage(ex.getMessage());
              apiError.setPath(((ServletWebRequest)request).getRequest().getRequestURI().toString());
              logger.warn(ex.getMessage());
              return buildResponseEntity(apiError);
      }
      }
      

      让我们从实现一个发送错误的简单结构开始:

      ApiError.java

      public class ApiError {
      // 4xx and 5xx
      private HttpStatus status;
      
      @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
      private LocalDateTime timestamp;
      
      // holds a user-friendly message about the error.
      private String message;
      
      // holds a system message describing the error in more detail.
      @JsonInclude(value = Include.NON_EMPTY)
      private String debugMessage;
      
      // returns the part of this request's URL
      private String path;
      
      @JsonInclude(value = Include.NON_EMPTY)
      private List<String> details=new ArrayList<>();
      
      // setters & getters
      }
      

      ResourceNotFoundException.java

      public class ResourceNotFoundException extends RuntimeException {
      
      private static final long serialVersionUID = 1L;
      
      public ResourceNotFoundException() {
          super();
      }
      
      public ResourceNotFoundException(String msg) {
          super(msg);
      }
      

      【讨论】:

        【解决方案4】:

        访问WebRequest对象的属性:

        Object obj = webRequest.getAttribute("org.springframework.web.util.UrlPathHelper.PATH", 0)
        String uri = String.valueOf(obj);
        
        webRequest.getAttribute(String attributeName, int scope);
        
        // scope can be either:
        //   0: request
        //   1: session
        
        // valid attribute names can be fetched with call:
        String[] attributeNames = webRequest.getAttributeNames(0);   //scope is request
        

        有效的属性名称是:

        org.springframework.web.util.UrlPathHelper.PATH
        org.springframework.web.context.request.async.WebAsyncManager.WEB_ASYNC_MANAGER
        org.springframework.web.servlet.HandlerMapping.bestMatchingHandler
        org.springframework.web.servlet.DispatcherServlet.CONTEXT
        org.springframework.web.servlet.resource.ResourceUrlProvider
        characterEncodingFilter.FILTERED
        org.springframework.boot.web.servlet.error.DefaultErrorAttributes.ERROR
        org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE
        org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER
        formContentFilter.FILTERED
        org.springframework.web.servlet.HandlerMapping.bestMatchingPattern
        requestContextFilter.FILTERED
        org.springframework.web.servlet.DispatcherServlet.OUTPUT_FLASH_MAP
        org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping
        org.springframework.web.servlet.DispatcherServlet.FLASH_MAP_MANAGER
        org.springframework.web.servlet.HandlerMapping.uriTemplateVariables
        org.springframework.web.servlet.DispatcherServlet.THEME_RESOLVER
        org.springframework.core.convert.ConversionService
        

        【讨论】:

          【解决方案5】:

          你可以使用 request.getDescription(false)。

          【讨论】:

            【解决方案6】:

            我正在使用 SpringBoot 2.5.3 和 globalExceptionHandler。 短片。使用“TheCoder”答案并从那里开始。 如果您不需要它们,则不必使用标头、状态、... WebRequest 作为输入参数。这仅给出了 url 的端点,而不是主机名。

            @ExceptionHandler(value = NotFound.class)
            ResponseEntity<...> httpNotFoundException(NotFound exc, HttpServletRequest req ) {
            //use req.getRequestURI();
            }
            
            @ExceptionHandler(value = HttpClientErrorException.class)
            ResponseEntity<...> httpClientException(HttpClientErrorException exc, HttpServletRequest req ) {
            exc.getRawStatusCode()  //to get status code
            //I am using this to check for 404 and handling here with other stuff instead of using NotFound.class above.
            // Use req.getRequestURI();
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-07-31
              • 2022-10-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-11-08
              • 1970-01-01
              相关资源
              最近更新 更多