Adding more information to the HATEOAS response in

2019-04-12 21:07发布

问题:

I have the following REST controller.

@RepositoryRestController
@RequestMapping(value = "/booksCustom")
public class BooksController extends ResourceSupport {

    @Autowired
    public BooksService booksService;

    @Autowired
    private PagedResourcesAssembler<Books> booksAssembler;

    @RequestMapping("/search")
    public HttpEntity<PagedResources<Resource<Books>>> search(@RequestParam(value = "q", required = false) String query, @PageableDefault(page = 0, size = 20) Pageable pageable) {
        pageable = new PageRequest(0, 20);

        Page<Books> booksResult = BooksService.findBookText(query, pageable);

        return new ResponseEntity<PagedResources<Resource<Books>>>(BooksAssembler.toResource(BooksResult), HttpStatus.OK);

    }

My Page<Books> BooksResult = BooksService.findBookText(query, pageable); is backed by SolrCrudRepository. When it is run BookResult has several fields in it, the content field and several other fields, one being highlighted. Unfortunately the only thing I get back from the REST response is the data in the content field and the metadata information in the HATEOAS response (e.g. page information, links, etc.). What would be the proper way of adding the highlighted field to the response? I'm assuming I would need to modify the ResponseEntity, but unsure of the proper way.

Edit:

Model:

@SolrDocument(solrCoreName = "Books_Core")
public class Books {
    @Field
    private String id;

    @Field
    private String filename;

    @Field("full_text")
    private String fullText;

    //Getters and setters omitted 
    ...
}

When a search and the SolrRepository is called (e.g. BooksService.findBookText(query, pageable);) I get back these objects.

However, in my REST response I only see the "content". I would like to be able to add the "highlighted" object to the REST response. It just appears that HATEOAS is only sending the information in the "content" object (see below for the object).

{
  "_embedded" : {
    "solrBooks" : [ {
      "filename" : "ABookName",
      "fullText" : "ABook Text"
    } ]
  },
  "_links" : {
    "first" : {
      "href" : "http://localhost:8080/booksCustom/search?q=ABook&page=0&size=20"
    },
    "self" : {
      "href" : "http://localhost:8080/booksCustom/search?q=ABook"
    },
    "next" : {
      "href" : "http://localhost:8080/booksCustom/search?q=ABook&page=0&size=20"
    },
    "last" : {
      "href" : "http://localhost:8080/booksCustom/search?q=ABook&page=0&size=20"
    }
  },
  "page" : {
    "size" : 1,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}

Just so you can get a full picture, this is the repository that is backing the BooksService. All the service does is call this SolrCrudRepository method.

public interface SolrBooksRepository extends SolrCrudRepository<Books, String> {

    @Highlight(prefix = "<highlight>", postfix = "</highlight>", fragsize = 20, snipplets = 3)
    HighlightPage<SolrTestDocuments> findBookText(@Param("fullText") String fullText, Pageable pageable);

}

回答1:

Ok, here is how I did it: I wrote mine HighlightPagedResources

public class HighlightPagedResources<R,T> extends PagedResources<R> {

    private List<HighlightEntry<T>> phrases;

    public HighlightPagedResources(Collection<R> content, PageMetadata metadata, List<HighlightEntry<T>> highlightPhrases, Link... links) {
        super(content, metadata, links);
        this.phrases = highlightPhrases;
    }

    @JsonProperty("highlighting")
    public List<HighlightEntry<T>> getHighlightedPhrases() {
        return phrases;
    }
}

and HighlightPagedResourcesAssembler:

public class HighlightPagedResourcesAssembler<T> extends PagedResourcesAssembler<T> {

    public HighlightPagedResourcesAssembler(HateoasPageableHandlerMethodArgumentResolver resolver, UriComponents baseUri) {
        super(resolver, baseUri);
    }


    public <R extends ResourceSupport> HighlightPagedResources<R,T> toResource(HighlightPage<T> page, ResourceAssembler<T, R> assembler) {
        final PagedResources<R> rs = super.toResource(page, assembler);
        final Link[] links = new Link[rs.getLinks().size()];
        return new HighlightPagedResources<R, T>(rs.getContent(), rs.getMetadata(), page.getHighlighted(), rs.getLinks().toArray(links));
    }
}

I had to add to my spring RepositoryRestMvcConfiguration.java:

@Primary
@Bean
public HighlightPagedResourcesAssembler solrPagedResourcesAssembler() {
    return new HighlightPagedResourcesAssembler<Object>(pageableResolver(), null);
}

In cotroller I had to change PagedResourcesAssembler for newly implemented one and also use new HighlightPagedResources in request method:

@Autowired
private HighlightPagedResourcesAssembler<Object> highlightPagedResourcesAssembler;

@RequestMapping(value = "/conversations/search", method = POST)

public HighlightPagedResources<PersistentEntityResource, Object> findAll(
        @RequestBody ConversationSearch search,
        @SortDefault(sort = FIELD_LATEST_SEGMENT_START_DATE_TIME, direction = DESC) Pageable pageable,
        PersistentEntityResourceAssembler assembler) {

    HighlightPage page = conversationRepository.findByConversationSearch(search, pageable);
    return highlightPagedResourcesAssembler.toResource(page, assembler);
}

RESULT:

  {
  "_embedded": {
    "conversations": [
    ..our stuff..
    ]
  },
  "_links": {
    ...as you know them...
  },
  "page": {
    "size": 1,
    "totalElements": 25,
    "totalPages": 25,
    "number": 0
  },
  "highlighting": [
    {
      "entity": {
        "conversationId": "a2127d01-747e-4312-b230-01c63dacac5a",
        ...
      },
      "highlights": [
        {
          "field": {
            "name": "textBody"
          },
          "snipplets": [
            "Additional XXX License for YYY Servers DCL-2016-PO0422 \n   \n<em>hi</em> bodgan  \n    \nwe urgently need the",
            "Additional XXX License for YYY Servers DCL-2016-PO0422\n \n<em>hi</em> bodgan\n \nwe urgently need the permanent"
          ]
        }
      ]
    }
  ]
}


回答2:

I was using Page<Books> instead of HighlightPage to create the response page. Page obviously doesn't contain content which was causing the highlighted portion to be truncated. I ended up creating a new page based off of HighlightPage and returning that as my result instead of Page.

@RepositoryRestController
@RequestMapping(value = "/booksCustom")
public class BooksController extends ResourceSupport {

    @Autowired
    public BooksService booksService;

    @Autowired
    private PagedResourcesAssembler<Books> booksAssembler;

    @RequestMapping("/search")
    public HttpEntity<PagedResources<Resource<HighlightPage>>> search(@RequestParam(value = "q", required = false) String query, @PageableDefault(page = 0, size = 20) Pageable pageable) {

        HighlightPage solrBookResult = booksService.findBookText(query, pageable);
        Page<Books> highlightedPages = new PageImpl(solrBookResult.getHighlighted(), pageable, solrBookResult.getTotalElements());
        return new ResponseEntity<PagedResources<Resource<HighlightPage>>>(booksAssembler.toResource(highlightedPages), HttpStatus.OK); 
    }

Probably a better way of doing this, but I couldn't find anything that would do what I wanted it to do without having a change a ton of code. Hope this helps!