使用 spring RestTemplate 消耗 json+hal _embedded 资源的问题

Posted

技术标签:

【中文标题】使用 spring RestTemplate 消耗 json+hal _embedded 资源的问题【英文标题】:Problems consuming json+hal _embedded resources with spring RestTemplate 【发布时间】:2018-03-18 18:12:52 【问题描述】:

我有一个简单的用例,我想使用一个用 json+hal 表示的资源集合。

我使用 spring RestTemplate 并将其配置为使用 Jackson2HalModule。 当我调试我的代码时,我发现 Response 对象确实包含准确的元数据(例如页面和资源的数量)和响应标头,但没有内容或链接。过去一天,我查看了互联网上的许多文章和指南,根据我的发现,我觉得我的自定义休息模板应该适用于我的用例。

如果有人能对此有所了解,我将不胜感激。

我的服务代码如下:

@Service
public class EventServiceImpl extends BaseService implements EventService 

private static final String knownEntity = "59d786d642572853721728f6";

private static  String SERVICE_URL = "http://EVENTS-SERVER";

private static String EVENTS_PATH = "/events";


@Autowired
@LoadBalanced
protected RestTemplate restTemplate;

@Override
public ResponseEntity<PagedResources<Event>> fetchEventsList() 
    // acceptable media type
    List<MediaType> acceptableMediaTypes = Arrays.asList(HAL_JSON);

    // header
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(acceptableMediaTypes);

    HttpEntity<String> entity = new HttpEntity<String>(null, headers);

    ResponseEntity<PagedResources<Event>> response = getRestTemplateWithHalMessageConverter()
            .exchange(SERVICE_URL + EVENTS_PATH, HttpMethod.GET, entity, new ParameterizedTypeReference<PagedResources<Event>>());
    return response;


public RestTemplate getRestTemplateWithHalMessageConverter() 
    List<HttpMessageConverter<?>> existingConverters = restTemplate.getMessageConverters();
    List<HttpMessageConverter<?>> newConverters = new ArrayList<>();
    newConverters.add(getHalMessageConverter());
    newConverters.addAll(existingConverters);
    restTemplate.setMessageConverters(newConverters);
    return restTemplate;


private HttpMessageConverter getHalMessageConverter() 
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new Jackson2HalModule());
    MappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class);
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    halConverter.setSupportedMediaTypes(Arrays.asList(HAL_JSON));
    halConverter.setObjectMapper(objectMapper);
    return halConverter;

我的简单模型是:

public class Event 

private String name;
private String location;
private int capacity;

public String getName() 
    return name;


public String getLocation() 
    return location;


public int getCapacity() 
    return capacity;

为了完整起见,这里是我尝试使用的 hal+json 形状的示例:


"_embedded": 
    "events": [
        
            "name": null,
            "location": null,
            "capacity": 0,
            "currentState": "CANCELLED",
            "_links": 
                "self": 
                    "href": "http://192.168.1.6:2221/events/59d786d642572853721728f6"
                ,
                "event": 
                    "href": "http://192.168.1.6:2221/events/59d786d642572853721728f6"
                ,
                "reinstate": 
                    "href": "http://192.168.1.6:2221/events/59d786d642572853721728f6/reinstate"
                ,
                "reschedule": 
                    "href": "http://192.168.1.6:2221/events/59d786d642572853721728f6/reschedule"
                
            
        ,
        
            "name": null,
            "location": null,
            "capacity": 0,
            "currentState": "ADVERTISED",
            "_links": 
                "self": 
                    "href": "http://192.168.1.6:2221/events/59d7f14342572812ceca7fc6"
                ,
                "event": 
                    "href": "http://192.168.1.6:2221/events/59d7f14342572812ceca7fc6"
                ,
                "cancel": 
                    "href": "http://192.168.1.6:2221/events/59d7f14342572812ceca7fc6/cancel"
                ,
                "reschedule": 
                    "href": "http://192.168.1.6:2221/events/59d7f14342572812ceca7fc6/reschedule"
                
            
        ,
        
            "name": null,
            "location": null,
            "capacity": 0,
            "currentState": "ADVERTISED",
            "_links": 
                "self": 
                    "href": "http://192.168.1.6:2221/events/59d7f14742572812ceca7fc7"
                ,
                "event": 
                    "href": "http://192.168.1.6:2221/events/59d7f14742572812ceca7fc7"
                ,
                "cancel": 
                    "href": "http://192.168.1.6:2221/events/59d7f14742572812ceca7fc7/cancel"
                ,
                "reschedule": 
                    "href": "http://192.168.1.6:2221/events/59d7f14742572812ceca7fc7/reschedule"
                
            
        ,
        
            "name": null,
            "location": null,
            "capacity": 0,
            "currentState": "ADVERTISED",
            "_links": 
                "self": 
                    "href": "http://192.168.1.6:2221/events/59d7f14c42572812ceca7fc8"
                ,
                "event": 
                    "href": "http://192.168.1.6:2221/events/59d7f14c42572812ceca7fc8"
                ,
                "cancel": 
                    "href": "http://192.168.1.6:2221/events/59d7f14c42572812ceca7fc8/cancel"
                ,
                "reschedule": 
                    "href": "http://192.168.1.6:2221/events/59d7f14c42572812ceca7fc8/reschedule"
                
            
        
    ]
,
"_links": 
    "self": 
        "href": "http://192.168.1.6:2221/events?page,size,sort",
        "templated": true
    ,
    "profile": 
        "href": "http://192.168.1.6:2221/profile/events"
    
,
"page": 
    "size": 20,
    "totalElements": 4,
    "totalPages": 1,
    "number": 0

编辑:我可以毫无问题地使用单个事件。

【问题讨论】:

【参考方案1】:

我遇到了类似的问题,最后我使用了org.springframework.hateoas.Resources

对于我下面的例子,这个对象位于org.springframework.hateoas:spring-hateoas:jar:0.25.2.RELEASE 它是从org.springframework.boot:spring-boot-starter-data-rest:jar:2.1.7.RELEASE 中提取的,因此假设您声明了spring-boot-starter-data-rest 依赖项,它很有可能已经在您的类路径中。

这是一个简单的例子(使用您的信息):

RestTemplate restTemplate = new RestTemplate();
Resources<Event> resourceEvents = restTemplate.getForObject("http://192.168.1.6:2221/events", Resources.class);
List<Event> events = new ArrayList<>(resourceEvents.getContent());

可能有一些陷阱使这不是那么简单,但希望这可以为解决您的问题提供一个开始。

【讨论】:

以上是关于使用 spring RestTemplate 消耗 json+hal _embedded 资源的问题的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot CALL API 消耗

如何使用 spring 记录 RestTemplate 请求和响应?

Spring RestTemplate 专题

Spring Cloud Commons教程Spring RestTemplate作为负载平衡器客户端

Spring RestTemplate 超时

spring RestTemplate提交json格式数据