Как использовать Spring REST Projection с пользовательским контроллером - PullRequest
0 голосов
/ 04 сентября 2018

У меня есть следующая Проекция, которая возвращает имя тега:

@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);
    }
}

1 Ответ

0 голосов
/ 04 сентября 2018

Если вы хотите добавить все поля 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 для отправки клиенту )

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