#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
усмотрению, но я не нашел ни одного, который бы из коробки поддерживал псевдонимы, как это делает Джексон.