Sytuacja kobiet w IT w 2024 roku
10.10.20199 min
 Iwona Jóźwiak
Divante

Iwona Jóźwiak Senior Magento DeveloperDivante

Magento 2 - praca z API

Zobacz, jak uzyskać dostęp do API Magento 2 i porównaj metody komunikacji SOAP, REST oraz GraphQL.

Magento 2 - praca z API

Każdy współczesny silnik, w tym Magento, pozwala na komunikację za pomocą API, dzięki czemu możemy integrować go z zewnętrznymi webservice’ami. Do dyspozycji do tej pory mieliśmy REST API (Representational State Transfer) i SOAP API (Simple Object Access Protocol). Nowością w wersji Magento 2.3 jest GraphQL. Zaznaczyć tu należy, że zasięg API dla REST, SOAP jest taki sam. Oznacza to, że każda funkcjonalność dostępna z poziomu REST API, może zostać również odpytana metodą SOAP. O wyborze metody decyduje developer. Dla Vue Storefront’a zdecydowanie bardziej przyda się REST API, natomiast w przypadku skryptów korzystających z SoapClient’a, naturalnym będzie wybór metody SOAP.

W poniższym artykule omówię:

Autoryzacja i uprawnienia

Dostęp do API posiada trzy sposoby uwierzytelniania:

  1. oAuth 1.0a- dla zewnętrznych aplikacji
  2. tokeny- dla aplikacji mobilnych
  3. login/ hasło - dla administratorów i klientów.


Zarówno endpointy, jak i konta, mają zdefiniowane zasoby, do których mogą uzyskać dostęp. Ponadto możemy wyróżnić 3 rodzaje uprawnień dla endpointu:

  1. anonymous- bez autoryzacji.
  2. self- dla żądań wysyłanych przez ajax z domeny sklepu po stronie klienta. Wymogiem jest by użytkownik był zalogowany. 
  3. namespace_Module::Model- Z poziomu modułu możemy dodać zasób, do którego następnie można przydzielić dostęp. Jeżeli zalogowany użytkownik ma dostęp do tego zasobu, będzie mógł również wysłać żądanie.

oAuth

Autoryzacja za pomocą oAuth wykorzystuje protokół oAuth 1.0a. Opiera się on o mechanizmie przekazywania tokenów. To pozwala Magento na kontrolę dostępu do udostępnionych zasobów przez zewnętrzne aplikacje. Rozszerzenia tego typu nazywamy “Integracjami”. W trakcie rejestracji nowej “Integracji” (System → Rozszerzenia → Integracje), tworzone są tokeny, których będziemy używać w celu uwierzytelnienia.

Najpierw tworzony jest token żądania, który jest krótkotrwały i musi zostać wymieniony na token dostępu (Access token). Tokeny dostępu są trwałe i nie wygasają, chyba że Admin dezaktywuje “Integrację”. Operacje te dzieją się w tle, w momencie, kiedy admin klika w przycisk aktywacji integracji. Po zakończeniu przetwarzania żądania otrzymamy zbiór tokenów:

Będą nam one potrzebne, aby uzyskać dostęp do zasobów zwracanych przez endpointy. 

Integracja z zewnętrzną aplikacją

W przypadku integracji “third-party”, należy poinformować zewnętrzną aplikację, jakie dane (consumer key, consumer secret i verifier code) pozwolą jej pobrać tokeny. W tym celu musimy stworzyć stronę logowania i podać jej adres w polu “Identity link URL” oraz endpoint - Callback URL, na który zostanie wysłany m.in. “verifier code”. Kod ten wysyłany jest tylko raz, podczas aktywacji integracji. 

Token żądania

Aby pobrać token żadania, należy odpytać endpoint /oauth/token/request, wykorzystując poniższe parametry:

  • oauth_consumer_key- generowany jest podczas tworzenia integracji
  • oauth_signature_method - nazwa metody podpisu, Magento wykorzystuje HMAC-SHA1
  • oauth_signature- podpis żądania
  • oauth_nonce- losowa wartość generowana przez aplikację
  • oauth_timestamp- dodatnia liczba całkowita, wyrażona w sekundach od 1 stycznia 1970 r. 00:00:00 GMT.
  • oauth_version- wersja oAuth, Magento wykorzystuje 1.0


W odpowiedzi otrzymamy:

  • oauth_token- token, który musimy podać wraz z wartościami “customer key” i “customer secret” podczas wysyłania żądania o token dostępu
  • oauth_token_secret -  sekretny klucz, który musimy podać wraz z powyższym tokenem oraz wartościami “customer key” i “customer secret” podczas wysyłania żądania o token dostępu


Token dostępu

Aby pobrać token dostępu, należy odpytać endpoint /oauth/token/access, wykorzystując poniższe parametry:

  • oauth_consumer_key- generowany jest podczas tworzenia integracji
  • oauth_signature_method- nazwa metody podpisu, Magento wykorzystuje HMAC-SHA1
  • oauth_signature- podpis żądania
  • oauth_nonce- losowa wartość generowana przez aplikację
  • oauth_timestamp- dodatnia liczba całkowita, wyrażona w sekundach od 1 stycznia 1970 r. 00:00:00 GMT.
  • oauth_version- wersja oAuth, Magento wykorzystuje 1.0
  • oauth_token- token żądania
  • oauth_token_secret- sekretny klucz tokenu żądania
  • oauth_verifier- kod weryfikacyjny powiązany z konsumentem i tokenem żądania. Jest on wysyłany na adres “Callback URL” jako część początkowej operacji POST, gdy aktywowana jest integracja.


W odpowiedzi otrzymamy:

  • oauth_token- token dostępu, który zapewnia dostęp do chronionych zasobów
  • oauth_token_secret-  sekretny klucz powiązany z tokenem dostępu


Callback URL


Aby móc poprawnie się zalogować, musimy najpierw przechwycić dane wysyłane do Magento i zapisać je do sesji. Służy do tego endpoint “Callback URL”. Poniżej jego przykładowy kod.

<?php
session_id(‘integration’);
session_start();

$_SESSION['oauth_consumer_key'] = $_POST['oauth_consumer_key'];
$_SESSION['oauth_consumer_secret'] = $_POST['oauth_consumer_secret'];
$_SESSION['store_base_url'] = $_POST['store_base_url'];
$_SESSION['oauth_verifier'] = $_POST['oauth_verifier'];

session_write_close();

header("HTTP/1.0 200 OK");


Identity link URL

Poniżej znajduje się przykładowy plik login.php, który możemy wrzucić w ramach testu do katalogu pub naszego Magento.

<?php
session_id(‘integration’);
session_start();

if (!isset($_POST['Submit'])):
   $consumerKey = $_REQUEST['oauth_consumer_key'];
   $callbackUrl = urlencode(urldecode($_REQUEST['success_call_back']));
?>
   <form name="form1" method="post" action="endpoint.php?=&callback_url=<?php echo $callbackUrl; ?>">
       <input type="submit" name="Submit" value="Aktywuj integrację" />
       <input type="hidden" name="oauth_consumer_key" value="<?php echo $consumerKey?>" />
   </form>
<?php else:

   $magentoBaseUrl = rtrim($_SESSION['store_base_url'], '/');

   define('TESTS_BASE_URL', $magentoBaseUrl);

   $credentials = new \OAuth\Common\Consumer\Credentials($_SESSION['oauth_consumer_key'], $_SESSION['oauth_consumer_secret'], $magentoBaseUrl);
   $oAuthClient = new OauthClient($credentials);
   $requestToken = $oAuthClient->requestRequestToken();
   $accessToken = $oAuthClient->requestAccessToken(
       $requestToken->getRequestToken(),
       $_SESSION['oauth_verifier'],
       $requestToken->getRequestTokenSecret()
   );

   header("location: " . $_GET['callback_url']);

endif; ?>


Uwaga!
Jeżeli Twój sklep korzysta z certyfikatu typu Self-sign, otrzymasz najprawdopodobniej błąd:

OAuth\Common\Http\Exception\TokenResponseException: cURL Error # 60: SSL certificate problem: self signed certificate in ...

Są różne sposoby na rozwiązanie tego problemu, ale jeśli testujemy integrację w ramach struktury Magento, najszybszym rozwiązaniem będzie dodanie poniższych linii w pliku, który wskazuje powyższy błąd:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);


tuż przed:

$response     = curl_exec($ch);


Jest to oczywiście rozwiązanie tylko dla środowiska developerskiego.

Na potrzeby tego artykułu nie wprowadzałam logowania w ramach “Identity Link”, jednak warto pamiętać, że tutaj należy przeprowadzić autoryzację użytkownika i przekierować go dopiero po jej prawidłowym przebiegu.

Pobranie tokenu dla klienta / administratora

Pobranie tokenu dla klienta, bądź administratora, w zasadzie sprowadza się do wysłania żądania POST do prawidłowego endpointu po stronie Magento:

  • V1/integration/admin/token - zwraca token dla administratora
  • V1/integration/customer/token - zwraca token dla klienta


W żądaniu należy podać 2 parametry:

  • username - login użytkownika
  • password - hasło użytkownika


W odpowiedzi otrzymamy bearer token, który należy przekazać wraz z żądaniami, podczas odpytywania endpointów Magento.

REST API

W codziennej pracy z REST API Magento (i nie tylko), najbardziej przydają się narzędzia Postman, Insomnia i Swagger. Niektóre endpointy, jak wspomniałam wyżej, są dostępne dla użytkowników anonimowych, a inne wymagają autoryzacji. W ramach Core dysponujemy bardzo pokaźną listą endpointów, która jest dość dobrze udokumentowana, ale nic nie stoi na przeszkodzie, by stworzyć swój własny.

Magento od wersji 2.3 wprowadza tzw. bulk endpoints, które pozwalają na grupowanie żądań tego samego typu. 

Deklaracja endpointu w webapi.xml

Deklaracji core’wych i niestandardowych endpointów należy szukać w plikach webapi.xml poszczególnych modułów. Poniżej znajduje się lista przykładowych deklaracji, uwzględniająca wcześniej omówione zasięgi dostępu:

<?xml version="1.0"?> 
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
    <!-- test api Group -->
    <route url="/V1/hello/name/:name" method="GET">
        <service class="Test\SimpleModule\Api\ApiInterface" method="name"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route url="/V1/helloself/name/:name" method="GET">
        <service class="Test\SimpleModule\Api\ApiInterface" method="name"/>
        <resources>
            <resource ref="self"/>
        </resources>
    </route>
    <route url="/V1/hellomodule/:name" method="GET">
        <service class="Test\SimpleModule\Api\ApiInterface" method="name"/>
        <resources>
            <resource ref="Test_Simplemodule:Test"/>
        </resources>
    </route>
</routes>


API Interface

Użycie interface’u nie jest obowiązkowe, ale rekomendowane. I jest to zgodne z regułami SOLID

# Test/SimpleModule/Api/ApiInterface.php
namespace Test\SimpleModule\Api;
 
interface ApiInterface
{
    /**
     * Returns greeting message to user
     *
     * @api
     * @param string $name Users name.
     * @return string Greeting message with users name.
     */
    public function name($name);
}


Zasób

Poniżej znajduje się przykładowy zasób implementujący wcześniej stworzony ApiInterface. Podczas tworzenia endpointów, należy zwrócić szczególną uwagę na docblock. Istotnym jest @return, ponieważ Magento używa refleksji do przetworzenia tego, co Magento zwraca.

Natomiast opcjonalnym jest @api. Tag ten wskazuje, że kod jest częścią publicznego interfejsu API i podlega zasadom zgodności wstecznej Magento.

# Test/SimpleModule/Model/Test.php
namespace Test\SimpleModule\Model;
class Test  implements \Test\SimpleModule\Api\ApiInterface
{
    /**
     * Returns greeting message to user
     *
     * @api
     * @param string $name Users name.
     * @return string Greeting message with users name.
     */
    public function name($name) {
        return "Hello, " . $name;
    }
}


Przykładowe żądanie

SOAP API

W przypadku skryptów wykorzystujących SoapClient, odwołujemy się do naszych zasobów API w nieco inny sposób. Na początek dobrze jest się dowiedzieć, pod jakim adresem jest nasz wsdl. W tym celu odpytujemy adres:

http://<host>/soap/all?wsdl_list=1

Następnie znajdujemy węzeł reprezentujący nasz service:

Nasz wsdl będzie więc znajdował się pod adresem:

http://<host>/soap/all?wsdl&services=testSimpleModuleApiV1

Uruchamiamy więc klienta:

$client = new 
SoapClient('http://<host>/soap/default?wsdl&services=testSimpleModuleApiV1')


Za pomocą metody __getFunctions() możemy zobaczyć jakie mamy wystawione metody:

var_dump($client->__getFunctions())


w odpowiedzi otrzymamy:

array(1) {
    [0]=> string(112) "TestSimpleModuleApiV1NameResponse testSimpleModuleApiV1Name(TestSimpleModuleApiV1NameRequest $messageParameters)"
}


W przypadku potrzeby autoryzacji za pomocą tokenu (jak w powyższym przykładzie) musimy go wysłać podobnie jak w przypadku REST API:

$opts = ['http' => ['header' => "Authorization: Bearer <token>"]];
$context = stream_context_create($opts);
 
$client = new SoapClient('http://<host>/soap/default?wsdl&services=testSimpleModuleApiV1',
                            [
                                'version' => SOAP_1_2,
                                'stream_context' => $context
                            ]
                        );


Teraz już możemy odpytać naszą metodę za pomocą linijki:

$result = $client->testSimpleModuleApiV1Name([
    "name" => "Divante"
]);
var_dump($result);

w odpowiedzi otrzymamy:

object(stdClass)#2 (1) {
    ["result"] => string(14) "Hello, Divante"
}

GraphQL

W Facebook w 2012 r. Facebook opracował GraphQL jako alternatywny język zapytań dla REST i SOAP. Publicznie wydano go dopiero  w 2015 r. Magento implementuje GraphQL od wersji 2.3 również jako alternatywę, która świetnie sprawdza się podczas programowania frontendu.

Świetną prezentację w ramach Meet Magento 2018 przedstawił Alex Paliarush. Skupił się w niej na tym, dlaczego warto przejść na GraphQL, jak i jakie to daje nam korzyści.

Co daje GraphQL użytkownikom?

Przede wszystkim zwiększa wydajność, dzięki temu, że to klient decyduje, co pobrać.

  • brak nadmiernego i niepełnego pobierania
  • minimalna liczba połączeń
  • możliwość konfiguracji ACL dla każdego obiektu


Twórcy Magento skupili się również na usprawnieniu pracy z API, w porównaniu do metody REST. Skupili się przede wszystkim na:

  • szybkości generowania zapytań
    - usunięto bezsensowne buforowanie
    - wprowadzono automatyczne grupowanie zapytań
  • dokumentacji
  • narzędziach, które uwzględnią wszystkie doświadczenia twórców


Podstawowym problemem tego rozwiązania jest jednak obsługa błędów. Każdy request w odpowiedzi otrzymuje kod 200 w nagłówku, analizować więc trzeba każdą odpowiedź.

Co daje GraphQL developerom?

  • Najważniejszą zaletą jest ochrona przeciwko atakom DoS, za pomocą zastosowania białej listy oraz dzięki złożoności i zagnieżdżaniu zapytań.
  • Poprawiono bezpieczeństwo brakiem automatycznych zasięgów, jak w przypadku REST i SOAP. GraphQL wymaga ACL dla każdego endpointu i usługi.
  • Dzięki ustandaryzowanej formie zapytań, kod wydawany przez zespoły, staje się bardziej ujednolicony.
  • Istnieje możliwość zwracania różnych formatów treści dla tych samych encji
  • Wszystkie zapytania są żądaniami POST na adres http://<magento2-3-server>/graphql, w przeciwieństwie do REST i SOAP


Przykładowy request

Zapytanie zawiera następujące elementy:

  • Słowo kluczowe. Jeśli nie zostanie podane na początku żądania, uznane zostanie za zapytanie.
  • Nazwa operacji lokalnej implementacji. Ta nazwa jest wymagana tylko w przypadku stosowania zmiennych, w przeciwnym razie jest to opcjonalna.
  • Nazwa zapytania
  • Warunki wyszukiwania. Mogą mieć postać obiektów, atrybutów lub kombinacji. Zapytania, które nie wymagają wyszukiwanych haseł, uzyskują kontekst z tokena autoryzacji klienta lub identyfikatora sklepu - oba podane w nagłówku połączenia.
  • Obiekt wyjściowy. Określa, które dane będzie zwracało zapytanie.


Poniżej znajduje się przykładowe żądanie:

query myCartQuery{
  cart(cart_id: "1WxKm8WUm3uFKXLlHXezew5WREfVRPAn") {
    items {
      id
      quantity
    }
    billing_address {
      firstname
      lastname
      postcode
      }
    shipping_addresses {
      firstname
      lastname
      postcode
    }
  }
}


oraz odpowiedź:

{
  "data": {
    "cart": {
      "items": [
        {
          "id": "5",
          "quantity": 1
        }
      ],
      "billing_address": {
        "firstname": "Veronica",
        "lastname": "Costello",
        "postcode": "49628-7978"
      },
      "shipping_addresses": [
        {
          "firstname": "Veronica",
          "lastname": "Costello",
          "postcode": "49628-7978"
        }
      ]
    }
  }
}


To i inne przykładowe zastosowanie można znaleźć na stronie dokumentacji.

Podsumowanie

Mam nadzieję, że ten artykuł rozwiewa pierwsze wątpliwości, które pojawią się podczas pracy z Magento API. Zapewne zaraz po nich pojawią się kolejne, ale ważne by się nie zrażać :) 

Pamiętać należy, żeby dla każdego nowego endpointu poprawnie skonfigurować uprawnienia i upewnić się, że nie można dostać się w sposób nieautoryzowany do zwracanych zasobów. Wydaje się to oczywiste, jednak w samym Magento zdarzały się tego typu luki bezpieczeństwa, które doprowadziły m.in. do wycieku danych klientów. Bądźmy zatem ostrożni podczas udostępniania czegokolwiek. 

W internecie znajdziemy wiele ciekawych tutoriali na temat API i pisania testów, które warto stosować nie tylko dla weryfikacji działania endpointów, ale i po to, by zwiększyć ich bezpieczeństwo.

<p>Loading...</p>