4

我有一个带有ui 路由器的AngularJS应用程序,它使用带有HypermediaREST 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 比上面提供的示例要复杂得多。

4

1 回答 1

1

我以前遇到过几次这个问题。我在下面逐步详细说明了我的首选解决方案。最后两个步骤专门针对您在帖子中概述的问题,但可以在整个应用程序中应用相同的原则。

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. 产品概览状态作为主状态的子状态

然后创建一个产品状态,它是主状态的后代。因为根端点已经解析,我们可以在这种状态下使用它来获取产品 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(因此,它是客户端 URI 的一部分)传入产品的 id,并使用它从父级解析的产品数组中检索正确的产品:

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

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

希望这可以帮助!

于 2017-02-07T09:38:56.260 回答