Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Spring Boot中为自定义控制器方法启用HAL序列化_Spring_Spring Mvc_Spring Boot_Spring Data Rest_Spring Hateoas - Fatal编程技术网

在Spring Boot中为自定义控制器方法启用HAL序列化

在Spring Boot中为自定义控制器方法启用HAL序列化,spring,spring-mvc,spring-boot,spring-data-rest,spring-hateoas,Spring,Spring Mvc,Spring Boot,Spring Data Rest,Spring Hateoas,我正在尝试使用SpringBootStarter数据rest构建一个带有SpringBoot的RESTfulAPI。有一些实体:账户、交易、类别和用户——只是普通的东西 当我通过默认生成的API检索处的对象时,一切进展顺利,我得到一个列表,其中所有事务都是JSON对象,如下所示: { "amount": -4.81, "date": "2014-06-17T21:18:00.000+0000", "description": "Pizza", "_links": { "s

我正在尝试使用SpringBootStarter数据rest构建一个带有SpringBoot的RESTfulAPI。有一些实体:账户、交易、类别和用户——只是普通的东西

当我通过默认生成的API检索处的对象时,一切进展顺利,我得到一个列表,其中所有事务都是JSON对象,如下所示:

{
  "amount": -4.81,
  "date": "2014-06-17T21:18:00.000+0000",
  "description": "Pizza",
  "_links": {
    "self": {
      "href": "http://localhost:8080/transactions/5"
    },
    "category": {
      "href": "http://localhost:8080/transactions/5/category"
    },
    "account": {
      "href": "http://localhost:8080/transactions/5/account"
    }
  }
}
但现在的目标是只检索该URL下的最新事务,因为我不想序列化整个数据库表。所以我写了一个控制器:

@Controller
public class TransactionController {
    private final TransactionRepository transactionRepository;

    @Autowired
    public TransactionController(TransactionRepository transactionRepository) {
        this.transactionRepository = transactionRepository;
    }

    // return the 5 latest transactions
    @RequestMapping(value = "/transactions", method = RequestMethod.GET)
    public @ResponseBody List<Transaction> getLastTransactions() {
        return  transactionRepository.findAll(new PageRequest(0, 5, new Sort(new Sort.Order(Sort.Direction.DESC, "date")))).getContent();
    }
}
因为用户和帐户之间的循环引用。当我通过向User中的帐户列表添加@JsonBackReference注释来解决此问题时,我可以检索事务列表,但只能使用此“经典”格式:

不再有HAL链接,所有内容都由jackson直接序列化。我试着加上

@EnableHypermediaSupport(type = HypermediaType.HAL)
到实体类,但这并没有让我得到任何东西。我只希望我的控制器返回与生成的API相同的对象,使用HAL\u链接,而不是序列化每个引用。有什么想法吗

编辑: 好的,在仔细考虑之后,我意识到@EnableHypermediaSupport注释当然必须添加到配置中。这解决了循环引用的问题,我可以从用户中删除@JsonBackReference。但是只有对象本身的属性被序列化,没有_links部分:

{
    "amount": -4.81,
    "date": "2014-06-17T21:18:00.000+0000",
    "description": "Pizza"
}

我知道我可以编写包装器类来扩展我所有实体的ResourceSupport,但这似乎毫无意义。由于spring hateoas能够通过自动创建的REST接口的_link部分神奇地生成表示,因此应该有一种方法可以从自定义控制器返回相同的表示,对吗?

您不需要创建自己的控制器来限制查询结果或对结果进行排序。只需在存储库中创建一个:

public interface TransactionRepository extends MongoRepository<Transaction, String> {

    List<Transaction> findFirst10ByOrderByDateDesc();

}
public interface TransactionRepository扩展了MongoRepository{
列出findFirst10ByOrderByDateDesc();
}

Spring Data REST将自动将其导出为at
/transactions/search/findFirst10ByOrderByDateDesc

这里有很多方面:

  • 我怀疑
    /transactions
    上的收集资源是否真的返回了您描述的单个事务。将为项目资源返回这些表示

  • 如果
    TransactionRepository
    已经是一个
    PageableAndSortingRepository
    ,则可以通过扩展API根目录中为名为
    transactions
    的链接公开的URI模板来调整集合资源。默认情况下,这是一个
    页面
    大小
    排序
    参数。这意味着客户端可以请求您想要公开的内容

  • 如果希望默认分页和排序选项,则实现控制器是正确的方法。但是,要实现SpringDataRESTExposes这样的表示,您需要至少返回的实例,因为这是注册HAL映射的类型

    如果你仔细想想,这里没有什么神奇的东西。普通实体没有任何链接,
    ResourcesSupport
    Resource
    等类型允许您根据需要包装实体并使用链接对其进行丰富。SpringDataREST基本上是通过使用大量关于域和存储库结构的知识来实现这一点的,这些知识是隐式可用的。如下图所示,您可以重复使用很多

    您需要注意以下几点:

    • PersistentEntityResourcesAssembler
      -通常注入控制器方法。它以Spring数据REST方式呈现单个实体,这意味着指向托管类型的关联将呈现为链接等
    • PagedResourcesAssembler
      -通常注入控制器实例。负责准备页面中包含的项目,可以选择使用专用的
      ResourceAssembler
    Spring Data REST对页面的基本功能如下:

    PersistentEntityResourceAssembler entityAssembler = …;
    Resources<?> … = pagedResourcesAssembler.toResources(page, entityAssembler);
    
    PersistentEntityResourcesAssembler entityAssembler=…;
    Resources…=pagedResourcesAssembler.toResources(第页,entityAssembler);
    
    这基本上是使用
    PagedResourcesAssembler
    persistenentEntityResourcesAssembler
    来呈现项目

    返回
    Resources
    实例将为您提供所需的表示设计


  • 要在控制器中使用PersistentEntityResourcesAssembler,我们应该将其标记为@RepositoryRestController

    @RestController
    @RequestMapping("/categories")
    @RepositoryRestController
    public class CategoryController implements ValidableController {
    
    // dependencies
    
    @RequestMapping(method = POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<PersistentEntityResource> create(@Valid @RequestBody CategoryForm category,
                                                           BindingResult validation,
                                                           PersistentEntityResourceAssembler resourceAssembler)
    {
        validate(validation);
        Category entity = categoryConverter.convert(category);
        entity = categoryService.save(entity);
        return ResponseEntity.ok(resourceAssembler.toFullResource(entity));
    }
    

    }

    谢谢,这正是我想要的!(方法名称必须是findFirst10ByOrderByDateDesc(),这使方法资源在可用)这非常有用,谢谢!
    ResourcesSupport
    是否有文档记录?是的:@OliverGierke非常感谢!关于您的评论:(1)是的,资源当然会返回一个列表。我只是缩短了输出以说明格式。(2.)是的,我有一个分页和排序存储库,但正如您所猜测的,我想(3.)为客户提供并消除一次查询整个数据库表的可能性。@OliverGierke再次感谢!可能是我,但我从未考虑过寻找SpringHateOAS文档。我刚才发现的SpringDataREST文档中有一个脚注。可能会添加一小段“如果您想创建自己的与HATEAOS集成的控制器,请返回
    ResourcesSupport
    ,有关详细信息,请参阅Spring HATEAOS文档”?@Tilman您还可以通过使用
    @RestResource(exported=false)对“findAll()”方法进行注释来禁用该方法的导出
    并且仍然使用查询方法。
    PersistentEntityResourceAssembler entityAssembler = …;
    Resources<?> … = pagedResourcesAssembler.toResources(page, entityAssembler);
    
    @RestController
    @RequestMapping("/categories")
    @RepositoryRestController
    public class CategoryController implements ValidableController {
    
    // dependencies
    
    @RequestMapping(method = POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<PersistentEntityResource> create(@Valid @RequestBody CategoryForm category,
                                                           BindingResult validation,
                                                           PersistentEntityResourceAssembler resourceAssembler)
    {
        validate(validation);
        Category entity = categoryConverter.convert(category);
        entity = categoryService.save(entity);
        return ResponseEntity.ok(resourceAssembler.toFullResource(entity));
    }
    
    {
    "createdTime": "2018-07-24T00:55:32.854",
    "updatedTime": "2018-07-24T00:55:32.855",
    "name": "cfvfcdfgdfdfdfs32",
    "options": [
        "aaa",
        "bbb"
    ],
    "_links": {
        "self": {
            "href": "http://localhost:8080/shop/categories/34"
        },
        "category": {
            "href": "http://localhost:8080/shop/categories/34{?projection}",
            "templated": true
        },
        "products": {
            "href": "http://localhost:8080/shop/categories/34/products"
        },
        "categories": {
            "href": "http://localhost:8080/shop/categories/34/categories{?projection}",
            "templated": true
        },
        "parent": {
            "href": "http://localhost:8080/shop/categories/34/parent{?projection}",
            "templated": true
        }
    }