从@PathVariable DomainObject转换为String? (使用ControllerLinkBuilder.methodOn)

时间:2014-03-07 02:14:56

标签: spring spring-mvc controller converter spring-hateoas

我试图用非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}}方法中尝试了一些内容,但它只显示了相同的错误。但毕竟我不确定我到底尝试了什么...

4 个答案:

答案 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种方法:

  1. public boolean resolveFor(MethodParameter methodParameter)
  2. 用于确定ArgumentResolver是否可用于处理methodParameter。例如:

    public boolean resolveFor(MethodParameter methodParameter) {
        return UserDefinedType.class.isAssignableFrom(methodParameter.getParameterType());
    }
    

    定义此ArgumentResover将用于UserDefinedType

    1. public void augmentTemplate(UriTemplateAugmenter uriTemplateAugmenter, MethodParameter methodParameter)
    2. 用于在与方法相关的uriTemplate中包含正确的模板部分。例如:

      @Override
      public void augmentTemplate(UriTemplateAugmenter uriTemplateAugmenter, MethodParameter methodParameter) {
          uriTemplateAugmenter.addToQuery("value1");
          uriTemplateAugmenter.addToQuery("value2");
      
      }
      

      将2个查询参数(value1和value2)添加到uri模板。

      1. public void setTemplateVariables(UriTemplate template, MethodParameter methodParameter, Object parameter, List<String> templatedParamNames)
      2. 在模板中设置模板变量的值。例如:

        @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);
    }
}

}

```