• 将Spring实战第5版中Spring HATEOAS部分代码迁移到Spring HATEOAS 1.0


           最近在阅读Spring实战第五版中文版,书中第6章关于Spring HATEOAS部分代码使用的是Spring HATEOAS 0.25的版本,而最新的Spring HATEOAS 1.0对旧版的API做了升级,导致在使用新版Spring Boot(截至文章发布日最新的Spring Boot版本为2.2.4)加载的Spring HATEOAS 1.0.3无法正常运行书中代码,所以我决定在此对书中的代码进行迁移升级。

    在线阅读书中这一部分:https://potoyang.gitbook.io/spring-in-action-v5/di-6-zhang-chuang-jian-rest-fu-wu/6.2-qi-yong-chao-mei-ti

    Spring HATEOAS 1.0 版本的变化

    封装结构的最大变化是通过引入超媒体类型注册API来实现的,以支持Spring HATEOAS中的其他媒体类型。这导致客户端API和服务器API(分别命名的包)以及包中的媒体类型实现的明确分离 mediatype

    最大的变化就是将原来的资源表示为模型,具体变化如下。

    ResourceSupportResourceResourcesPagedResources组类从来没有真正感受到适当命名。毕竟,这些类型实际上并不表示资源,而是表示模型,可以通过超媒体信息和提供的内容来丰富它们。这是新名称映射到旧名称的方式:

    • ResourceSupport 就是现在 RepresentationModel

    • Resource 就是现在 EntityModel

    • Resources 就是现在 CollectionModel

    • PagedResources 就是现在 PagedModel

    因此,ResourceAssembler已被重命名为RepresentationModelAssembler和及其方法toResource(…)分别toResources(…)被重命名为toModel(…)toCollectionModel(…)名称更改也反映在中包含的类中TypeReferences

    • RepresentationModel.getLinks()现在公开了一个Links实例(通过List<Link>),该实例公开了其他API,以Links使用各种策略来连接和合并不同的实例。同样,它已经变成了自绑定的泛型类型,以允许向实例添加链接的方法返回实例本身。

    • LinkDiscovererAPI已移动到client包。

    • LinkBuilderEntityLinksAPI已经被移到了server包。

    • ControllerLinkBuilder已移入server.mvc,不推荐使用替换WebMvcLinkBuilder

    • RelProvider已重命名为LinkRelationProvider并返回LinkRelation实例,而不是String

    • VndError已移至mediatype.vnderror套件。

    另外注意 ResourceProcessor 接口被 RepresentationModelProcessor 取代

    更多变化请参考Spring HATEOAS文档:https://spring.io/projects/spring-hateoas

    代码迁移升级

    书中程序清单6.4 为资源添加超链接

        @GetMapping("/recent")
        public CollectionModel<EntityModel<Taco>> recentTacos() {
            PageRequest page = PageRequest.of(
                    0, 12, Sort.by("createdAt").descending());
    
            List<Taco> tacos = tacoRepo.findAll(page).getContent();
            CollectionModel<EntityModel<Taco>> recentResources = CollectionModel.wrap(tacos);
    
            recentResources.add(
                    new Link("http://localhost:8080/design/recent", "recents"));
            return recentResources;
        }

    消除URL硬编码

        @GetMapping("/recent")
        public CollectionModel<EntityModel<Taco>> recentTacos() {
            PageRequest page = PageRequest.of(
                    0, 12, Sort.by("createdAt").descending());
    
            List<Taco> tacos = tacoRepo.findAll(page).getContent();
            CollectionModel<EntityModel<Taco>> recentResources = CollectionModel.wrap(tacos);
    
            recentResources.add(
                    linkTo(methodOn(DesignTacoController.class).recentTacos()).withRel("recents"));
            return recentResources;
        }
    程序清单6.5 能够携带领域数据和超链接列表taco资源
    public class TacoResource extends RepresentationModel<TacoResource> {
    
        @Getter
        private String name;
    
        @Getter
        private Date createdAt;
    
        @Getter
        private List<Ingredient> ingredients;
    
        public TacoResource(Taco taco) {
            this.name = taco.getName();
            this.createdAt = taco.getCreatedAt();
            this.ingredients = taco.getIngredients();
        }
    }
    程序清单6.6 装配taco资源的资源装配器
    public class TacoResourceAssembler extends RepresentationModelAssemblerSupport<Taco, TacoResource> {
        /**
         * Creates a new {@link RepresentationModelAssemblerSupport} using the given controller class and resource type.
         *
         * @param controllerClass DesignTacoController {@literal DesignTacoController}.
         * @param resourceType    TacoResource {@literal TacoResource}.
         */
        public TacoResourceAssembler(Class<?> controllerClass, Class<TacoResource> resourceType) {
            super(controllerClass, resourceType);
        }
    
        @Override
        protected TacoResource instantiateModel(Taco taco) {
            return new TacoResource(taco);
        }
    
    
        @Override
        public TacoResource toModel(Taco entity) {
            return createModelWithId(entity.getId(), entity);
        }
    }

    之后对recentTacos()的调整

    @GetMapping("/recentNew")
        public CollectionModel<TacoResource> recentTacos() {
            PageRequest page = PageRequest.of(
                    0, 12, Sort.by("createdAt").descending());
            List<Taco> tacos = tacoRepo.findAll(page).getContent();
    
            CollectionModel<TacoResource> tacoResources =
                    new TacoResourceAssembler(DesignTacoController.class, TacoResource.class).toCollectionModel(tacos);
    
            tacoResources.add(linkTo(methodOn(DesignTacoController.class)
                    .recentTacos())
                            .withRel("recents"));
            return tacoResources;
        }

    创建 IngredientResource 对象

    @Data
    public class IngredientResource extends RepresentationModel<IngredientResource> {
        public IngredientResource(Ingredient ingredient) {
            this.name = ingredient.getName();
            this.type = ingredient.getType();
        }
    
        private final String name;
        private final Ingredient.Type type;
    }
    IngredientResourceAssembler 对象
    public class IngredientResourceAssembler extends RepresentationModelAssemblerSupport<Ingredient, IngredientResource> {
        /**
         * Creates a new {@link RepresentationModelAssemblerSupport} using the given controller class and resource type.
         *
         * @param controllerClass IngredientController {@literal IngredientController}.
         * @param resourceType    IngredientResource {@literal IngredientResource}.
         */
        public IngredientResourceAssembler(Class<?> controllerClass, Class<IngredientResource> resourceType) {
            super(controllerClass, resourceType);
        }
    
        @Override
        protected IngredientResource instantiateModel(Ingredient entity) {
            return new IngredientResource(entity);
        }
    
        @Override
        public IngredientResource toModel(Ingredient entity) {
            return createModelWithId(entity.getId(), entity);
        }
    }

    对 TacoResource 对象的修改

    public class TacoResource extends RepresentationModel<TacoResource> {
        private static final IngredientResourceAssembler
                ingredientAssembler = new IngredientResourceAssembler(IngredientController.class, IngredientResource.class);
    
        @Getter
        private String name;
    
        @Getter
        private Date createdAt;
    
        @Getter
        private CollectionModel<IngredientResource> ingredients;
    
        public TacoResource(Taco taco) {
            this.name = taco.getName();
            this.createdAt = taco.getCreatedAt();
            this.ingredients = ingredientAssembler.toCollectionModel(taco.getIngredients());
    
        }
    }

     程序清单6.7

    @RepositoryRestController
    public class RecentTacosController {
        private TacoRepository tacoRepo;
    
        public RecentTacosController(TacoRepository tacoRepo) {
            this.tacoRepo = tacoRepo;
        }
    
        /**
         * 虽然@GetMapping映射到了“/tacos/recent”路径,但是类级别的@Repository RestController注解会确保这个路径添加
         * Spring Data REST的基础路径作为前缀。按照我们的配置,recentTacos()方法将会处理针对“/api/tacos/recent”的GET请求。
         * */
        @GetMapping(path="/tacos/recent", produces="application/hal+json")
        public ResponseEntity<CollectionModel<TacoResource>> recentTacos() {
            PageRequest page = PageRequest.of(
                    0, 12, Sort.by("createdAt").descending());
            List<Taco> tacos = tacoRepo.findAll(page).getContent();
    
            CollectionModel<TacoResource> tacoResources =
                    new TacoResourceAssembler(DesignTacoController.class, TacoResource.class).toCollectionModel(tacos);
    
            tacoResources.add(
                    linkTo(methodOn(RecentTacosController.class).recentTacos())
                            .withRel("recents"));
            return new ResponseEntity<>(tacoResources, HttpStatus.OK);
        }
    
    }
    程序清单6.8 为Spring Data REST端点添加自定义的链接
        @Bean
        public RepresentationModelProcessor<PagedModel<EntityModel<Taco>>> tacoProcessor(EntityLinks links) {
    
            return new RepresentationModelProcessor<PagedModel<EntityModel<Taco>>>() {
                @Override
                public PagedModel<EntityModel<Taco>> process(PagedModel<EntityModel<Taco>> resource) {
                    resource.add(
                            links.linkFor(Taco.class)
                                    .slash("recent")
                                    .withRel("recents"));
                    return resource;
                }
            };
        }

    另一种写法

    如果你觉得写使用资源装配器有点麻烦,那么你还可以采用这种方法。

        @GetMapping("/employees")
        public ResponseEntity<CollectionModel<EntityModel<Taco>>> findAll() {
            PageRequest page = PageRequest.of(
                    0, 12, Sort.by("createdAt").descending());
            List<EntityModel<Taco>> employees = StreamSupport.stream(tacoRepo.findAll(page).spliterator(), false)
                    .map(employee -> new EntityModel<>(employee,
                            linkTo(methodOn(DesignTacoController.class).findOne(employee.getId())).withSelfRel(),
                            linkTo(methodOn(DesignTacoController.class).findAll()).withRel("employees")))
                    .collect(Collectors.toList());
    
            return ResponseEntity.ok(
                    new CollectionModel<>(employees,
                            linkTo(methodOn(DesignTacoController.class).findAll()).withSelfRel()));
        }
    @GetMapping("/employees/{id}")
        public ResponseEntity<EntityModel<Taco>> findOne(@PathVariable long id) {
    
            return tacoRepo.findById(id)
                    .map(employee -> new EntityModel<>(employee,
                            linkTo(methodOn(DesignTacoController.class).findOne(employee.getId())).withSelfRel(), //
                            linkTo(methodOn(DesignTacoController.class).findAll()).withRel("employees"))) //
                    .map(ResponseEntity::ok)
                    .orElse(ResponseEntity.notFound().build());
        }

    参考来源:https://github.com/spring-projects/spring-hateoas-examples/tree/master/simplified

    END

  • 相关阅读:
    [LeetCode] Diagonal Traverse 对角线遍历
    [LeetCode] Find Bottom Left Tree Value 寻找最左下树结点的值
    [LeetCode] Next Greater Element I 下一个较大的元素之一
    [LeetCode] Teemo Attacking 提莫攻击
    [LeetCode] 494. Target Sum 目标和
    记一次Android studio升级之后的坑
    Flutter:Slivers大家族,让滑动视图的组合变得很简单!
    flutter控件之ListView滚动布局
    flutter 生命周期
    Flutter 案例学习之:GridView
  • 原文地址:https://www.cnblogs.com/puzhiwei/p/12333471.html
Copyright © 2020-2023  润新知