我试图用非String类型调用Spring的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
域对象是从DB中获取的。 (我正在使用DomainClassConverter
)
但另一种方法不起作用:ConverterNotFoundException: No converter found capable of converting from type @PathVariable Company to type String
我只需要Converter<Company, String>
吗?我应该在哪里注册?我在addFormatters(FormatterRegistry registry)
的{{1}}方法中尝试了一些内容,但它只显示了相同的错误。但毕竟我不确定我到底尝试了什么...
答案 0 :(得分:5)
我有同样的问题,它是bug。如果你不想做副本和粘贴在每个控制器上,您可以在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);
}
}
答案 1 :(得分:0)
找到了“解决方案”。它需要很多副本和从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());
}
...
}
此类由ControllerLinkBuilderFactory
使用。所以我不得不复制&amp;粘贴它。
ControllerLinkBuilder
使用了这个。同时复制&amp;糊。
我的Converter
只是myDomainObject.getId().toString()
:
public class MyNewConverter implements Converter<Company, String> {
@Override
public String convert(Company source) {
return source.getId().toString();
}
}
现在您可以在控制器中使用复制和粘贴的ControllerLinkBuilder
,它可以按预期工作!
答案 2 :(得分:0)
我开发了一个framework来渲染春天hateoas中的链接,它支持带注释的参数(@PathVariable
和@RequestParam
)和任意参数类型。
为了渲染这些任意类型,你必须创建一个实现com.github.osvaldopina.linkbuilder.argumentresolver.ArgumentResolver
接口的spring bean。
界面有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
实例并使用它来设置augmentTemplate
方法中定义的模板变量value1和value2。
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"
}
...
}
}
答案 3 :(得分:0)
春季启动时的完整配置。与Franco Gotusso的答案相同,只是提供更多细节。 ```
/ ** *此配置文件用于修复Spring Hateoas的错误。 *请查看https://github.com/spring-projects/spring-hateoas/issues/118。 * /
@Component 公共类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);
}
}
}
```