web-dev-qa-db-ja.com

Spring-Data-Rest / Spring-Hateoasで生成されたクラスのフォーマットをカスタムコントローラーにミラーリングさせることはできますか?

私は本当に簡単なはずだと思うことをやろうとしています。 Questionオブジェクトがあり、spring-boot、spring-data-rest、spring-hateoasでセットアップされています。すべての基本は正常に機能します。 List<Question>Repository/questions URLへのGETとまったく同じ形式で返すカスタムコントローラーを追加して、2つの間の応答に互換性を持たせたいと思います。

これが私のコントローラーです:

@Controller
public class QuestionListController {

    @Autowired private QuestionRepository questionRepository;

    @Autowired private PagedResourcesAssembler<Question> pagedResourcesAssembler;

    @Autowired private QuestionResourceAssembler questionResourceAssembler;

    @RequestMapping(
            value = "/api/questions/filter", method = RequestMethod.GET,
            consumes = MediaType.APPLICATION_JSON_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public @ResponseBody PagedResources<QuestionResource> filter(
            @RequestParam(value = "filter", required = false) String filter,
            Pageable p) {

        // Using queryDSL here to get a paged list of Questions
        Page<Question> page = 
            questionRepository.findAll(
                QuestionPredicate.findWithFilter(filter), p);

        // Option 1 - default resource assembler
        return pagedResourcesAssembler.toResource(page);

        // Option 2 - custom resource assembler
        return pagedResourcesAssembler.toResource(page, questionResourceAssembler);
    }

}

オプション1:提供されたSimplePagedResourceAssemblerに依存します

このオプションの問題は、必要な_linksがレンダリングされないことです。これに対する修正があれば、それが最も簡単な解決策になります。

オプション2:オープンリソースアセンブラを実装します

このオプションの問題は、 Spring-Hateoasドキュメント に従ってQuestionResourceAssemblerを実装すると、QuestionResourceQuestionのほぼ重複するパスになり、アセンブラが手動でデータをコピーする必要があることです。 2つのオブジェクト、および関連するすべての_linksを手動で作成する必要があります。これは多くの無駄な努力のようです。

何をしますか?

SpringがQuestionRepositoryをエクスポートするときに、これらすべてを実行するコードをすでに生成していることを私は知っています。そのコードを利用して使用し、コントローラーからの出力がシームレスで、生成された応答と交換可能であることを確認する方法はありますか?

25
JBCP

Spring DataRestの動作を完全に模倣する方法を見つけました。秘訣は、PagedResourcesAssemblerと引数が挿入されたPersistentEntityResourceAssemblerのインスタンスの組み合わせを使用することにあります。コントローラを次のように定義するだけです...

@RepositoryRestController
@RequestMapping("...")
public class ThingController {

    @Autowired
    private PagedResourcesAssembler pagedResourcesAssembler;

    @SuppressWarnings("unchecked") // optional - ignores warning on return statement below...
    @RequestMapping(value = "...", method = RequestMethod.GET)
    @ResponseBody
    public PagedResources<PersistentEntityResource> customMethod(
            ...,
            Pageable pageable,
            // this gets automatically injected by Spring...
            PersistentEntityResourceAssembler resourceAssembler) {

        Page<MyEntity> page = ...;
        ...
        return pagedResourcesAssembler.toResource(page, resourceAssembler);
    }
}

これは、SpringがPersistentEntityResourceAssemblerArgumentResolverを挿入するために使用するPersistentEntityResourceAssemblerの存在のおかげで機能します。結果は、リポジトリクエリメソッドの1つに期待するものとまったく同じです。

25
Ryan

この古い質問に対する更新された回答:PersistentEntityResourceAssemblerでそれを行うことができるようになりました

@RepositoryRestControllerの内部:

@RequestMapping(value = "somePath", method = POST)
public @ResponseBody PersistentEntityResource postEntity(@RequestBody Resource<EntityModel> newEntityResource, PersistentEntityResourceAssembler resourceAssembler)
{
  EntityModel newEntity = newEntityResource.getContent();
  // ... do something additional with new Entity if you want here ...  
  EntityModel savedEntity = entityRepo.save(newEntity);

  return resourceAssembler.toResource(savedEntity);  // this will create the complete HATEOAS response
}
9
Robert

この問題はかなり簡単な方法で解決できたと思いますが、より適切に文書化されている可能性があります。

SimplePagedResourceAssemblerの実装を読んだ後、ハイブリッドソリューションが機能する可能性があることに気付きました。提供されたResource<?>クラスはエンティティを正しくレンダリングしますが、リンクは含まれていないため、追加するだけで済みます。

私のQuestionResourceAssembler実装は次のようになります。

@Component
public class QuestionResourceAssembler implements ResourceAssembler<Question, Resource<Question>> {

    @Autowired EntityLinks entityLinks;

    @Override
    public Resource<Question> toResource(Question question) {
        Resource<Question> resource = new Resource<Question>(question);

        final LinkBuilder lb = 
            entityLinks.linkForSingleResource(Question.class, question.getId());

        resource.add(lb.withSelfRel());
        resource.add(lb.slash("answers").withRel("answers"));
        // other links

        return resource;
    }
}

それが終わったら、私のコントローラーでオプション2上記を使用しました:

    return pagedResourcesAssembler.toResource(page, questionResourceAssembler);

これはうまく機能し、コードはそれほど多くありません。唯一の面倒は、必要な参照ごとに手動でリンクを追加する必要があることです。

4
JBCP