AngularJS与超媒体(HATEOAS):如何在各个状态中使用超媒体URL。

4
我有一个使用ui路由器的AngularJS应用程序,它调用一个具有超媒体功能的REST API。总的想法是使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 router的data属性传递URL。但是,该页面将无法添加书签。另一种方法是在UI URL中传递API URL,这将使页面可添加书签(只要API URL不更改),但UI URL会非常丑陋。
还有其他想法吗?
除非我非常错误,否则我不需要模板化的解决方案,即API返回一个URL模板,需要客户端注入参数的解决方案。整个重点是URL已经填充了数据,因为有些URL比上面提供的示例复杂得多。
1个回答

1
我之前遇到过这个问题几次。我已经详细说明了我的首选解决方案步骤。最后两个步骤是针对你在帖子中概述的问题,但同样的原则可以应用于你的整个应用程序。
1. 根端点
首先,在API级别上定义一个根端点。相应的根实体是顶层链接的集合,换句话说,客户端需要直接访问的链接集合。
这个想法是,客户端只需要知道一个端点,即根端点。这有以下优点:不需要将路由逻辑复制到客户端,并且API的版本控制变得更加容易。
根据你的示例,这个根端点可能如下所示:
{
    "_links": {
      "products": {
        "href": "http://localhost:4444/api/products",
      }
    }
}

2. 摘要主状态

接下来定义一个抽象的超级状态,位于状态层次结构的顶部。我通常将这个状态命名为main,以避免与root端点混淆。这个超级状态的任务是解决根端点,例如:

$stateProvider.state('main', {
    resolve: {
        root: function($http) {
         return $http.get("http://localhost:4444/api/").then(function(resp){
              return resp.data;
         });
        }
    }
});

3. 产品概述状态作为主状态的子状态

然后创建一个产品状态,它是从主状态继承而来的。因为根端点已经解析,所以我们可以在此状态中使用它来获取到产品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);
    }
});

您可以将上面的代码移入服务中,使状态更轻量化。

希望能对您有所帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接