2014-03-07 26 views
6

我試圖用非字符串類型調用Spring的ControllerLinkBuilder.methodOn(),它總是失敗。我不知道要使用哪種Converter以及在哪裏註冊它。從@PathVariable DomainObject轉換爲字符串? (使用ControllerLinkBuilder.methodOn)

這裏是我的控制器:

@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>?我應該在哪裏註冊?我嘗試了WebMvcConfigurationSupportaddFormatters(FormatterRegistry registry)方法,但它只顯示相同的錯誤。但畢竟我不知道我到底想要什麼......

回答

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控制器內部副本&和它的作品如預期!

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

我開發了一個framework呈現在春季HATEOAS的鏈接,並支持註釋參數(@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

  • 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)。

  • 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實例,並用它來設置模板變量值1和值2在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" 
        } 
        ... 
    } 
    

    }

    0

    在春季啓動全面的配置。同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); 
        } 
    } 
    

    }

    ```