【问题标题】:How to use Spring REST Projections with custom controller如何将 Spring REST Projections 与自定义控制器一起使用
【发布时间】:2018-09-03 22:14:01
【问题描述】:

我有以下返回标签名称的投影:

@Projection(types = NewsTag.class, name = "newsTag")
public interface NewsTagProjection {

    @Value("#{target.tag.name}")
    String getName();
}

我在以下负责 newsTag 模型的存储库中使用它:

@RepositoryRestResource(excerptProjection = NewsTagProjection.class)
public interface NewsTagRepository extends JpaRepository<NewsTag, Integer> {
}

所以当我调用具有 NewsTag 列表的新闻存储库时:

@RepositoryRestResource
public interface NewsRepository extends JpaRepository<News, Integer> {
}

一切正常,它只返回一个带有 nae 的标签列表。
但是如果我想使用具有相同端点的自定义控制器(假设我正在这样做以进行错误处理),它只会返回没有标签列表的新闻,就像投影不存在一样。

这是控制器:

@RepositoryRestController
@RequestMapping("news")
public class NewsController {

    private NewsService newsService;
    private PagedResourcesAssembler<News> pagedAssembler;
    private NewsResourceAssembler newsResourceAssembler;

    @Autowired
    public void setNewsService(NewsService newsService) {
        this.newsService = newsService;
    }

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    public void setPagedAssembler(PagedResourcesAssembler<News> pagedAssembler) {
        this.pagedAssembler = pagedAssembler;
    }

    @Autowired
    public void setNewsResourceAssembler(NewsResourceAssembler newsResourceAssembler) {
        this.newsResourceAssembler = newsResourceAssembler;
    }

    // Return all news with pagination
    @GetMapping
    public ResponseEntity<?> getAllNews(Pageable pageable) {
        Page<News> newsPage = this.newsService.getAllNews(pageable);
        return ResponseEntity.ok(this.pagedAssembler.toResource(newsPage, this.newsResourceAssembler));
    }
}

编辑 NewsResourceAssembler:

@Service
public class NewsResourceAssembler implements ResourceAssembler<News, Resource<News>> {

    private EntityLinks entityLinks;

    @Autowired
    public void setEntityLinks(EntityLinks entityLinks) {
        this.entityLinks = entityLinks;
    }

    @Override
    public Resource<News> toResource(News news) {
        Link self = entityLinks.linkFor(News.class).slash(news.getId()).withSelfRel();
        Link newsTags = entityLinks.linkFor(News.class).slash(news.getId()).slash("newsTags").withRel("newsTags");
        Link newsComments = entityLinks.linkFor(News.class).slash(news.getId()).slash("newsComments").withRel("newsComments");
        return new Resource<>(news, self, newsTags, newsComments);
    }
}

【问题讨论】:

  • 你能提供NewsResourceAssembler.class代码吗?
  • @drowny 看到我的编辑,谢谢。

标签: java spring spring-boot spring-data spring-rest


【解决方案1】:

如果要添加News的所有字段,可以操作ResponseEntity的返回对象。所以我用这个代码改变了,你可以申请你的代码吗?

NewsResourceAssembler.class 应该是 ;

@Service
public class NewsResourceAssembler implements ResourceAssembler<News, Resource<NewsResource>> {

    private EntityLinks entityLinks;

    @Autowired
    public void setEntityLinks(EntityLinks entityLinks) {
        this.entityLinks = entityLinks;
    }

    @Override
    public Resource<NewsResource> toResource(News news) {

        Link self = entityLinks.linkFor(News.class).slash(news.getId()).withSelfRel();
        Link newsTags = entityLinks.linkFor(News.class).slash(news.getId()).slash("newsTags").withRel("newsTags");
        Link newsComments = entityLinks.linkFor(News.class).slash(news.getId()).slash("newsComments").withRel("newsComments");

        final NewsResource newsResource = new NewsResource();
        //set any fields to which do you wants to send client
        //newsResource.setExampleFields();
        //newsResource.setNewsTagList(news.getNewsTag()); //example code. Change according to your models

        return new Resource(newsResource, self, newsTags, newsComments);
    }
}

现在你需要NewsResource.class,所以应该是这样的;

@JsonIgnoreProperties(ignoreUnknown = true)
public class NewsResource extends ResourceSupport {

    //private fields of which do you wants
    //dont forget list of newstag field
    //ex : private List<NewsTagFields> newsTag = new ArrayList<>();

    //Also should write getter and setter methods
}

PS:不要忘记注释行的规则。根据此更改,您的客户端应分析对客户端的传入响应。因为作为响应,应该在主对象上更改一些链接 rel。但是您的自定义参数没有改变。

编辑: 如果在你的NewsResource.class --> newsTag 的字段应该是这样的(请不要忘记在你的资源汇编类中设置所有标签!);

private List<NewsTagFields> newsTag = new ArrayList<>();

现在您需要 NewsTagFields.class 。你也可以使用这个类;

public class NewsTagFields {

    private final String id;
    private final String name;

    public NewsTagFields(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

最后将此代码添加到ResourceAssembler 以添加新的NewsTagFields 列表; (此代码提供获取所有 newsTag 并完全转换为 NewsTagFields 列表。并添加到 newsResource 以发送给客户 em>)

final List<NewsTagFields> allNewsTag =
                news.getNewsTag().stream()
                        .map(newsTag -> new NewsTagFields(newsTag.getId(), newsTag.getName()))
                        .collect(Collectors.toList());
        newsResource.setNewsTagList(allNewsTag);

【讨论】:

  • 谢谢,现在我得到了我在 NewsResourceAssembler 中提供的字段,唯一的问题是响应中的 newsTag 是空的,实际上应该返回 4 个 newsTag 对象,但对象是空的,应该打印 id 和 name 但我得到 "newsTag": [ {}, {}, {}, {}]
  • 您应该为汇编程序编写一个与 NewsTag 实体匹配的新模型。我用 EDIT 编辑了答案:你能检查一下吗?
  • 但是如果我这样做了,我不能在程序集中做newsResource.setNewsTagList(news.getNewsTag());,因为它会是类型不匹配,我也不能做newsResource.setNewsTagList(news.NewsTagFields ());,因为新闻类没有 NewsTagFields 字段。跨度>
  • 请让我澄清一下
  • 好的,谢谢。仅供参考 newsTag 是介于新闻和标签之间的中间变量。它有标签对象和新闻对象
猜你喜欢
  • 2016-05-13
  • 1970-01-01
  • 1970-01-01
  • 2016-02-07
  • 1970-01-01
  • 1970-01-01
  • 2018-05-27
  • 2014-01-18
  • 2018-04-26
相关资源
最近更新 更多