我怎样才能简单地将 link 添加到 Spring 数据 REST 实体
How can I simply add a link to a Spring Data REST Entity
我的实体使用 Spring Data JPA,但为了生成关于它们的统计信息,我在 Spring @Repository
.
中使用 jOOQ
由于我的方法 return 是 List
实体或 Double
,我如何将它们公开为链接?假设我有一个 User
实体,我想获得以下 JSON:
{
"_embedded" : {
"users" : [ ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/api/users"
},
"stats" : {
"href" : "http://localhost:8080/api/users/stats"
}
"profile" : {
"href" : "http://localhost:8080/api/profile/users"
}
},
"page" : {
"size" : 20,
"totalElements" : 0,
"totalPages" : 0,
"number" : 0
}
}
在 http://localhost:8080/api/users/stats 中,我想获得一个链接列表,其中包含我在 jOOQ 存储库中声明的方法。我将如何处理这个?谢谢。
从docs
看到这个
@Bean
public ResourceProcessor<Resource<Person>> personProcessor() {
return new ResourceProcessor<Resource<Person>>() {
@Override
public Resource<Person> process(Resource<Person> resource) {
resource.add(new Link("http://localhost:8080/people", "added-link"));
return resource;
}
};
}
添加 link 的最佳方法是考虑 Spring-HATEOAS,它使代码看起来更干净。
忠告:始终使用 org.springframework.http.ResponseEntity 将响应返回给客户端,因为它可以轻松自定义响应。
因此,由于您的要求是在响应中发送 link,因此建议的最佳做法是使用 ResourceSupport(org.springframework.hateoas.ResourceSupport) 和 ResourceAssemblerSupport(org.springframework.hateoas.mvc.ResourceAssemblerSupport) 创建需要发送给客户端的资源。
例如:
如果您有一个像 Account 这样的模型对象,那么肯定有一些您不希望客户端知道或不包含在响应中的字段,因此要从响应中排除这些属性,我们可以使用 ResourceAssemblerSupport class'
public TResource toResource(T t);
从需要作为响应发送的模型对象生成资源的方法。
比如我们有一个类似Class的账户(可以直接用于所有服务器端的交互和操作)
@Document(collection = "Accounts_Details")
public class Account {
@Id
private String id;
private String username;
private String password;
private String firstName;
private String lastName;
private String emailAddress;
private String role;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private boolean enabled;
private long accountNonLockedCounter;
private Date lastPasswordResetDate;
private Address address;
private long activationCode;
public Account() {
}
//getters and setters
}
现在,我们将从这个 POJO 创建一个 Resource 对象,该对象将使用选定的属性发送到客户端。
为此,我们将创建一个帐户资源,其中仅包含客户可见的必要字段。然后我们创建另一个 class.
@XmlRootElement
public class AccountResource extends ResourceSupport {
@XmlAttribute
private String username;
@XmlAttribute
private String firstName;
@XmlAttribute
private String lastName;
@XmlAttribute
private String emailAddress;
@XmlAttribute
private Address address;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
所以现在这个资源就是客户将要看到或必须使用的资源。
创建 AccountResource 的蓝图后,我们需要一种方法将 Model POJO 转换为该资源,为此建议的最佳做法是创建 ResourceAssemblerSupport Class 并覆盖 toResource(T t) 方法.
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.hateoas.mvc.ResourceAssemblerSupport;
import org.springframework.stereotype.Component;
import com.brx.gld.www.api.controller.RegistrationController;
import com.brx.gld.www.api.model.Account;
@Component
public class AccountResourceAssembler extends ResourceAssemblerSupport<Account, AccountResource> {
public AccountResourceAssembler(Class<RegistrationController> controllerClass,
Class<AccountResource> resourceType) {
super(controllerClass, resourceType);
}
public AccountResourceAssembler() {
this(RegistrationController.class, AccountResource.class);
}
@Override
public AccountResource toResource(Account account) {
AccountResource accountResource = instantiateResource(account); //or createResourceWithId(id, entity) canbe used which will automatically create a link to itself.
accountResource.setAddress(account.getAddress());
accountResource.setFirstName(account.getFirstName());
accountResource.setLastName(account.getLastName());
accountResource.setEmailAddress(account.getEmailAddress());
accountResource.setUsername(account.getUsername());
accountResource.removeLinks();
accountResource.add(ControllerLinkBuilder.linkTo(RegistrationController.class).slash(account.getId()).withSelfRel());
return accountResource;
}
}
在 toReource 方法中,我们必须使用 createdResourceWithId(id, entity) 而不是使用 instanriateReource(..),然后将 custum links 添加到资源中,这实际上也是一个值得考虑的最佳实践,但为了演示,我使用了 instantiateResource(..)
现在在 Controller 中使用它:
@Controller
@RequestMapping("/api/public/accounts")
public class RegistrationController {
@Autowired
private AccountService accountService;
@Autowired
private AccountResourceAssembler accountResourceAssembler;
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<AccountResource>> getAllRegisteredUsers() {
List<AccountResource> accountResList = new ArrayList<AccountResource>();
for (Account acnt : accountService.findAllAccounts())
accountResList.add(this.accountResourceAssembler.toResource(acnt));
return new ResponseEntity<List<AccountResource>>(accountResList, HttpStatus.OK);
}
/*Use the below method only if you have enabled spring data web Support or otherwise instead of using Account in @PathVariable usr String id or int id depending on what type to id you have in you db*/
@RequestMapping(value = "{userID}", method = RequestMethod.GET)
public ResponseEntity<AccountResource> getAccountForID(@PathVariable("userID") Account fetchedAccountForId) {
return new ResponseEntity<AccountResource>(
this.accountResourceAssembler.toResource(fetchedAccountForId), HttpStatus.OK);
}
要启用 Spring 数据 Web 支持,它会为您的代码添加更多功能,例如根据我们在之前的方法中使用的传递的 id 从数据库中自动获取模型数据。
现在回到 toResource(Account account) 方法:首先初始化资源对象,然后设置所需的道具,然后使用静态 links 添加到 AccountResorce org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo(..) 方法,然后控制器 class 被传入,它从中选择基地 url 然后 url 是使用 slash(..) 等构建的。指定完整路径后,我们使用 rel 方法指定关系(就像这里我们使用 withSelfRel() 指定关系是它自己。对于其他关系,我们可以使用 withRel(String relation) 来更具描述性。
所以在我们的 toResource 方法的代码中,我们使用了类似
accountResource.add(ControllerLinkBuilder.linkTo(RegistrationController.class).slash(account.getId()).withSelfRel());
这会将 URL 构建为 /api/public/accounts/{userID}
现在在 postman 中,如果我们在此使用 get url
http://localhost:8080/api/public/accounts
{
"username": "Arif4",
"firstName": "xyz",
"lastName": "Arif",
"emailAddress": "xyz@outlook.com",
"address": {
"addressLine1": "xyz",
"addressLine2": "xyz",
"addressLine3": "xyz",
"city": "xyz",
"state": "xyz",
"zipcode": "xyz",
"country": "India"
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/api/public/accounts/5628b95306bf022f33f0c4f7"
}
]
},
{
"username": "Arif5",
"firstName": "xyz",
"lastName": "Arif",
"emailAddress": "xyz@gmail.com",
"address": {
"addressLine1": "xyz",
"addressLine2": "xyz",
"addressLine3": "xyz",
"city": "xyz",
"state": "xyz",
"zipcode": "xyz",
"country": "India"
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/api/public/accounts/5628c04406bf23ea911facc0"
}
]
}
单击任何 link 并发送获取请求,响应将是
http://localhost:8080/api/public/accounts/5628c04406bf23ea911facc0
{
"username": "Arif5",
"firstName": "xyz",
"lastName": "Arif",
"emailAddress": "xyz@gmail.com",
"address": {
"addressLine1": "xyz",
"addressLine2": "xyz",
"addressLine3": "xyz",
"city": "xyz",
"state": "xyz",
"zipcode": "xyz",
"country": "India"
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/api/public/accounts/5628c04406bf23ea911facc0"
}
]
}
有关手动创建链接的信息,请参阅 spring-hateoas-examples。
最简单的 wai 是 via new Resource
如果没有 DTO 和 extends ResourceSupport
对于 DTO。
指向 spring-data-rest 托管实体的链接我已自定义类似于 links to root resource:
MyController implements ResourceProcessor<Resource<ManagedEntity>> {
@Override
public Resource<Restaurant> process(Resource<ManagedEntity> resource) {
resource.add(linkTo(methodOn(MyController.class)
.myMethod(resource.getContent().getId(), ...)).withRel("..."));
return resource;
}
对于分页资源
MyController implements ResourceProcessor<PagedResources<Resource<ManagedEntity>>>
问题是当您同时需要两者时,因为通用类型擦除,您无法扩展这两个接口。作为黑客,我创建了虚拟 ResourceController
https://docs.spring.io/spring-hateoas/docs/current/reference/html/#reference
public class PaymentProcessor implements RepresentationModelProcessor<EntityModel<Order>> {
@Override
public EntityModel<Order> process(EntityModel<Order> model) {
model.add(
Link.of("/payments/{orderId}").withRel(LinkRelation.of("payments")) //
.expand(model.getContent().getOrderId()));
return model;
}
}
ResourceSupport is now RepresentationModel
Resource is now EntityModel
Resources is now CollectionModel
PagedResources is now PagedModel
我的实体使用 Spring Data JPA,但为了生成关于它们的统计信息,我在 Spring @Repository
.
由于我的方法 return 是 List
实体或 Double
,我如何将它们公开为链接?假设我有一个 User
实体,我想获得以下 JSON:
{
"_embedded" : {
"users" : [ ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/api/users"
},
"stats" : {
"href" : "http://localhost:8080/api/users/stats"
}
"profile" : {
"href" : "http://localhost:8080/api/profile/users"
}
},
"page" : {
"size" : 20,
"totalElements" : 0,
"totalPages" : 0,
"number" : 0
}
}
在 http://localhost:8080/api/users/stats 中,我想获得一个链接列表,其中包含我在 jOOQ 存储库中声明的方法。我将如何处理这个?谢谢。
从docs
看到这个@Bean
public ResourceProcessor<Resource<Person>> personProcessor() {
return new ResourceProcessor<Resource<Person>>() {
@Override
public Resource<Person> process(Resource<Person> resource) {
resource.add(new Link("http://localhost:8080/people", "added-link"));
return resource;
}
};
}
添加 link 的最佳方法是考虑 Spring-HATEOAS,它使代码看起来更干净。
忠告:始终使用 org.springframework.http.ResponseEntity 将响应返回给客户端,因为它可以轻松自定义响应。
因此,由于您的要求是在响应中发送 link,因此建议的最佳做法是使用 ResourceSupport(org.springframework.hateoas.ResourceSupport) 和 ResourceAssemblerSupport(org.springframework.hateoas.mvc.ResourceAssemblerSupport) 创建需要发送给客户端的资源。
例如: 如果您有一个像 Account 这样的模型对象,那么肯定有一些您不希望客户端知道或不包含在响应中的字段,因此要从响应中排除这些属性,我们可以使用 ResourceAssemblerSupport class'
public TResource toResource(T t);
从需要作为响应发送的模型对象生成资源的方法。
比如我们有一个类似Class的账户(可以直接用于所有服务器端的交互和操作)
@Document(collection = "Accounts_Details")
public class Account {
@Id
private String id;
private String username;
private String password;
private String firstName;
private String lastName;
private String emailAddress;
private String role;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private boolean enabled;
private long accountNonLockedCounter;
private Date lastPasswordResetDate;
private Address address;
private long activationCode;
public Account() {
}
//getters and setters
}
现在,我们将从这个 POJO 创建一个 Resource 对象,该对象将使用选定的属性发送到客户端。
为此,我们将创建一个帐户资源,其中仅包含客户可见的必要字段。然后我们创建另一个 class.
@XmlRootElement
public class AccountResource extends ResourceSupport {
@XmlAttribute
private String username;
@XmlAttribute
private String firstName;
@XmlAttribute
private String lastName;
@XmlAttribute
private String emailAddress;
@XmlAttribute
private Address address;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
所以现在这个资源就是客户将要看到或必须使用的资源。
创建 AccountResource 的蓝图后,我们需要一种方法将 Model POJO 转换为该资源,为此建议的最佳做法是创建 ResourceAssemblerSupport Class 并覆盖 toResource(T t) 方法.
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.hateoas.mvc.ResourceAssemblerSupport;
import org.springframework.stereotype.Component;
import com.brx.gld.www.api.controller.RegistrationController;
import com.brx.gld.www.api.model.Account;
@Component
public class AccountResourceAssembler extends ResourceAssemblerSupport<Account, AccountResource> {
public AccountResourceAssembler(Class<RegistrationController> controllerClass,
Class<AccountResource> resourceType) {
super(controllerClass, resourceType);
}
public AccountResourceAssembler() {
this(RegistrationController.class, AccountResource.class);
}
@Override
public AccountResource toResource(Account account) {
AccountResource accountResource = instantiateResource(account); //or createResourceWithId(id, entity) canbe used which will automatically create a link to itself.
accountResource.setAddress(account.getAddress());
accountResource.setFirstName(account.getFirstName());
accountResource.setLastName(account.getLastName());
accountResource.setEmailAddress(account.getEmailAddress());
accountResource.setUsername(account.getUsername());
accountResource.removeLinks();
accountResource.add(ControllerLinkBuilder.linkTo(RegistrationController.class).slash(account.getId()).withSelfRel());
return accountResource;
}
}
在 toReource 方法中,我们必须使用 createdResourceWithId(id, entity) 而不是使用 instanriateReource(..),然后将 custum links 添加到资源中,这实际上也是一个值得考虑的最佳实践,但为了演示,我使用了 instantiateResource(..)
现在在 Controller 中使用它:
@Controller
@RequestMapping("/api/public/accounts")
public class RegistrationController {
@Autowired
private AccountService accountService;
@Autowired
private AccountResourceAssembler accountResourceAssembler;
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<AccountResource>> getAllRegisteredUsers() {
List<AccountResource> accountResList = new ArrayList<AccountResource>();
for (Account acnt : accountService.findAllAccounts())
accountResList.add(this.accountResourceAssembler.toResource(acnt));
return new ResponseEntity<List<AccountResource>>(accountResList, HttpStatus.OK);
}
/*Use the below method only if you have enabled spring data web Support or otherwise instead of using Account in @PathVariable usr String id or int id depending on what type to id you have in you db*/
@RequestMapping(value = "{userID}", method = RequestMethod.GET)
public ResponseEntity<AccountResource> getAccountForID(@PathVariable("userID") Account fetchedAccountForId) {
return new ResponseEntity<AccountResource>(
this.accountResourceAssembler.toResource(fetchedAccountForId), HttpStatus.OK);
}
要启用 Spring 数据 Web 支持,它会为您的代码添加更多功能,例如根据我们在之前的方法中使用的传递的 id 从数据库中自动获取模型数据。
现在回到 toResource(Account account) 方法:首先初始化资源对象,然后设置所需的道具,然后使用静态 links 添加到 AccountResorce org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo(..) 方法,然后控制器 class 被传入,它从中选择基地 url 然后 url 是使用 slash(..) 等构建的。指定完整路径后,我们使用 rel 方法指定关系(就像这里我们使用 withSelfRel() 指定关系是它自己。对于其他关系,我们可以使用 withRel(String relation) 来更具描述性。 所以在我们的 toResource 方法的代码中,我们使用了类似 accountResource.add(ControllerLinkBuilder.linkTo(RegistrationController.class).slash(account.getId()).withSelfRel());
这会将 URL 构建为 /api/public/accounts/{userID}
现在在 postman 中,如果我们在此使用 get url http://localhost:8080/api/public/accounts
{
"username": "Arif4",
"firstName": "xyz",
"lastName": "Arif",
"emailAddress": "xyz@outlook.com",
"address": {
"addressLine1": "xyz",
"addressLine2": "xyz",
"addressLine3": "xyz",
"city": "xyz",
"state": "xyz",
"zipcode": "xyz",
"country": "India"
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/api/public/accounts/5628b95306bf022f33f0c4f7"
}
]
},
{
"username": "Arif5",
"firstName": "xyz",
"lastName": "Arif",
"emailAddress": "xyz@gmail.com",
"address": {
"addressLine1": "xyz",
"addressLine2": "xyz",
"addressLine3": "xyz",
"city": "xyz",
"state": "xyz",
"zipcode": "xyz",
"country": "India"
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/api/public/accounts/5628c04406bf23ea911facc0"
}
]
}
单击任何 link 并发送获取请求,响应将是 http://localhost:8080/api/public/accounts/5628c04406bf23ea911facc0
{
"username": "Arif5",
"firstName": "xyz",
"lastName": "Arif",
"emailAddress": "xyz@gmail.com",
"address": {
"addressLine1": "xyz",
"addressLine2": "xyz",
"addressLine3": "xyz",
"city": "xyz",
"state": "xyz",
"zipcode": "xyz",
"country": "India"
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/api/public/accounts/5628c04406bf23ea911facc0"
}
]
}
有关手动创建链接的信息,请参阅 spring-hateoas-examples。
最简单的 wai 是 via new Resource
如果没有 DTO 和 extends ResourceSupport
对于 DTO。
指向 spring-data-rest 托管实体的链接我已自定义类似于 links to root resource:
MyController implements ResourceProcessor<Resource<ManagedEntity>> {
@Override
public Resource<Restaurant> process(Resource<ManagedEntity> resource) {
resource.add(linkTo(methodOn(MyController.class)
.myMethod(resource.getContent().getId(), ...)).withRel("..."));
return resource;
}
对于分页资源
MyController implements ResourceProcessor<PagedResources<Resource<ManagedEntity>>>
问题是当您同时需要两者时,因为通用类型擦除,您无法扩展这两个接口。作为黑客,我创建了虚拟 ResourceController
https://docs.spring.io/spring-hateoas/docs/current/reference/html/#reference
public class PaymentProcessor implements RepresentationModelProcessor<EntityModel<Order>> {
@Override
public EntityModel<Order> process(EntityModel<Order> model) {
model.add(
Link.of("/payments/{orderId}").withRel(LinkRelation.of("payments")) //
.expand(model.getContent().getOrderId()));
return model;
}
}
ResourceSupport is now RepresentationModel
Resource is now EntityModel
Resources is now CollectionModel
PagedResources is now PagedModel