带超媒体的AngularJS(HATEOAS):如何跨状态使用超媒体URL

带超媒体的AngularJS(HATEOAS):如何跨状态使用超媒体URL,angularjs,rest,hateoas,hypermedia,Angularjs,Rest,Hateoas,Hypermedia,我有一个AngularJS应用程序,它带有ui路由器,使用restapi和超媒体。一般的想法是让API为其各种调用生成URL,并防止客户端自行构建URL 例如,获取产品列表时,API会返回以下内容: [ { "id": 1, "name": "Product A", "_links": { "self": { "href": "http://localhost:4444/api/products/1", "name": nu

我有一个AngularJS应用程序,它带有ui路由器,使用restapi超媒体。一般的想法是让API为其各种调用生成URL,并防止客户端自行构建URL

例如,获取产品列表时,API会返回以下内容:

[
  {
    "id": 1,
    "name": "Product A",
    "_links": {
      "self": {
        "href": "http://localhost:4444/api/products/1",
        "name": null,
        "templated": false
      },
      "actions": []
    }
  },
  {
    "id": 2,
    "name": "Product B",
    "_links": {
      "self": {
        "href": "http://localhost:4444/api/products/2",
        "name": null,
        "templated": false
      },
      "actions": []
    }
  }
]
所以,问题是:我想导航到产品的细节。我从超媒体收藏中获得了API url。但是,在更改状态时,我需要以某种方式将此url传递给详细状态,以便详细状态的控制器可以获取产品

UI url与API url完全解耦,即客户端有自己的url方案

实现这一点的最佳方法是什么,同时保持客户端无状态和每个页面都可书签

一种解决方案是通过
ui路由器
数据
属性传递url。但是,该页面不可添加书签。另一种方法是在UI url中传递API url,这将使页面保持可书签状态(只要API url不变),但UI url将非常难看

还有其他想法吗


除非我在这一点上大错特错,否则我不会寻找模板化解决方案,即API返回url模板的解决方案,该url模板需要客户端注入参数。关键是url已经填充了数据,因为有些url比上面提供的示例要复杂得多。

我以前曾多次遇到过这个问题。我已经在下面一步一步详细介绍了我的首选解决方案。最后两个步骤专门针对您在文章中概述的问题,但相同的原则可以应用于整个应用程序

1.根端点 首先在API级别定义根端点。相应的根实体是顶级链接的集合,换句话说,客户端需要直接访问的链接

其思想是,客户端只需要知道一个端点,即根端点。这样做的优点是,您不需要将路由逻辑复制到客户端,而且API的版本控制变得容易得多

根据您的示例,此根端点可能如下所示:

{
    "_links": {
      "products": {
        "href": "http://localhost:4444/api/products",
      }
    }
}
2.抽象主状态 接下来定义位于状态层次结构顶部的抽象超级状态。我通常将此状态命名为main,以避免与根端点混淆。此超级状态的任务是解析根端点,如:

$stateProvider.state('main', {
    resolve: {
        root: function($http) {
         return $http.get("http://localhost:4444/api/").then(function(resp){
              return resp.data;
         });
        }
    }
});
3.产品概述状态作为主状态的子状态 然后创建一个products状态,它是主状态的后代。由于根端点已解析,因此我们可以在此状态下使用它来获取到产品API的链接:

 $stateProvider.state('products', {
    parent: 'main',
    url: "/products",
    resolve: {
        products: function($http, root) {
         return $http.get(root._links.products.href).then(function(resp){
              return resp.data;
         });
        }
    }
});
4.产品详细信息状态作为产品状态的子级 最后,创建一个产品细节状态作为上面产品状态的子级。通过
$stateParams
传入产品id(因此,它是客户端URI的一部分),并使用它从父级解析的产品数组中检索正确的产品:

$stateProvider.state('products.product', {
    url: "/{productId}"
    resolve: {
        product: function($http, $timeout, $state, $stateParams, $q products) {
         var productId = parseInt($stateParams.productId);
         var product;

        for (var i = 0; i < products.length; i++) {
            product = products[i];
            if (product.id === productId) {
                return $http.get(product._links.self.href).then(function(response){
                    return response.data;
                });
                }
        }

        // go back to parent state if no child was found, do so in a $timeout to prevent infinite state reload
        $timeout(function(){
            $state.go('^', $stateParams);
        });

        // reject the resolve
        return $q.reject('No product with id ' + productId);
    }
});
$stateProvider.state('products.product'{
url:“/{productId}”
决心:{
产品:函数($http、$timeout、$state、$stateParams、$q产品){
var productId=parseInt($stateParams.productId);
var乘积;
对于(变量i=0;i
您可以将上面的代码移动到服务中,以使您的状态更加轻量级

希望这有帮助