2016-08-17 20 views
3

У меня есть AngularJS приложение с Ui маршрутизатор, который потребляет REST API с гипермедиа. Общая идея заключается в том, чтобы API генерировал URL-адреса для своих различных вызовов и не позволял клиенту создавать сами URL-адреса.AngularJS с гипермедиа (HATEOAS): как использовать HyperMedia URLS accross состояния

Например, при получении списка продуктов, вот то, что 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": [] 
    } 
    } 
] 

Итак, проблема: я хочу, чтобы перейдите к деталям продукта. У меня есть URL-адрес API, доступный из коллекции hypermedia. Однако при изменении состояний мне как-то нужно передать этот url в состояние детали, так что контроллер состояния детали может извлечь продукт.

URL-адреса пользовательского интерфейса полностью отделены от URL-адресов API, то есть клиент имеет свою собственную схему URL-адресов.

Каков наилучший способ достичь этого, сохраняя при этом клиента без гражданства и каждую страницу закладок?

Одним из решений является передать URL-адрес ui router's data. Однако страница не будет заклассифицирована. Другой способ - передать URL-адрес API в UI-url, который сохранит страницу закладок (пока URL-адрес API не изменится), но URL-адрес UI будет очень уродливым.

Любые другие мысли?

Если я не ошибаюсь, я не ищу шаблонное решение, то есть решение, в котором API возвращает шаблон URL-адреса, который должен быть проинформирован клиентом. Все дело в том, что url уже заполнен данными, поскольку некоторые URL-адреса довольно сложны, чем пример, приведенный выше.

ответ

1

Я столкнулся с этой проблемой несколько раз раньше. Ниже я подробно изложил свое предпочтительное решение. Последние два шага специально предназначены для вашей проблемы, изложенные в вашем сообщении, но тот же принцип может применяться во всех ваших приложениях.

1. Корневая конечная точка

Начните с определения корневой конечной точки на уровне API. Соответствующий корневой объект представляет собой набор ссылок верхнего уровня, другими словами, ссылки, к которым клиент (ы) требуют прямого доступа.

Идея состоит в том, что клиенту нужно знать только об одной конечной точке, а именно оконечной точке корня. Это имеет то преимущество, что вы не копируете логику маршрутизации клиенту и, что управление версиями API становится намного проще.

Основываясь на вашем примере, этот корень конечная точка может выглядеть следующим образом:

{ 
    "_links": { 
     "products": { 
     "href": "http://localhost:4444/api/products", 
     } 
    } 
} 

2. Аннотация основного состояние

Следующих определить абстрактное супер состояние, которое находится в верхней части государственной иерархии.Обычно я называю это состояние основным, чтобы избежать путаницы с корнем оконечная точка. Задача этого супер состояния разрешить корневую конечную точку, как:

$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) и использовать его, чтобы получить правильный продукт из массива продуктов разрешенного в родительском:

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

Вы можете переместить код, указанной выше в чтобы сделать ваши штаты более легкими.

Надеюсь, это поможет!