Возврат HTTP-статуса BAD_REQUEST с использованием специального распознавателя в Spring - PullRequest
1 голос
/ 05 июля 2019

Я разрабатываю REST API с использованием Spring Boot 1.5.15 . Я реализовал клиента 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 ]

1 голос
/ 05 июля 2019

Если вы видите реализацию RequestHeaderMethodArgumentResolver, которая используется для @RequestHeader, вы увидите переопределенную реализацию handleMissingValue метода 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 для AbstractNamedValueMethodArgumentResolver, который расширяется RequestHeaderMethodArgumentResolver в зависимости от некоторых условий. Поэтому, когда заголовок отсутствует и выброшен ServletRequestBindingException, Spring 101 * обработает это и отправит ответ 400 .

Вот так работает проверка в случае @RequestHeader. Таким образом, вы можете реализовать нечто подобное в resolveArgument методе вашего SomeHeaderArgumentResolver класса, как показано ниже:

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
        }
       }
    }
1 голос
/ 05 июля 2019

Вы можете объявить свои собственные исключения для любых случаев и установить 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(); }
0 голосов
/ 05 июля 2019

Благодаря предложению, данному @ madhu-bhat, я понимаю, какой правильный класс можно расширить, чтобы Spring мог творить чудеса.

Класс RequestHeaderMethodArgumentResolver, используемый Spring для разрешения значений заголовков HTTP внутри Java-объекта, расширяет абстрактный класс 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()));
    }
}

Единственное, что мне не нравится, это то, что я использую структуры, разработанные для обработки именованных значений , но у меня нет именованного значения .

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...