0

I am not a proffessional Spring Boot developer. I am writing a program with help from a tutorial and I am using Hateoas and Rest and when I am returning the response from a request I get a whitelabel error page.

The resourceobject itself looks to be correct when I try to write the values by using System.out.println().

This is the RestController-method.

@RequestMapping(method = RequestMethod.GET ,produces = {MediaType.APPLICATION_JSON_VALUE, "application/hal+json"})
    Resources<SubjectResource> readSubject(@PathVariable String userId) {

        this.validateUser(userId);
        List<SubjectResource>subjectResource=subjectRepository.findByTeacherUsername(userId).stream().map(SubjectResource:: new).collect(Collectors.toList());

        Resources <SubjectResource>r=new Resources<>(subjectResource);

        return new Resources<>(subjectResource);

    }

This is the imports I use in the RestController-class

import java.net.URI;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.hateoas.Link;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Resources;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

and this is the Resourcefile

import org.springframework.hateoas.Link;

import org.springframework.hateoas.ResourceSupport; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;

class SubjectResource extends ResourceSupport {

    private final Subject subject;

    public SubjectResource(Subject subject) {

        String username = subject.getTeacher().getUsername();
        this.subject = subject;
        this.add(new Link(subject.uri, "subject-uri"));

        this.add(linkTo(SubjectRestController.class, username).withRel("subjects"));

        this.add(linkTo(
             methodOn(SubjectRestController.class, username).readSubject(username,
                    subject.getId())).withSelfRel());




    }

    public Subject getSubject() {
        return subject;
    }
}

and in my browser I typ

http://localhost:8080/anna_n/subjects

Do anyone know what I am doing wrong? Thanks /Micke

bajen micke
  • 309
  • 1
  • 7
  • 18
  • By the look of your code, I'd say you should build a `Resources`, not a `Resources`. But first, you need to state your problem better: show us the `Subject` class, tell us what you which to get when you call `/anna_n/subjects`. You can also clean your code sample by removing unnecessary blank lines and also the import statements (you're using standard Java and Spring classes so the imports bring no relevant information here). It will make it easier for us to help you. – Marc Tarin Jan 26 '18 at 14:57
  • Thanks. Will check that and add the extra code when I get home – bajen micke Jan 26 '18 at 18:28

1 Answers1

1

First of all lets start with an example to compare with your code:

@RestController
@ExposesResourceFor(Booleans.class)
@RequestMapping("/booleans")
public class BooleansController {

    public static final String TRUE = "true";
    public static final String FALSE = "false";
    public static final String TRUEFALSE = "truefalse";

    @Autowired
    private EntityLinks entityLinks;

    @RequestMapping(value = "/{booleansId}",
            produces = { "application/hal+json", "application/json" },
            method = RequestMethod.GET)
    public ResponseEntity<Booleans> getBooleans(@PathVariable("booleansId") String booleansId) {
        return getResource(booleansId)
                .map(r -> new ResponseEntity<>(r, HttpStatus.OK))
                .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    private Optional<Booleans> getResource(String id)  {
        switch (id) {
            case TRUE:
                final Booleans trueInstance = new Booleans().addValuesItem(true);
                trueInstance.add(entityLinks.linkToSingleResource(Booleans.class, TRUE));
                return Optional.of(trueInstance);
            case FALSE:
                final Booleans falseInstance = new Booleans().addValuesItem(false);
                falseInstance.add(entityLinks.linkToSingleResource(Booleans.class, FALSE));
                return Optional.of(falseInstance);
            case TRUEFALSE:
                final Booleans truefalseInstance = new Booleans().addValuesItem(true).addValuesItem(false);
                truefalseInstance.add(entityLinks.linkToSingleResource(Booleans.class, TRUEFALSE));
                return Optional.of(truefalseInstance);
            default:
                return Optional.empty();
        }
    }

}

And the resource:

public class Booleans extends ResourceSupport {

    @JsonProperty("values")
    private List<Boolean> values = new ArrayList<Boolean>();

    public Booleans addValuesItem(Boolean valuesItem) {
        this.values.add(valuesItem);
        return this;
    }

    public List<Boolean> getValues() {
        return values;
    }
    public void setValues(List<Boolean> values) {
       this.values = values;
    }

}

Try with curl:

curl "http://localhost:8085/myapp/booleans/truefalse"

{
  "_links": {
    "self": {
      "href": "http://localhost:8085/myapp/booleans/truefalse"
    }
  },
  "values": [
    true,
    false
  ]
}

A notable difference is @RequestMapping(value = "/{booleansId}",, which is missing from your code. That is my first bet. Try @RequestMapping(value = "/{userId}/subjects"

I have updated this answer to use EntityLinks and @ExposesResourceFor annotation together with a class and method level @RequestMapping to demonstrate how to ensure proper JSON HAL links for Resources. See ResourceAssemblerSupport for a way to build proper resources.

Christer B
  • 155
  • 1
  • 1
  • 7