Spring 启动 HATEOAS 输出失败

Spring Boot HATEOAS Output Fails

我的实体在 Sprint Boot REST HATEOAS 服务中的输出不起作用。服务 returns 每个实体的空字符串。没有错误消息。我试过 Spring Boot 1.5.4 和 2.0.0.RC1。

完整的源代码在 GitHub: https://github.com/murygin/hateoas-people-service

申请

@SpringBootApplication
@Configuration
@EnableHypermediaSupport(type={EnableHypermediaSupport.HypermediaType.HAL})
public class Application {   
  public static void main(String args[]) {
    SpringApplication.run(Application.class);
  }
}

个人控制器

@RestController
@RequestMapping(value = "/persons", produces = "application/hal+json")
public class PersonController {

  @GetMapping
  public ResponseEntity<Resources<PersonResource>> all() {
    final List<PersonResource> collection =
        getPersonList().stream().map(PersonResource::new).collect(Collectors.toList());
    final Resources<PersonResource> resources = new Resources<>(collection);
    final String uriString = ServletUriComponentsBuilder.fromCurrentRequest().build().toUriString();
    resources.add(new Link(uriString, "self"));
    return ResponseEntity.ok(resources);
  }

  @GetMapping("/{id}")
  public ResponseEntity<PersonResource> get(@PathVariable final long id) {
    Person p = new Person((long)1,"Donald","Duck");
    return ResponseEntity.ok(new PersonResource(p));
  }

  private List<Person> getPersonList() {
    List<Person> personList = new LinkedList<>();
    personList.add(new Person((long)1,"Donald","Duck"));
    personList.add(new Person((long)2,"Dagobert","Duck"));
    personList.add(new Person((long)3,"Daniel","Duesentrieb"));
    return personList;
  }

}

人物资源

public class PersonResource extends ResourceSupport {

  private final Person person;

  public PersonResource(final Person person) {
    this.person = person;
    final long id = person.getId();
    add(linkTo(PersonController.class).withRel("people"));
    add(linkTo(methodOn(PersonController.class).get(id)).withSelfRel());
  }
}

public class Person {    
  private Long id;   
  private String firstName;   
  private String secondName;

  public Person() {
  }

  public Person(Long id, String firstName, String secondName) {
    this.id = id;
    this.firstName = firstName;
    this.secondName = secondName;
  }

  // getter and setter...
}

http://localhost:8080/persons

的输出
    {
        _embedded: {
            personResourceList: [{
                    _links: {
                        people: {
                            href: "http://localhost:8080/persons"
                        },
                        self: {
                            href: "http://localhost:8080/persons/1"
                        }
                    }
                },
                {
                    _links: {
                        people: {
                            href: "http://localhost:8080/persons"
                        },
                        self: {
                            href: "http://localhost:8080/persons/2"
                        }
                    }
                },
                {
                    _links: {
                        people: {
                            href: "http://localhost:8080/persons"
                        },
                        self: {
                            href: "http://localhost:8080/persons/3"
                        }
                    }
                }
            ]
        },
        _links: {
            self: {
                href: "http://localhost:8080/persons"
            }
        }
    }

http://localhost:8080/persons/1

的输出
    {
        _links: {
            people: {
                href: "http://localhost:8080/persons"
            },
            self: {
                href: "http://localhost:8080/persons/1"
            }
        }
    }

为 PersonResource 中的人员添加 getter:

public class PersonResource extends ResourceSupport {

  private final Person person;

  public PersonResource(final Person person) {
    this.person = person;
    final long id = person.getId();
    add(linkTo(PersonController.class).withRel("people"));
    add(linkTo(methodOn(PersonController.class).get(id)).withSelfRel());
  }

    public Person getPerson() {
        return person;
    }
}

使用 getter,Spring 将 person 包裹在您的 PersonResource 中并将其序列化:

获取http://localhost:8080/persons/1

{
  "person" : {
    "id" : 1,
    "firstName" : "Donald",
    "secondName" : "Duck"
  },
  "_links" : {
    "people" : {
      "href" : "http://localhost:8080/persons"
    },
    "self" : {
      "href" : "http://localhost:8080/persons/1"
    }
  }
}

获取http://localhost:8080/persons

{
  "_embedded" : {
    "personResources" : [ {
      "person" : {
        "id" : 1,
        "firstName" : "Donald",
        "secondName" : "Duck"
      },
      "_links" : {
        "people" : {
          "href" : "http://localhost:8080/persons"
        },
        "self" : {
          "href" : "http://localhost:8080/persons/1"
        }
      }
    }, {
      "person" : {
        "id" : 2,
        "firstName" : "Dagobert",
        "secondName" : "Duck"
      },
      "_links" : {
        "people" : {
          "href" : "http://localhost:8080/persons"
        },
        "self" : {
          "href" : "http://localhost:8080/persons/2"
        }
      }
    }, {
      "person" : {
        "id" : 3,
        "firstName" : "Daniel",
        "secondName" : "Duesentrieb"
      },
      "_links" : {
        "people" : {
          "href" : "http://localhost:8080/persons"
        },
        "self" : {
          "href" : "http://localhost:8080/persons/3"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/persons"
    }
  }
}

注意:我是个懒惰的流浪汉,我在 pom.xml 依赖项中添加了 spring-boot-starter-data-rest 以漂亮地打印结果,因此您的实际结果可能会有所不同。

我建议您使用 Resource 而不是 ResourceSupport 的扩展来针对您的案例采取侵入性较小的方法。您的代码将如下所示:

@RestController
@RequestMapping(value = "/persons", produces = "application/hal+json")
public class PersonController {

  @GetMapping
  public ResponseEntity<List<Resource<Person>>> all() {
    final List<Resource<Person>> collection =
        getPersonList().stream()
                .map(p -> new Resource<>(p, this.getLinks(p.getId())))
                .collect(Collectors.toList());
    return ResponseEntity.ok(collection);
  }

  @GetMapping("/{id}")
  public ResponseEntity<Resource<Person>> get(@PathVariable final long id) {
    Person p = new Person(id,"Donald","Duck");
    Resource<Person> resource = new Resource<>(p, this.getLinks(id));
    return ResponseEntity.ok(resource);
  }

  private List<Person> getPersonList() {
    List<Person> personList = new LinkedList<>();
    personList.add(new Person((long)1,"Donald","Duck"));
    personList.add(new Person((long)2,"Dagobert","Duck"));
    personList.add(new Person((long)3,"Daniel","Duesentrieb"));
    return personList;
  }

  private List<Link> getLinks(long id) {
      return Arrays.asList(
              linkTo(PersonController.class).withRel("people"),
              linkTo(methodOn(getClass()).get(id)).withSelfRel());
  }
}

输出更清晰:

获取http://localhost:8080/persons/1

{
   "id": 1,
   "firstName": "Donald",
   "secondName": "Duck",
   "_links":    {
      "people": {"href": "http://localhost:8080/persons"},
      "self": {"href": "http://localhost:8080/persons/1"}
   }
}

获取http://localhost:8080/persons

[
   {
      "id": 1,
      "firstName": "Donald",
      "secondName": "Duck",
      "_links":       {
         "people": {"href": "http://localhost:8080/persons"},
         "self": {"href": "http://localhost:8080/persons/1"}
      }
   },
   {
      "id": 2,
      "firstName": "Dagobert",
      "secondName": "Duck",
      "_links":       {
         "people": {"href": "http://localhost:8080/persons"},
         "self": {"href": "http://localhost:8080/persons/2"}
      }
   },
   {
      "id": 3,
      "firstName": "Daniel",
      "secondName": "Duesentrieb",
      "_links":       {
         "people": {"href": "http://localhost:8080/persons"},
         "self": {"href": "http://localhost:8080/persons/3"}
      }
   }
]

换个角度,希望对你有帮助。