在Spring中使用自定义解析器返回HTTP Status BAD_REQUEST

时间:2019-07-05 14:01:04

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

我正在使用 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")批注获得相同的行为。

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

3 个答案:

答案 0 :(得分:1)

对于任何情况,您都可以声明自己的异常,并在控制器中设置ExceptionHandler以返回正确的http状态。

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

因此,如果标题不存在,则可以引发此异常:

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

答案 1 :(得分:1)

如果您看到用于RequestHeaderMethodArgumentResolver的{​​{1}}的实现,则会看到@RequestHeader抽象类的handleMissingValue方法的重写实现,如下所示:< / p>

AbstractNamedValueMethodArgumentResolver

根据某些条件,此@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扩展的resolveArgument的{​​{1}}方法中。因此,当标题不存在并且抛出AbstractNamedValueMethodArgumentResolver时,Spring的RequestHeaderMethodArgumentResolver会处理该问题并发送 400 响应。

这就是ServletRequestBindingException时验证的工作方式。因此,您可以在DefaultHandlerExceptionResolver类的@RequestHeader方法中实现类似的操作,如下所示:

resolveArgument

答案 2 :(得分:0)

由于@ madhu-bhat给出的建议,我知道哪个是可以扩展的让Spring发挥作用的正确类。

Spring用于在Java对象中解析HTTP标头值的类RequestHeaderMethodArgumentResolver扩展了抽象类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()));
    }
}

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

希望有帮助。