【问题标题】:Return HTTP Status BAD_REQUEST using a custom resolver in Spring在 Spring 中使用自定义解析器返回 HTTP 状态 BAD_REQUEST
【发布时间】:2019-11-16 04:12:01
【问题描述】:

我正在使用 Spring Boot 1.5.15 开发一个 REST API。我已经实现了一个客户HandlerMethodArgumentResolver 来映射一个 HTTP 标头。具体来说,我分配了HTTP头Some-Header的值,去掉了前缀“XXX”。

首先,我定义了一个自定义注解。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface SomeHeader {
}

然后,我实现了一个自定义解析器。

public class SomeHeaderArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterAnnotation(SomeHeader.class) != null;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) {
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        final String headerValue = request.getHeader("Some-Header");

        return headerValue.replace("XXX ", "");
    }
}

最后,我让 Spring 知道配置类中的解析器。

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new SomeHeaderArgumentResolver());
    }
}

现在,我可以在我需要的任何控制器中使用以下映射。

@PostMapping("/some/paath")
public void someMethod(@SomeHeader String someHeaderValue) {
   // Method body...
}

但是,Some-Header 信息对我来说是强制性的。我希望如果它不存在,Spring 会向调用者返回 400 Bad Request 响应。这与我使用 @RequestHeader("Some-Header") 注释可以获得的行为相同。

我可以复制相同的行为吗?可能,我不想使用专门的控制器建议

【问题讨论】:

    标签: java spring-boot spring-mvc spring-restcontroller


    【解决方案1】:

    感谢@madhu-bhat 提供的建议,我知道扩展哪个类来让 Spring 发挥作用是正确的。

    RequestHeaderMethodArgumentResolver,被Spring用来解析Java对象内部的HTTP头值,扩展了抽象类AbstractNamedValueMethodArgumentResolver。此类让您可以使用createNamedValueInfo 方法指定标头值是否具有某些默认值。

    所以,它遵循代码。

    public class SomeHeaderArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
        @Override
        protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
            // The second parameter specifies if the value is required, 
            // and the third if there is some default value.
            return new NamedValueInfo("", true, null);
        }
    
        @Override
        protected Object resolveName(String name, 
                                     MethodParameter parameter, 
                                     NativeWebRequest request) {
            final String headerValue = request.getHeader("Some-Value");
            if (StringUtils.isEmpty(headerValue)) {
                // Returning null tells Spring that there is no value for the parameter
                return null;
            }
            return headerValue.replace("XXX ", "");
        }
    
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            return (parameter.hasParameterAnnotation(SomeHeader.class) &&
                    !Map.class.isAssignableFrom(
                        parameter.nestedIfOptional().getNestedParameterType()));
        }
    }
    

    我唯一不喜欢的是我正在使用为处理命名值而开发的结构,但我没有命名值

    希望对你有帮助。

    【讨论】:

    • 但是,如果您在问题中询问的标头值不存在,它会帮助您发送 400 吗?
    • 是的,如果 resolveName 方法返回值 null,Spring 会返回 400 BAD REQUEST HTTP 响应。
    • 即使你返回空值,它在内部也会抛出 ServletRequestBindingException 本身。所以你之前看到的 500 可能是其他原因造成的。
    【解决方案2】:

    如果您看到用于@RequestHeaderRequestHeaderMethodArgumentResolver 的实现,您将看到AbstractNamedValueMethodArgumentResolver 抽象类的handleMissingValue 方法的重写实现,如下所示:

    @Override
    protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
            throw new ServletRequestBindingException("Missing request header '" + name +
                    "' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
    }
    

    handleMissingValue 方法用于AbstractNamedValueMethodArgumentResolverresolveArgument 方法,RequestHeaderMethodArgumentResolver 根据某些条件扩展了该方法。因此,当标头不存在并且 ServletRequestBindingException 被抛出时,Spring 的 DefaultHandlerExceptionResolver 将处理该问题并发送 400 响应。

    这就是@RequestHeader 的验证方式。所以你可以在SomeHeaderArgumentResolver 类的resolveArgument 方法中实现类似的东西,如下所示:

    public class SomeHeaderArgumentResolver implements HandlerMethodArgumentResolver {
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            return parameter.getParameterAnnotation(SomeHeader.class) != null;
        }
    
        @Override
        public Object resolveArgument(MethodParameter parameter,
                                      ModelAndViewContainer mavContainer,
                                      NativeWebRequest webRequest,
                                      WebDataBinderFactory binderFactory) {
            HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
            final String headerValue = request.getHeader("Some-Header");
            if(headerValue != null){
               return headerValue.replace("XXX ", "");
            } else {
               //handle scenario if header absent with ServletRequestBindingException
            }
           }
        }
    

    【讨论】:

    • 抱歉,它不起作用 :( 它返回 500 INTERNAL SERVER ERROR。
    • 我找到了解决问题的方法。请尝试看看。
    • @riccardo.cardin 但这也可以解决您的问题,而无需覆盖其他任何内容,对吗?
    • @riccardo.cardin 如果我的回答有帮助,请考虑投票。
    • 要使用你的答案,我还需要注册DispatcherServlet并主动抛出异常。只需更改解析器的超类型,我不需要做任何其他事情。
    【解决方案3】:

    您可以为任何情况声明自己的异常,并在控制器中设置 ExceptionHandler 以返回正确的 http-status。

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({SameHeaderException.class})
    public Object onSameHeaderException(SameHeaderException e) {
        return ImmutableMap.of("reason", e.getMessage());
    }
    

    所以如果没有标头,你可以抛出这个异常:

    if (someHeaderValue == null) { throw new SameHeaderException(); }
    

    【讨论】:

      猜你喜欢
      • 2017-03-30
      • 1970-01-01
      • 2020-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-12
      • 2019-09-01
      • 1970-01-01
      相关资源
      最近更新 更多