4

I'm using Spring boot 1.5.3, Spring Data REST, Spring HATEOAS. Spring Data REST is amazing and does a perfect job, but sometimes it's needed custom business logic and therfore I need to create a custom controller.

I'm going to use a @RepositoryRestController to benefit of Spring Data REST functionality (http://docs.spring.io/spring-data/rest/docs/current/reference/html/#customizing-sdr.overriding-sdr-response-handlers).

Because Spring Data REST use HATEOAS by default, I'm using that. I need a controller like this:

@RepositoryRestController
@RequestMapping(path = "/api/v1/workSessions")
public class WorkSessionController {

    @Autowired
    private EntityLinks entityLinks;

    @Autowired
    private WorkSessionRepository workSessionRepository;

    @Autowired
    private UserRepository userRepository;

    @PreAuthorize("isAuthenticated()")
    @RequestMapping(method = RequestMethod.POST, path = "/start")
    public ResponseEntity<?> start(@RequestBody(required = true) CheckPoint checkPoint) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (checkPoint == null) {
            throw new RuntimeException("Checkpoint cannot be empty");
        }

        if (workSessionRepository.findByAgentUsernameAndEndDateIsNull(auth.getName()).size() > 0) {
            // TODO return exception
            throw new RuntimeException("Exist a open work session for the user {0}");
        }

        // ...otherwise it's opened a new work session
        WorkSession workSession = new WorkSession();
        workSession.setAgent(userRepository.findByUsername(auth.getName()));
        workSession.setCheckPoint(checkPoint);
        workSession = workSessionRepository.save(workSession);

        Resource<WorkSession> resource = new Resource<>(workSession); 
        resource.add(entityLinks.linkFor(WorkSession.class).slash(workSession.getId()).withSelfRel());
        return ResponseEntity.ok(resource);
    }
}

Because the parameter CheckPoint must be an existant resource, I want the client send the link of the resourse (like you can do in Spring Data REST POST methods). Unfortunately when I try to do that, server side I receive an empty CheckPoint object.

I already read Resolving entity URI in custom controller (Spring HATEOAS) and converting URI to entity with custom controller in spring data rest? and expecially Accepting a Spring Data REST URI in custom controller.

I'm wondering if there is a best practice to do this avoiding to expose id to the client, followind so the HATEOAS principles.

drenda
  • 5,846
  • 11
  • 68
  • 141
  • Please show your POST request url and body. I think you should pass CheckPoint ID as parameter to the controller method.. – Cepr0 Jun 24 '17 at 14:25
  • @Cepr0 this is the url: http://localhost:8080/api/v1/workSessions/start and this is the body {"checkPoint":"http://localhost:8080/api/v1/checkPoints/1"} – drenda Jun 24 '17 at 14:33
  • See my answer... – Cepr0 Jun 24 '17 at 15:01

1 Answers1

2

Try to change you controller like this:

@RepositoryRestController
@RequestMapping(path = "/checkPoints")
public class CheckPointController {

    //...

    @Transactional
    @PostMapping("/{id}/start")
    public ResponseEntity<?> startWorkSession(@PathVariable("id") CheckPoint checkPoint) {

        //...           
    }
}

That will mean: "For CheckPoint with given ID start new WorkSession". Your POST request will be like: localhost:8080/api/v1/checkPoints/1/start.

Cepr0
  • 28,144
  • 8
  • 75
  • 101
  • 1
    I can't follow the first solution because is not HATEOAS compliant. I've not the id of the resource but just the full ui (e.g. http://localhost:8080/api/v1/checkPoints/1). The second idea to use a "container" doesn't work due to a Jackson deserializer exception. I'm looking for a Spring best practice to follow. – drenda Jun 24 '17 at 15:10
  • 1
    Sorry, maybe I expressed myself in a wrong way. I was trying a way to resolve entity uri in @RepositoryRestController like I wrote in the title. Your suggestion works for the specific case, but what if I need more parameters (entities)? The point is: have a way to convert uri -> entity. Thanks – drenda Jun 24 '17 at 15:24
  • I have the same problem. Yes I also read the suggestion with the ID in the path param. But I agree with @drenda The client does not know the ID (the number). The client only knows the full URI (which is the ID in spring-hateoas). – Robert Nov 17 '17 at 18:53
  • This is not working too. Because the request is handled by framework's internal controller called RepositoryPropertyReferenceController which inspects the resource for property and if the fragment after slash is not a property on the rest resource, then it throws 404 – Abdul Fatah Apr 07 '20 at 09:20