我怎样才能简单地添加一个Spring Data REST实体的链接

Des*_*tes 8 spring entity-framework jpa jooq spring-data-rest

我有我的实体与Spring Data JPA,但为了生成关于它们的统计数据,我在Spring中使用jOOQ @Repository.

由于我的方法返回List实体或a 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
  }
} 
Run Code Online (Sandbox Code Playgroud)

http:// localhost:8080/api/users/stats中,我想获得一个链接列表,其中包含我在jOOQ存储库中声明的方法.我该如何处理?谢谢.

Evg*_*rov 7

文档中看到这一点

@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;
     }
   };
}
Run Code Online (Sandbox Code Playgroud)


小智 6

添加链接的最佳方式是考虑 Spring-HATEOAS,它使代码看起来更简洁。

一个忠告:始终使用org.springframework.http.ResponseEntity将响应返回给客户端,因为它允许轻松自定义响应。

因此,由于您的要求是在响应中发送链接,因此建议的最佳实践是使用一种ResourceSupport(org.springframework.hateoas.ResourceSupport)ResourceAssemblerSupport(org.springframework.hateoas.mvc.ResourceAssemblerSupport)来创建资源这需要发送给客户端。

例如:如果你有一个像 Account 这样的模型对象,那么肯定有几个字段你不希望客户端知道或包含在响应中,以便从响应中排除这些属性,我们可以使用ResourceAssemblerSupport类'

公共 TResource toResource(T t);

方法从需要作为响应发送的模型对象生成资源。

比如我们有一个Account Class like(可以直接用于所有服务器端的交互和操作)

@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
}
Run Code Online (Sandbox Code Playgroud)

现在从这个 POJO,我们将创建一个 Resource 对象,该对象将被发送到具有选定属性的客户端。

为此,我们将创建一个帐户资源,该资源将仅包含对客户可见的必要字段。然后我们创建另一个类。

@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;
    }


}
Run Code Online (Sandbox Code Playgroud)

所以现在这个资源是客户将看到或必须使用的。

在创建 AccountResource 的蓝图后,我们需要一种方法将我们的 Model POJO 转换为这个资源,为此建议的最佳实践是创建一个 ResourceAssemblerSupport 类并覆盖 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;
    }

}
Run Code Online (Sandbox Code Playgroud)

在 toReource 方法中,而不是使用 intanriateReource(..) 我们必须使用 createdResourceWithId(id, entity) 然后将自定义链接添加到资源中,这实际上又是一个需要考虑的最佳实践,但为了演示,我使用了实例化资源(..)

现在在 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);
    }
Run Code Online (Sandbox Code Playgroud)

启用 Spring Data Web 支持,为代码添加更多功能,例如根据我们在前面的方法中使用的传递的 id 自动从 DB 获取模型数据。

现在返回 toResource(Account account) 方法:首先初始化资源对象,然后设置所需的道具,然后使用静态org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo将链接添加到 AccountResorce (..)方法,然后传入控制器类,从中选择基本 url,然后使用 slash(..) 构建 url,依此类推。指定完整路径后,我们使用 rel 方法来指定关系(就像这里我们使用 withSelfRel() 指定关系为 self。对于其他关系,我们可以使用 withRel(String relation) 来更具描述性。所以在我们在 toResource 方法中的代码我们使用了类似的东西 accountResource.add(ControllerLinkBuilder.linkTo(RegistrationController.class).slash(account.getId()).withSelfRel());

这会将 URL 构建为 /api/public/accounts/{userID}

现在在邮递员中,如果我们在这个 url http://localhost:8080/api/public/accounts上使用 get

{
    "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"
      }
    ]
  }
Run Code Online (Sandbox Code Playgroud)

单击任何链接并发送获取请求,响应将是 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"
      }
    ]
  }
Run Code Online (Sandbox Code Playgroud)

  • 这是不可接受的。太多样板了。一句话忠告:org.springframework.http.ResponseEntity 生成样板代码。您最终会在控制器中的任何地方使用这些 ResponseEntity,从而使其难以阅读和理解。如果您想处理诸如错误(未找到、冲突等)之类的特殊情况,您可以将 HandlerExceptionResolver 添加到您的 Spring 配置中。因此,在您的代码中,您只需抛出一个特定的异常(例如 NotFoundException)并决定在您的 Handler 中做什么(将 HTTP 状态设置为 404),使控制器代码更加清晰。 (5认同)
  • “太多的样板代码” le 它是 Java .. 添加 2000 行 XML,其中一半是自动生成的,现在您可以看到 Java 的大量样板代码。对 OP:我喜欢这种方法,我希望更多人意识到资源(表示)应该与域模型分开,然后您可以轻松推出新版本的“帐户” (2认同)