Конвертер из @PathVariable DomainObject в строку? (использование ControllerLinkBuilder.methodOn)
Я пытаюсь вызвать весны ControllerLinkBuilder.methodOn()
С типом без строки, который всегда терпит неудачу. И я не знаю, какого рода Converter
использовать и где его зарегистрировать.
вот мой контроллер:
@RestController
@RequestMapping("/companies")
class CompanyController {
@RequestMapping(value="/{c}", method=RequestMethod.GET)
void getIt(@PathVariable Company c) {
System.out.println(c);
Link link = linkTo(methodOn(getClass()).getIt(c));
}
}
на System.out.println(c)
работает хорошо. Мой Company
объект домена получает из БД. (Я использую DomainClassConverter
)
но другой способ не работает:ConverterNotFoundException: No converter found capable of converting from type @PathVariable Company to type String
мне нужно Converter<Company, String>
? И где я должен его зарегистрировать? Я попробовал что-то внутри addFormatters(FormatterRegistry registry)
метод WebMvcConfigurationSupport
, но он просто отображает ту же ошибку. Но, в конце концов, я не знаю, что именно я пытался сделать...
4 ответов
у меня была такая же проблема, это ошибка. Если вы не хотите копировать и вставлять на каждый контроллер, вы можете попробовать что-то подобное в своем WebMvcConfigurationSupport
. Это работает на меня.
@Override
public void addFormatters(final FormatterRegistry registry) {
super.addFormatters(registry);
try {
Class<?> clazz = Class.forName("org.springframework.hateoas.mvc.AnnotatedParametersParameterAccessor$BoundMethodParameter");
Field field = clazz.getDeclaredField("CONVERSION_SERVICE");
field.setAccessible(true);
DefaultFormattingConversionService service = (DefaultFormattingConversionService) field.get(null);
for (Converter<?, ?> converter : beanFactory.getBeansOfType(Converter.class).values()) {
service.addConverter(converter);
}
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
нашел "решение". Это требует много копирования и вставки из классов Spring, но, по крайней мере, это работает!
в основном мне пришлось скопировать org.springframework.hateoas.mvc.AnnotatedParametersParameterAccessor
и меняем две строки:
class AnnotatedParametersParameterAccessor {
...
static class BoundMethodParameter {
// OLD: (with this one you can't call addConverter())
// private static final ConversionService CONVERSION_SERVICE = new DefaultFormattingConversionService();
// NEW:
private static final FormattingConversionService CONVERSION_SERVICE = new DefaultFormattingConversionService();
...
public BoundMethodParameter(MethodParameter parameter, Object value, AnnotationAttribute attribute) {
...
// ADD:
CONVERSION_SERVICE.addConverter(new MyNewConverter());
}
...
}
этот класс get используется ControllerLinkBuilderFactory
. Поэтому мне пришлось скопировать и вставить, это тоже.
и этот сделать используется ControllerLinkBuilder
. Также скопируйте и вставьте.
мой Converter
просто так myDomainObject.getId().toString()
:
public class MyNewConverter implements Converter<Company, String> {
@Override
public String convert(Company source) {
return source.getId().toString();
}
}
теперь вы можете использовать copy & pasted ControllerLinkBuilder
внутри контроллера и его работает, как ожидалось!
разработал рамки для отображения ссылок в spring hateoas и поддерживает аннотированные параметры (@PathVariable
и @RequestParam
) и произвольные типы параметров.
чтобы отобразить эти произвольные типы, вы должны создать Spring bean, который реализует com.github.osvaldopina.linkbuilder.argumentresolver.ArgumentResolver
интерфейс.
интерфейс имеет 3 метода:
public boolean resolveFor(MethodParameter methodParameter)
используется для определения, если ArgumentResolver
смогите быть использовано для того чтобы общаться с methodParameter
. Например:
public boolean resolveFor(MethodParameter methodParameter) {
return UserDefinedType.class.isAssignableFrom(methodParameter.getParameterType());
}
определяет, что это ArgumentResover
будет использоваться для UserDefinedType
.
-
public void augmentTemplate(UriTemplateAugmenter uriTemplateAugmenter, MethodParameter methodParameter)
используется для включения в uriTemplate, связанный с методом соответствующих частей шаблона. Например:
@Override
public void augmentTemplate(UriTemplateAugmenter uriTemplateAugmenter, MethodParameter methodParameter) {
uriTemplateAugmenter.addToQuery("value1");
uriTemplateAugmenter.addToQuery("value2");
}
добавляет 2 параметра запроса (value1 и value2) в шаблон uri.
public void setTemplateVariables(UriTemplate template, MethodParameter methodParameter, Object parameter, List<String> templatedParamNames)
задает в шаблоне значения для шаблона переменная. Например:
@Override
public void setTemplateVariables(UriTemplate template, MethodParameter methodParameter, Object parameter, List<String> templatedParamNames) {
if (parameter != null && ((UserDefinedType) parameter).getValue1() != null) {
template.set("value1", ((UserDefinedType) parameter).getValue1());
}
else {
template.set("value1", "null-value");
}
if (parameter != null && ((UserDefinedType) parameter).getValue2() != null) {
template.set("value2", ((UserDefinedType) parameter).getValue2());
}
else {
template.set("value2", "null-value");
}
}
получает UserDefinedType
экземпляр и используйте его для установки переменных шаблонов value1 и value2, определенных в augmentTemplate
метод.
A ArgumentResolver
полный пример будет выглядеть так:
@Component
public class UserDefinedTypeArgumentResolver implements ArgumentResolver {
@Override
public boolean resolveFor(MethodParameter methodParameter) {
return UserDefinedType.class.isAssignableFrom(methodParameter.getParameterType());
}
@Override
public void augmentTemplate(UriTemplateAugmenter uriTemplateAugmenter, MethodParameter methodParameter) {
uriTemplateAugmenter.addToQuery("value1");
uriTemplateAugmenter.addToQuery("value2");
}
@Override
public void setTemplateVariables(UriTemplate template, MethodParameter methodParameter, Object parameter, List<String> templatedParamNames) {
if (parameter != null && ((UserDefinedType) parameter).getValue1() != null) {
template.set("value1", ((UserDefinedType) parameter).getValue1());
}
else {
template.set("value1", "null-value");
}
if (parameter != null && ((UserDefinedType) parameter).getValue2() != null) {
template.set("value2", ((UserDefinedType) parameter).getValue2());
}
else {
template.set("value2", "null-value");
}
}
}
и для следующего компоновщика ссылок:
linksBuilder.link()
.withRel("user-type")
.fromControllerCall(RootRestController.class)
.queryParameterForUserDefinedType(new UserDefinedType("v1", "v2"));
к следующему методу:
@RequestMapping("/user-defined-type")
@EnableSelfFromCurrentCall
public void queryParameterForUserDefinedType(UserDefinedType userDefinedType) {
}
создаст следующую ссылку:
{
...
"_links": {
"user-type": {
"href": "http://localhost:8080/user-defined-type?value1=v1&value2=v2"
}
...
}
}
полная конфигурация в весенней загрузке. то же, что и ответ Франко Готуссо, просто более подробно. ``
/** * Этот файл конфигурации должен исправить ошибку Spring Hateoas. * пожалуйста, проверьте https://github.com/spring-projects/spring-hateoas/issues/118. */
@компонентов открытый класс MvcConfig расширяет WebMvcConfigurerAdapter {
@Autowired
private ApplicationContext applicationContext;
@Override
public void addFormatters(final FormatterRegistry registry) {
super.addFormatters(registry);
try {
Class<?> clazz = Class.forName("org.springframework.hateoas.mvc."
+ "AnnotatedParametersParameterAccessor$BoundMethodParameter");
Field field = clazz.getDeclaredField("CONVERSION_SERVICE");
field.setAccessible(true);
DefaultFormattingConversionService service =
(DefaultFormattingConversionService) field.get(null);
for (Formatter<?> formatter : applicationContext
.getBeansOfType(Formatter.class).values()) {
service.addFormatter(formatter);
}
for (Converter<?, ?> converter : applicationContext
.getBeansOfType(Converter.class).values()) {
service.addConverter(converter);
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
``