Przejdź do głównej treści

Własny endpoint REST API


Własny endpoint REST API

Ten artykuł opisuje jak zarejestrować endpointy REST API w utworzonej przez ciebie aplikacji do systemu Ready.

Przykładowa klasa endpointu

<?php

namespace ReadyApp\TestRestApi;

use ApiRequest;
use PgManager;
use Ready\App\RestApi\AbsRestEndpoint;
use Ready\App\RestApi\Custom\EndpointConfig;
use Ready\App\RestApi\Custom\UriVariable;
use Symfony\Component\HttpFoundation\Response;
use ReadyApi\Rest\RestApiEndpointInterface;

class GetOneUser extends AbsRestEndpoint implements RestApiEndpointInterface {

    /**
     * @return EndpointConfig
     * @throws \Exception
     */
    public function getConfiguration(): EndpointConfig {
        $config = new EndpointConfig('GET', '/users/{usr_id}');
        $config->setUriVariable('usr_id', UriVariable::INTEGER);
        return $config;
    }

    /**
     * @param ApiRequest $request
     * @param array      $uriVariables
     *
     * @return void
     * @throws \Exception
     */
    public function processRequest(ApiRequest $request, array $uriVariables): void {

        $db = PgManager::getInstance();
        $user = $db->select('users', 'usr_id, usrnam', 'usr_id = '.$uriVariables['usr_id'], FALSE, PGSQL_ASSOC);

        $this->prepareResponseDataArray($user[0] ?? [], Response::HTTP_OK);
    }

}

W pigułce

W nowym standardzie pisania aplikacji 2.0, każdy endpoint to oddzielna klasa, która implementuje interfejs RestApiEndpointInterface.

Implementacja interfejsu:

  • public function getConfiguration(): EndpointConfig - Dostarcza konfiguracje endpointu czyli opisuje nazwę metody http, uri oraz zmienne w uri wraz z ich walidacją. (Do zaimplementowania przez developera)

getConfiguration()

Definiujemy TYLKO nazwę metody, uri i zmienne w uri

  • public function processRequest(ApiRequest, array $uriVariables): void - Wykonuje logikę biznesową potrzebną do obsłużenia requesta. (Do zaimplementowania przez developera).

  • public function getResponse(): Response - Zwraca response po przetworzeniu requesta. (Wystarczy rozszerzyć klasę endpointu o AbsRestEndpoint, która to zwiera implementacje metody getResponse())

Rejestracja klasy za pomocą ready-cli:

./ready-cli config:register \\Namespace\\Wraz\\Z\\Nazwą\\Klasy

Czyli dla przykładu powyżej rejestracja będzie wyglądać tak:

./ready-cli config:register \\ReadyApp\\TestRestApi\\GetOneUser

Escape'owanie backslash'y

Zwróć uwagę, że podczas rejestracji znak backslash '\' jest escape'owany na '\\'

Autoload'ing klasy

Upewnij się, że podawany namespace jest w pliku composer.json w standardzie PSR-4 lub znajduje się w katalogu public_html/apps/edokumenty/scripts/my/catalog/structure z namespace'm namespace ReadyApp\my\catalog\structure;

Następnie trzeba przegenerować config.

./ready-cli config:compile

EndpointConfig szczegółowe omówienie

Klasa dzięki, której dodamy konfiguracje endpointu. Sama klasa waliduje konfiguracje, którą jej się dostarcza, więc np: jeśli zapomnisz zadeklarować wszystkich zmiennych podanych w uri to zostaniesz o tym poinformowany 😃

Utworzenie obiektu

$config = new EndpointConfig($httpMethod, $uri);

$httpMethod - nazwę metody http np: 'GET'

Dostępne metody to:

  • 'GET',
  • 'POST',
  • 'PATCH',
  • 'PUT',
  • 'DELETE',
  • 'OPTIONS'

$uri - uri endpointu np: '/catTypes/{cat_tp}/cats/{cat_id}'

Zmienne podajemy otoczone wyłącznie nawiasami klamrowymi np: {cat_id}

Powyższy przykład ($uri) wygeneruje nam URL, do którego możemy odwołać się np:
https://my/ready/domain/api.php/REST/custom/catTypes/1/cats/1

Ustawianie zmiennych z Uri

Każdą zmienną w uri trzeba zadeklarować przy pomocy metody setUriVariable($varName, $pattern)
Dla przykładu poniżej MUSIMY zadeklarować zmienne {cat_tp} oraz {cat_id}

    /**
     * @return EndpointConfig
     * @throws \Exception
     */
    public function getConfiguration(): EndpointConfig {
        $config = new EndpointConfig('GET', '/catTypes/{cat_tp}/cats/{cat_id}');
        $config->setUriVariable('cat_tp', UriVariable::STRING);
        $config->setUriVariable('cat_id', UriVariable::INTEGER);
    }

$varName - nazwa zmiennej z uri np: 'cat_id'

Nazwa zmiennej musi się pokrywać z nazwą zmiennej podanej w uri.

$pattern - regex używany do walidacji zmiennej. np: '[A-Z]+'

Możliwe jest użycie predefiniowanych typów, które są przyjaznym aliasem na regex, który
reprezentują.

TypRegex
UriVariable::INTEGER[0-9]+
UriVariable::STRING[A-Za-z0-9]+
UriVariable::EMAIL[a-z_-.0-9]+@[a-z_-.0-9]+.[a-z]{2,3}

Pełny schemat URL

Endpointy po zajerestrowaniu i przegenerowaniu konfiguracji dostępne są pod adresem:

https://my/ready/domain/api.php/REST/custom/my/{uri}

  • my/ready/domain - nazwa domeny na którym znajduje się system ready.
  • /my/{uri} - uri podane w konstruktorze klasy EndpointConfig np: /users/{usr_id}

Generowanie response'a w Funkcji processRequest() - szczegółowe omówienie

Procesowanie requesta zawsze musi się zakończyć się wygenerowaniem response'a. Ponieważ nasza klasa endpointu dziedziczy po AbsRestEndpoint to mamy dostęp do funkcji pomocniczych, które wygenerują response zgodnie z naszymi oczekiwaniami.

prepareCustomResponse()

Użyj jeśli chcesz aby twój zwracany JSON miał unikalną strukturę.

 public function processRequest(ApiRequest $request, array $uriVariables): void {
        
        $data = [
             [
               "id" => 1,
               "name" => "Barbara Cacko",
               "email" => "bcacko@edokumenty.eu"
             ],
             [
               "id" => 2,
               "name" => "Jan Nowak",
               "email" => "jnowak@edokumenty.eu"
             ]
        ];
        
        $links = [
            "first" => "https://my/ready/domain/api.php/REST/custom/users?page=1",
            "last" => "https://my/ready/domain/api.php/REST/custom/users/?page=5",
            "prev" => "https://my/ready/domain/api.php/REST/custom/users?page=2",
            "next" => "https://my/ready/domain/api.php/REST/custom/users/?page=4"
        ]; 
        
        $meta = [
            "current_page" => 3,
            "last_page" => 5,
            "per_page" => 2,
            "total" => 10
        ];
        
        $responseBody = [
            'data' => $data,
            'links' => $links,
            'meta' => $meta
        ];
        
        $headers = [
            'myCustom' => 'header'
        ];

        $this->prepareCustomResponse($responseBody, Response::HTTP_OK, $headers);
 }

Tak będzie wyglądać JSON w response body

 {
    "data": [
        {
            "id": 1,
            "name": "Barbara Cacko",
            "email": "bcacko@edokumenty.eu"
        },
        {
            "id": 2,
            "name": "Jan Nowak",
            "email": "jnowak@edokumenty.eu"
        }
    ],
    "links":{
        "first": "https://my/ready/domain/api.php/REST/custom/users?page=1", 
        "last": "https://my/ready/domain/api.php/REST/custom/users/?page=5", 
        "prev": "https://my/ready/domain/api.php/REST/custom/users?page=2", 
        "next": "https://my/ready/domain/api.php/REST/custom/users/?page=4"
    },
    "meta":{
        "current_page": 3,
        "last_page": 5,
        "per_page": 2,
        "total": 10
    }
 }

prepareResponse()

Użyj jeśli chcesz zwrócić identyfikator obiektu. Przydatne dla endpoint'ów typu POST/PUT/PATCH/

   public function processRequest(ApiRequest $request, array $uriVariables): void {

        $data = [
            'dctpid' => 4,
            'dscrpt' => 'Przykładowa notatka'
        ];

        include_once('./classes/eDokumentyApi/EDokApi.inc');
        $localApi = new EDokApi();
        $doc_id = $localApi->createDocument($data);

        $headers = [
            'myCustom' => 'header'
        ];

        $this->prepareResponse('doc_id', $doc_id, Response::HTTP_OK, $headers);
    }

Tak będzie wyglądać JSON w response body

 {
     "data": {
         "doc_id": "11"
     }
 }

prepareResponseDataArray()

Użyj jeśli chcesz zwrócić dane w formacie tablicy obiektów. Przydatne dla endpoint'ów typu GET

  public function processRequest(ApiRequest $request, array $uriVariables): void {

        $db = PgManager::getInstance();
        $data = $db->select('my_register', 'field_1, field_2', 'is_del = FALSE', FALSE, PGSQL_ASSOC);

        $headers = [
            'myCustom' => 'header'
        ];

        $this->prepareResponseDataArray($data, Response::HTTP_OK, NULL, $headers);
  }

Tak będzie wyglądać JSON w response body

 {
     "data": [
         {
             "field_1": "1",
             "field_2": "Dummy data"
         },
         {
             "field_1": "2",
             "field_2": "Bla ble"
         },
         {
             ...
         }
     ]
 }

prepareNotFoundResponse()

Użyj jeśli nie znaleziono szukanego obiektu. Zwraca kod 404. Przydatne dla endpoint'ów typu GET/PATCH/DELETE

 public function processRequest(ApiRequest $request, array $uriVariables): void {
                
        $this->prepareNotFoundResponse();
 }

Tak będzie wyglądać JSON w response body

 {
   "data": []
 }

prepareEmptyResponse()

Zwraca puste response body. domyślnie ustawia kod 204, ale można ustawić inny. Przydatne dla endpoint'ów typu DELETE

 public function processRequest(ApiRequest $request, array $uriVariables): void {
                        
        $this->prepareEmptyResponse();
 }

Tak będzie wyglądać JSON w response body (Tak nie będzie go wcale 😃 )


Klika przykładowych wywołań getConfiguration()

Przykład nr 1

   public function getConfiguration(): EndpointConfig {
        return new EndpointConfig('POST', '/documents');     
   }

Przykład wygeneruje nam URL https://my/ready/domain/api.php/REST/custom/documents

Przykład nr 2

  public function getConfiguration(): EndpointConfig {
       $config = new EndpointConfig('GET', '/documents/{dctptp}/{doc_id}/features');
       $config->setUriVariable('dctptp', UriVariable::STRING);
       $config->setUriVariable('doc_id', UriVariable::INTEGER);
       return $config;
  }

Przykład wygeneruje nam URL https://my/ready/domain/api.php/REST/custom/documents/Note/1/features Zwróć uwagę, że:

  • Note to {dctptp}
  • 1 to {doc_id}

Przykład nr 3

   public function getConfiguration(): EndpointConfig {
        $config = new EndpointConfig('PUT', '/notifications/{msg_id}');
        $config->setUriVariable('msg_id', '[1-9][0-9]*'); // można podawać swój regex
        return $config;
   }

Przykład wygeneruje nam URL https://my/ready/domain/api.php/REST/custom/documents/notifications/10 Zwróć uwagę, że:

  • 10 to {msg_id}