How can I simply add a link to a Spring Data REST Entity

entity-framework jooq jpa spring spring-data-rest

Accepted Answer

This is from 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;
     }
   };
}
7
12/15/2015 3:46:51 PM

Popular Answer

Consider Spring-HATEOAS as the best method for adding links to make your code look more cleaner.

One piece of advice: Since it makes it simple to customize responses, always use org.springframework.http.ResponseEntity when sending responses to customers.

As a result, using the types ResourceSupport(org.springframework.hateoas.ResourceSupport) and ResourceAssemblerSupport(org.springframework.hateoas.mvc.ResourceAssemblerSupport) to generate resources that must be given to the client is advised as your requirement is to deliver links in the response.

If you have a model object like Account, there must be a few fields that you do not want the client to be aware of or to have access to in the response. To do this, you can use the ResourceAssemblerSupport class.

toResource(T t); public TResource;

a way to create the resource from a model object that must be sent as a response.

For illustration, we have a class of accounts called (Can be directly used for all server side interaction and operations)

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

We will now generate a Resource object with the desired characteristics from this POJO and send it to the client.

To do this, We'll construct an Account resource that only has the Client-viewable fields that are Required. and after that, we develop a new 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;
    }


}

Therefore, the client will now see or have to use this resource.

In order to convert our Model POJO to this resource after constructing the AccountResource's blueprint, it is necessary to create a ResourceAssemblerSupport Class and override the toResource(T t) method.

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

}

To add the custum links to the resource, we must use createdResourceWithId(id, entity) in the toResource Method rather than instantiateResource(..), which is actually a best practice to take into consideration. However, for the purposes of illustration, I have used instantiateResource (..)

Use this in Controller now:

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

To activate Spring Data Web support, which gives your code a few extra features like automatically collecting model data from the database based on the passed-in id like we did in the previous approach.

Returning to the toResource(Account account) method, it first initializes the resource object, sets the desired props, adds links to the Account Resource using the static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo(..) method, takes the controller class as a parameter from which it selects the base url, builds the url using slash(..), and so on. We utilize the rel method to specify the relation once the whole path has been specified (in this case, we used withSelfRel() to indicate the relation to be it self). With withRel(String relation), we can be more descriptive for other relations. Therefore, in our code, we used something like accountResource.add(ControllerLinkBuilder.linkTo(RegistrationController.class).slash(account.getId()).withSelfRel()); in the toResource method.

resulting in the creation of the URL /api/public/accounts/userID.

Now, if we use a get on this address, http://localhost:8080/api/public/accounts, in Postman

{
    "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"
      }
    ]
  }

If you select any of the links and send a get request, you will receive the response 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"
      }
    ]
  }


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow