Формируемый почтовый запрос, мне нужно преобразовать значения случая змеи в camelCase с помощью SpringBoot и Джексона

#spring-boot #spring-mvc #x-www-form-urlencoded

Вопрос:

Я интегрируюсь с API стороннего поставщика.

У меня есть установка SpringBoot и Джексона

Они отправляют мне запрос на публикацию, который имеет тип formUrlEncoded и с параметрами в snake_case (всего более 10 параметров и без тела).

напр.

 
POST www.example.com?player_id=somePlayerIdamp;product_id=someProductIdamp;total_amount=totalAmount...

 

Есть много готовых помощников для JSON, но я не могу найти ни одного для formUrlEncoded (надеюсь, я упускаю что-то очевидное).

Я пробовал @ModelAttribute и @RequestParam, но мне не повезло.

Я пытаюсь избежать опции @RequestParam MultiValueMap<String, String> params пользовательский картограф

Ответ №1:

@RequestParam это самый простой способ, который позволяет определить точное имя параметра запроса примерно так:

 @PostMapping
public String foo(@RequestParam("player_id") String playerId){
    

}
 

Если вы хотите привязать все параметры запроса к объекту , вы должны использовать @ModelAttribute . Он основан на DataBinder и не имеет ничего общего с Джексоном. По умолчанию он поддерживает привязку параметра запроса только к объекту, поля которого имеют то же имя, что и параметр запроса. Таким образом, вы можете рассмотреть возможность привязки параметра запроса к следующему объекту :

 public class Request {
    private String player_id;
    private String product_id;
    private Long total_amount;
}
 

Если вы действительно хотите привязаться к объекту , который следует традиционному соглашению об именовании java (т. е. нижнему регистру верблюда), из параметра запроса, имеющего значения в регистре змеи, вам необходимо выполнить cusomtize WebDataBinder .

Идея состоит в том , чтобы переопределить его addBindValues() и проверить, находится ли имя параметра запроса в формате змеиного регистра, преобразовать его в формат нижнего регистра верблюда, а также добавить его в качестве значений привязки для запроса. Что-то вроде :

 public class MyServletRequestDataBinder extends ExtendedServletRequestDataBinder {

    private static Converter<String, String> snakeCaseToLowerCamelConverter = CaseFormat.LOWER_UNDERSCORE
            .converterTo(CaseFormat.LOWER_CAMEL);

    public MyServletRequestDataBinder(Object target) {
        super(target);
    }

    public MyServletRequestDataBinder(Object target, String objectName) {
        super(target, objectName);
    }

    @Override
    protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
        super.addBindValues(mpvs, request);

        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames != null amp;amp; paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            
            if(paramName.contains("_")) {
                String[] values = request.getParameterValues(paramName);
                if (values == null || values.length == 0) {
                    // Do nothing, no values found at all.
                } else if (values.length > 1) {
                    mpvs.addPropertyValue(snakeCaseToLowerCamelConverter.convert(paramName), values);
                } else {
                    mpvs.addPropertyValue(snakeCaseToLowerCamelConverter.convert(paramName), values[0]);
                }
            }
        }
    }
}

 

P. S Я использую Гуаву для того, чтобы помочь мне преобразовать чехол со змеей в чехол с нижней камерой.

Но для того , чтобы использовать настроенное WebDataBinder , вы должны, в свою очередь, настроить WebDataBinderFactory и RequestMappingHandlerAdapter потому :

  • настройте WebDataBinderFactory , чтобы создать настроенный WebDataBinder
  • настройте RequestMappingHandlerAdapter , чтобы создать WebDataBinderFactory

Что-то вроде:

 public class MyServletRequestDataBinderFactory extends ServletRequestDataBinderFactory {

        public MyServletRequestDataBinderFactory(List<InvocableHandlerMethod> binderMethods,
                WebBindingInitializer initializer) {
            super(binderMethods, initializer);
        }

        @Override
        protected ServletRequestDataBinder createBinderInstance(Object target, String objectName,
                NativeWebRequest request) throws Exception {
            return new MyServletRequestDataBinder(target, objectName);
        }

    }
 

и

 public class MyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {

    @Override
    protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
            throws Exception {
        return new MyServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
    }

}
    
 

И, наконец, зарегистрируйтесь, чтобы использовать настроенные RequestMappingHandlerAdapter в вашей конфигурации :

 @Configuration
public class Config extends DelegatingWebMvcConfiguration {

    @Override
    protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
        return new MyRequestMappingHandlerAdapter();
    }
}

 

Ответ №2:

Я не думаю, что вы что — то упускаете. Глядя на источник RequestParamMethodArgumentResolver#ResolveName, я не вижу способа настроить соответствие параметра запроса. Таким образом, похоже, что вам нужно либо реализовать свой собственный распознаватель, либо просто аннотировать каждый параметр @RequestParam и указывать имя, например @RequestParam("product_id") String productId

Редактировать:

Что касается ModelAttribute , ModelAttributeMethodProcessor использует WebDataBinder. Опять же, вы можете настроить его по своему DataBinder усмотрению, но я не нашел ни одного, который бы из коробки поддерживал псевдонимы, как это делает Джексон.