6

我试图ControllerLinkBuilder.methodOn()用非字符串类型调用 Spring,但总是失败。而且我不知道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

4 回答 4

5

我有同样的问题,这是一个错误。如果您不想在每个控制器上进行复制和粘贴,您可以在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);
    }
}
于 2014-06-06T12:24:01.240 回答
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. 所以我也不得不复制和粘贴它。

而这个被ControllerLinkBuilder. 也可以复制粘贴。

Converter只是这样做myDomainObject.getId().toString()

public class MyNewConverter implements Converter<Company, String> {
    @Override
    public String convert(Company source) {
        return source.getId().toString();
    }   
}

现在您可以在控制器中使用复制和粘贴ControllerLinkBuilder,它可以按预期工作!

于 2014-03-08T00:26:46.097 回答
0

我开发了一个框架来在 spring hatoas 中渲染链接,它支持带注释的参数(@PathVariable@RequestParam)和任意参数类型。

为了呈现这些任意类型,您必须创建一个实现com.github.osvaldopina.linkbuilder.argumentresolver.ArgumentResolver接口的 spring bean。

该接口有3个方法:

  1. public boolean resolveFor(MethodParameter methodParameter)

是用来判断是否ArgumentResolver可以用来处理的methodParameter。例如:

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

定义这ArgumentResover将用于UserDefinedType.

  1. public void augmentTemplate(UriTemplateAugmenter uriTemplateAugmenter, MethodParameter methodParameter)

用于在与方法关联的 uriTemplate 中包含适当的模板部分。例如:

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

}

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

  1. 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

一个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"
    }
    ...
}

}

于 2016-06-08T22:42:08.743 回答
0

spring boot 中的完整配置。与 Franco Gotusso 的回答相同,只是提供更多细节。```

/** * 这个配置文件是为了修复 Spring Hateoas 的 bug。* 请查看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);
    }
}

}

```

于 2017-08-31T13:12:19.353 回答