Как написать api для приложения

В данной статье вы узнаете, как создать простой REST API в PHP.

  • 1. Обзор проекта
  • 1.1 Что такое REST API?
  • 1.2 Зачем нужен REST API?
  • 1.3 Где используется REST API?
  • 2. Файловая структура
  • 3. Настройка базы данных
  • 3.1 Создание таблицы категорий
  • 3.2 Дамп данных для таблицы категорий
  • 3.3 Создание таблицы товаров
  • 3.4 Дамп данных для таблицы товаров
  • 3.5 Подключение к базе данных
  • 4. Получение товаров
  • 4.1 Создание объекта Product
  • 4.2 Создание файла для чтения товаров
  • 4.3 Подключение к базе данных и таблице товаров
  • 4.4 Чтение товаров из базы данных
  • 4.5 Создание метода read()
  • 4.6 Уведомление пользователя о том, что товары не найдены
  • 5. Создание товаров
  • 5.1 Создание файла create.php
  • 5.2 Создание метода create()
  • 6. Получение одного товара
  • 6.1 Создание файла read_one.php
  • 6.2 Создание метода readOne()
  • 7. Обновление товара
  • 7.1 Создание файла update.php
  • 7.2 Создание метода update()
  • 8. Удаление товара
  • 8.1 Создание файла delete.php
  • 8.2 Создание метода delete()
  • 9. Поиск товаров
  • 9.1 Создание файла search.php
  • 9.2 Создание метода search()
  • 10. Пагинация товаров
  • 10.1 Создание файла read_paging.php
  • 10.2 Создание файла core.php
  • 10.3 Создание метода readPaging()
  • 10.4 Создание метода count()
  • 10.5 Получение массива пагинации
  • 11. Получение категорий
  • 11.1 Создание класса Category
  • 11.2 Создание метода readAll()
  • 11.3 Создание файла read.php

1. Обзор проекта

1.1 Что такое REST API?

REST — это концепция (архитектура) для организации взаимодействия между независимыми объектами (приложениями) посредством протокола HTTP. Включает в себя набор принципов (рекомендаций) взаимодействия клиент-серверных приложений. Обычно он представлен в формате JSON.

API — интерфейс взаимодействия с каким-либо объектом (программой, приложением), включающий в себя набор правил, которые позволяют одному приложению общаться с другим. Эти «правила» могут включать в себя операции создания, чтения, обновления и удаления. Примером API может служить всем известная библиотека jQuery.

REST API позволяет вашему приложению взаимодействовать с одним или несколькими различными приложениями, используя концепции REST.

RESP API PHP

1.2 Зачем нужен REST API?

Во многих приложениях REST API необходим, потому что это самый легкий способ создания, чтения, обновления или удаления информации между различными приложениями через Интернет или протокол HTTP. Эта информация представляется пользователю в одно мгновение, особенно если вы используете JavaScript для отображения данных на веб-странице.

1.3 Где используется REST API?

REST API может использоваться любым приложением, которое может подключаться к Интернету. Если данные из приложения могут быть созданы, прочитаны, обновлены или удалены с помощью другого приложения, это обычно означает, что используется REST API.

2. Файловая структура

  • api/
    • config/
      • core.php
      • database.php
    • objects/
      • product.php
      • category.php
    • product/
      • create.php/
      • delete.php/
      • read.php/
      • read_paging.php/
      • read_one.php/
      • update.php/
      • search.php/
    • category/
      • read.php
    • shared/
      • utilities.php

3. Настройка базы данных

Используя PhpMyAdmin, создайте новую базу данных api_db. После этого выполните следующие SQL-запросы, чтобы создать новые таблицы с образцами данных.

3.1 Создание таблицы категорий

CREATE TABLE IF NOT EXISTS `categories` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(256) NOT NULL,
    `description` text NOT NULL,
    `created` datetime NOT NULL,
    `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=19 ;

3.2 Дамп данных для таблицы категорий

INSERT INTO `categories` (`id`, `name`, `description`, `created`, `modified`) VALUES
(1, "Fashion", "Category for anything related to fashion.", "2014-06-01 00:35:07", "2014-05-30 17:34:33"),
(2, "Electronics", "Gadgets, drones and more.", "2014-06-01 00:35:07", "2014-05-30 17:34:33"),
(3, "Motors", "Motor sports and more", "2014-06-01 00:35:07", "2014-05-30 17:34:54"),
(5, "Movies", "Movie products.", "2019-05-20 10:22:05", "2019-08-20 10:30:15"),
(6, "Books", "Kindle books, audio books and more.", "2018-03-14 08:05:25", "2019-05-20 11:29:11"),
(13, "Sports", "Drop into new winter gear.", "2016-01-09 02:24:24", "2016-01-09 01:24:24");

3.3 Создание таблицы товаров

CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `description` text NOT NULL,
  `price` decimal(10,0) NOT NULL,
  `category_id` int(11) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=65 ;

3.4 Дамп данных для таблицы товаров

INSERT INTO `products` (`id`, `name`, `description`, `price`, `category_id`, `created`, `modified`) VALUES
(1, "LG P880 4X HD", "My first awesome phone!", "336", 3, "2014-06-01 01:12:26", "2014-05-31 17:12:26"),
(2, "Google Nexus 4", "The most awesome phone of 2013!", "299", 2, "2014-06-01 01:12:26", "2014-05-31 17:12:26"),
(3, "Samsung Galaxy S4", "How about no?", "600", 3, "2014-06-01 01:12:26", "2014-05-31 17:12:26"),
(6, "Bench Shirt", "The best shirt!", "29", 1, "2014-06-01 01:12:26", "2014-05-31 02:12:21"),
(7, "Lenovo Laptop", "My business partner.", "399", 2, "2014-06-01 01:13:45", "2014-05-31 02:13:39"),
(8, "Samsung Galaxy Tab 10.1", "Good tablet.", "259", 2, "2014-06-01 01:14:13", "2014-05-31 02:14:08"),
(9, "Spalding Watch", "My sports watch.", "199", 1, "2014-06-01 01:18:36", "2014-05-31 02:18:31"),
(10, "Sony Smart Watch", "The coolest smart watch!", "300", 2, "2014-06-06 17:10:01", "2014-06-05 18:09:51"),
(11, "Huawei Y300", "For testing purposes.", "100", 2, "2014-06-06 17:11:04", "2014-06-05 18:10:54"),
(12, "Abercrombie Lake Arnold Shirt", "Perfect as gift!", "60", 1, "2014-06-06 17:12:21", "2014-06-05 18:12:11"),
(13, "Abercrombie Allen Brook Shirt", "Cool red shirt!", "70", 1, "2014-06-06 17:12:59", "2014-06-05 18:12:49"),
(26, "Another product", "Awesome product!", "555", 2, "2014-11-22 19:07:34", "2014-11-21 20:07:34"),
(28, "Wallet", "You can absolutely use this one!", "799", 6, "2014-12-04 21:12:03", "2014-12-03 22:12:03"),
(31, "Amanda Waller Shirt", "New awesome shirt!", "333", 1, "2014-12-13 00:52:54", "2014-12-12 01:52:54"),
(42, "Nike Shoes for Men", "Nike Shoes", "12999", 3, "2015-12-12 06:47:08", "2015-12-12 05:47:08"),
(48, "Bristol Shoes", "Awesome shoes.", "999", 5, "2016-01-08 06:36:37", "2016-01-08 05:36:37"),
(60, "Rolex Watch", "Luxury watch.", "25000", 1, "2016-01-11 15:46:02", "2016-01-11 14:46:02");

3.5 Подключение к базе данных

Приведенный ниже код показывает учетные данные базы данных и метод для получения подключения к базе данных с помощью PDO.

Создайте папку api и откройте её. Создайте папку config и в ней создайте файл database.php со следующим кодом.

<?php

class Database
{
    // укажите свои учетные данные базы данных
    private $host = "localhost";
    private $db_name = "api_db";
    private $username = "root";
    private $password = "";
    public $conn;

    // получаем соединение с БД
    public function getConnection()
    {
        $this->conn = null;

        try {
            $this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
            $this->conn->exec("set names utf8");
        } catch (PDOException $exception) {
            echo "Ошибка подключения: " . $exception->getMessage();
        }

        return $this->conn;
    }
}

4. Получение товаров

4.1 Создание объекта Product

Код ниже содержит класс с именем Product и несколько свойств. Также показан метод конструктора, который принимает соединение с базой данных.

Мы будем использовать этот класс для чтения данных из базы. Откройте папку api. Создайте папку objects. Откройте папку её и создайте файл product.php. Поместите в него следующий код.

<?php

class Product
{
    // подключение к базе данных и таблице "products"
    private $conn;
    private $table_name = "products";

    // свойства объекта
    public $id;
    public $name;
    public $description;
    public $price;
    public $category_id;
    public $category_name;
    public $created;

    // конструктор для соединения с базой данных
    public function __construct($db)
    {
        $this->conn = $db;
    }

    // здесь будет метод read()
}

4.2 Создание файла для чтения товаров

Код ниже содержит заголовки о том, кто может читать этот файл и какой тип содержимого он будет возвращать.

В данном случае наш файл read.php может быть прочитан кем угодно (звездочка * означает все) и вернет данные в формате JSON.

Откройте папку api. Создайте в ней папку product. Откройте её и создайте файл read.php со следующим кодом.

<?php

// необходимые HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// подключение к базе данных будет здесь

4.3 Подключение к базе данных и таблице товаров

В приведенном ниже коде мы подключаем файлы database.php и product.php. Это файлы, которые мы создали ранее.

Нам нужно использовать метод getConnection() класса Database для получения соединения с базой данных. Мы передаем это подключение классу Product.

Замените комментарий // подключение к базе данных будет здесь в файле read.php следующим кодом.

// подключение базы данных и файл, содержащий объекты
include_once "../config/database.php";
include_once "../objects/product.php";

// получаем соединение с базой данных
$database = new Database();
$db = $database->getConnection();

// инициализируем объект
$product = new Product($db);
 
// чтение товаров будет здесь

4.4 Чтение товаров из базы данных

В приведенном ниже коде мы используем метод read() класса Product для получения данных из базы. Через переменную $num мы проверяем, найдены ли записи.

Если найдены записи, мы перебираем их с помощью цикла while, и добавляем каждую запись в массив $products_arr, устанавливаем код ответа 200 OK и показываем его пользователю в формате JSON.

Замените комментарий // чтение товаров будет здесь в файле read.php следующим кодом.

// запрашиваем товары
$stmt = $product->read();
$num = $stmt->rowCount();

// проверка, найдено ли больше 0 записей
if ($num > 0) {
    // массив товаров
    $products_arr = array();
    $products_arr["records"] = array();

    // получаем содержимое нашей таблицы
    // fetch() быстрее, чем fetchAll()
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        // извлекаем строку
        extract($row);
        $product_item = array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description),
            "price" => $price,
            "category_id" => $category_id,
            "category_name" => $category_name
        );
        array_push($products_arr["records"], $product_item);
    }

    // устанавливаем код ответа - 200 OK
    http_response_code(200);

    // выводим данные о товаре в формате JSON
    echo json_encode($products_arr);
}

// "товары не найдены" будет здесь

4.5 Создание метода read()

Мы использовали метод read() в предыдущем разделе, но он пока ещё не существует в классе Product. Нам нужно добавить этот метод. С помощью кода ниже, мы делаем запрос для получения записей из базы данных.

Откройте папку objects. Откройте файл product.php. Поместите следующий код в класс Product перед последней закрывающей фигурной скобкой вместо комментария // здесь будет метод read().

// метод для получения товаров
function read()
{
    // выбираем все записи
    $query = "SELECT
        c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
    FROM
        " . $this->table_name . " p
        LEFT JOIN
            categories c
                ON p.category_id = c.id
    ORDER BY
        p.created DESC";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // выполняем запрос
    $stmt->execute();
    return $stmt;
}

4.6 Уведомление пользователя о том, что товары не найдены

Если переменная $num имеет нулевое или отрицательное значение, это означает, что из базы данных не возвращено никаких записей. Мы должны сообщить пользователю об этом.

В приведенном ниже коде мы устанавливаем код ответа 404 — Не найдено и сообщение, что Товары не найдены.

Замените комментарий // «товары не найдены» будет здесь в файле read.php следующим кодом.

else {
    // установим код ответа - 404 Не найдено
    http_response_code(404);

    // сообщаем пользователю, что товары не найдены
    echo json_encode(array("message" => "Товары не найдены."), JSON_UNESCAPED_UNICODE);
}

5. Создание товаров

5.1 Создание файла create.php

Откройте папку product и создайте в ней файл create.php со следующим содержимым.

<?php

// необходимые HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// получаем соединение с базой данных
include_once "../config/database.php";

// создание объекта товара
include_once "../objects/product.php";
$database = new Database();
$db = $database->getConnection();
$product = new Product($db);

// получаем отправленные данные
$data = json_decode(file_get_contents("php://input"));

// убеждаемся, что данные не пусты
if (
    !empty($data->name) &&
    !empty($data->price) &&
    !empty($data->description) &&
    !empty($data->category_id)
) {
    // устанавливаем значения свойств товара
    $product->name = $data->name;
    $product->price = $data->price;
    $product->description = $data->description;
    $product->category_id = $data->category_id;
    $product->created = date("Y-m-d H:i:s");

    // создание товара
    if ($product->create()) {
        // установим код ответа - 201 создано
        http_response_code(201);

        // сообщим пользователю
        echo json_encode(array("message" => "Товар был создан."), JSON_UNESCAPED_UNICODE);
    }
    // если не удается создать товар, сообщим пользователю
    else {
        // установим код ответа - 503 сервис недоступен
        http_response_code(503);

        // сообщим пользователю
        echo json_encode(array("message" => "Невозможно создать товар."), JSON_UNESCAPED_UNICODE);
    }
}
// сообщим пользователю что данные неполные
else {
    // установим код ответа - 400 неверный запрос
    http_response_code(400);

    // сообщим пользователю
    echo json_encode(array("message" => "Невозможно создать товар. Данные неполные."), JSON_UNESCAPED_UNICODE);
}

5.2 Создание метода create()

Откройте папку objects. Откройте файл product.php и добавьте следующий код внутри класса Product (objects / product.php).

// метод для создания товаров
function create()
{
    // запрос для вставки (создания) записей
    $query = "INSERT INTO
            " . $this->table_name . "
        SET
            name=:name, price=:price, description=:description, category_id=:category_id, created=:created";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // очистка
    $this->name = htmlspecialchars(strip_tags($this->name));
    $this->price = htmlspecialchars(strip_tags($this->price));
    $this->description = htmlspecialchars(strip_tags($this->description));
    $this->category_id = htmlspecialchars(strip_tags($this->category_id));
    $this->created = htmlspecialchars(strip_tags($this->created));

    // привязка значений
    $stmt->bindParam(":name", $this->name);
    $stmt->bindParam(":price", $this->price);
    $stmt->bindParam(":description", $this->description);
    $stmt->bindParam(":category_id", $this->category_id);
    $stmt->bindParam(":created", $this->created);

    // выполняем запрос
    if ($stmt->execute()) {
        return true;
    }
    return false;
}

6. Получение одного товара

6.1 Создание файла read_one.php

Откройте папку product, создайте в ней файл read_one.php со следующим содержимым.

<?php

// необходимые HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: GET");
header("Access-Control-Allow-Credentials: true");
header("Content-Type: application/json");

// подключение файла для соединения с базой и файл с объектом
include_once "../config/database.php";
include_once "../objects/product.php";

// получаем соединение с базой данных
$database = new Database();
$db = $database->getConnection();

// подготовка объекта
$product = new Product($db);

// установим свойство ID записи для чтения
$product->id = isset($_GET["id"]) ? $_GET["id"] : die();

// получим детали товара
$product->readOne();

if ($product->name != null) {

    // создание массива
    $product_arr = array(
        "id" =>  $product->id,
        "name" => $product->name,
        "description" => $product->description,
        "price" => $product->price,
        "category_id" => $product->category_id,
        "category_name" => $product->category_name
    );

    // код ответа - 200 OK
    http_response_code(200);

    // вывод в формате json
    echo json_encode($product_arr);
} else {
    // код ответа - 404 Не найдено
    http_response_code(404);

    // сообщим пользователю, что такой товар не существует
    echo json_encode(array("message" => "Товар не существует"), JSON_UNESCAPED_UNICODE);
}

6.2 Создание метода readOne()

Откройте папку objects. Откройте файл product.php и добавьте следующий код внутри класса Product.

// метод для получения конкретного товара по ID
function readOne()
{
    // запрос для чтения одной записи (товара)
    $query = "SELECT
            c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
        FROM
            " . $this->table_name . " p
            LEFT JOIN
                categories c
                    ON p.category_id = c.id
        WHERE
            p.id = ?
        LIMIT
            0,1";
            
    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // привязываем id товара, который будет получен
    $stmt->bindParam(1, $this->id);

    // выполняем запрос
    $stmt->execute();

    // получаем извлеченную строку
    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    // установим значения свойств объекта
    $this->name = $row["name"];
    $this->price = $row["price"];
    $this->description = $row["description"];
    $this->category_id = $row["category_id"];
    $this->category_name = $row["category_name"];
}

7. Обновление товара

7.1 Создание файла update.php

Откройте папку product, создайте в ней файл update.php и поместите в него следующий код.

<?php

// HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// подключаем файл для работы с БД и объектом Product
include_once "../config/database.php";
include_once "../objects/product.php";

// получаем соединение с базой данных
$database = new Database();
$db = $database->getConnection();

// подготовка объекта
$product = new Product($db);

// получаем id товара для редактирования
$data = json_decode(file_get_contents("php://input"));

// установим id свойства товара для редактирования
$product->id = $data->id;

// установим значения свойств товара
$product->name = $data->name;
$product->price = $data->price;
$product->description = $data->description;
$product->category_id = $data->category_id;

// обновление товара
if ($product->update()) {
    // установим код ответа - 200 ok
    http_response_code(200);

    // сообщим пользователю
    echo json_encode(array("message" => "Товар был обновлён"), JSON_UNESCAPED_UNICODE);
}
// если не удается обновить товар, сообщим пользователю
else {
    // код ответа - 503 Сервис не доступен
    http_response_code(503);

    // сообщение пользователю
    echo json_encode(array("message" => "Невозможно обновить товар"), JSON_UNESCAPED_UNICODE);
}

7.2 Создание метода update()

В папке objects откройте файл product.php и добавьте новый метод update() внутри класса Product.

// метод для обновления товара
function update()
{
    // запрос для обновления записи (товара)
    $query = "UPDATE
            " . $this->table_name . "
        SET
            name = :name,
            price = :price,
            description = :description,
            category_id = :category_id
        WHERE
            id = :id";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // очистка
    $this->name = htmlspecialchars(strip_tags($this->name));
    $this->price = htmlspecialchars(strip_tags($this->price));
    $this->description = htmlspecialchars(strip_tags($this->description));
    $this->category_id = htmlspecialchars(strip_tags($this->category_id));
    $this->id = htmlspecialchars(strip_tags($this->id));

    // привязываем значения
    $stmt->bindParam(":name", $this->name);
    $stmt->bindParam(":price", $this->price);
    $stmt->bindParam(":description", $this->description);
    $stmt->bindParam(":category_id", $this->category_id);
    $stmt->bindParam(":id", $this->id);

    // выполняем запрос
    if ($stmt->execute()) {
        return true;
    }
    return false;
}

8. Удаление товара

8.1 Создание файла delete.php

Откройте папку product и создайте файл delete.php со следующим содержимым.

<?php

// HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// подключим файл для соединения с базой и объектом Product
include_once "../config/database.php";
include_once "../objects/product.php";

// получаем соединение с БД
$database = new Database();
$db = $database->getConnection();

// подготовка объекта
$product = new Product($db);

// получаем id товара
$data = json_decode(file_get_contents("php://input"));

// установим id товара для удаления
$product->id = $data->id;

// удаление товара
if ($product->delete()) {
    // код ответа - 200 ok
    http_response_code(200);

    // сообщение пользователю
    echo json_encode(array("message" => "Товар был удалён"), JSON_UNESCAPED_UNICODE);
}
// если не удается удалить товар
else {
    // код ответа - 503 Сервис не доступен
    http_response_code(503);

    // сообщим об этом пользователю
    echo json_encode(array("message" => "Не удалось удалить товар"));
}

8.2 Создание метода delete()

В папке objects откройте файл product.php и добавьте новый метод в класс Product.

// метод для удаления товара
function delete()
{
    // запрос для удаления записи (товара)
    $query = "DELETE FROM " . $this->table_name . " WHERE id = ?";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // очистка
    $this->id = htmlspecialchars(strip_tags($this->id));

    // привязываем id записи для удаления
    $stmt->bindParam(1, $this->id);

    // выполняем запрос
    if ($stmt->execute()) {
        return true;
    }
    return false;
}

9. Поиск товаров

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

9.1 Создание файла search.php

В папке product создайте файл search.php со следующим кодом.

<?php

// HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// подключение необходимых файлов
include_once "../config/core.php";
include_once "../config/database.php";
include_once "../objects/product.php";

// создание подключения к БД
$database = new Database();
$db = $database->getConnection();

// инициализируем объект
$product = new Product($db);

// получаем ключевые слова
$keywords = isset($_GET["s"]) ? $_GET["s"] : "";

// запрос товаров
$stmt = $product->search($keywords);
$num = $stmt->rowCount();

// проверяем, найдено ли больше 0 записей
if ($num > 0) {
    // массив товаров
    $products_arr = array();
    $products_arr["records"] = array();

    // получаем содержимое нашей таблицы
    // fetch() быстрее чем fetchAll()
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

        // извлечём строку
        extract($row);
        $product_item = array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description),
            "price" => $price,
            "category_id" => $category_id,
            "category_name" => $category_name
        );
        array_push($products_arr["records"], $product_item);
    }
    // код ответа - 200 OK
    http_response_code(200);

    // покажем товары
    echo json_encode($products_arr);
} else {
    // код ответа - 404 Ничего не найдено
    http_response_code(404);

    // скажем пользователю, что товары не найдены
    echo json_encode(array("message" => "Товары не найдены."), JSON_UNESCAPED_UNICODE);
}

9.2 Создание метода search()

В папке objects откройте product.php и добавьте метод search().

// метод для поиска товаров
function search($keywords)
{
    // поиск записей (товаров) по "названию товара", "описанию товара", "названию категории"
    $query = "SELECT
            c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
        FROM
            " . $this->table_name . " p
            LEFT JOIN
                categories c
                    ON p.category_id = c.id
        WHERE
            p.name LIKE ? OR p.description LIKE ? OR c.name LIKE ?
        ORDER BY
            p.created DESC";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // очистка
    $keywords = htmlspecialchars(strip_tags($keywords));
    $keywords = "%{$keywords}%";

    // привязка
    $stmt->bindParam(1, $keywords);
    $stmt->bindParam(2, $keywords);
    $stmt->bindParam(3, $keywords);

    // выполняем запрос
    $stmt->execute();

    return $stmt;
}

10. Пагинация товаров

10.1 Создание файла read_paging.php

В папке product создайте файл read_paging.php со следующим кодом.

<?php

// установим HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// подключение файлов
include_once "../config/core.php";
include_once "../shared/utilities.php";
include_once "../config/database.php";
include_once "../objects/product.php";

// utilities
$utilities = new Utilities();

// создание подключения
$database = new Database();
$db = $database->getConnection();

// инициализация объекта
$product = new Product($db);

// запрос товаров
$stmt = $product->readPaging($from_record_num, $records_per_page);
$num = $stmt->rowCount();

// если больше 0 записей
if ($num > 0) {

    // массив товаров
    $products_arr = array();
    $products_arr["records"] = array();
    $products_arr["paging"] = array();

    // получаем содержимое нашей таблицы
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

        // извлечение строки
        extract($row);
        $product_item = array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description),
            "price" => $price,
            "category_id" => $category_id,
            "category_name" => $category_name
        );
        array_push($products_arr["records"], $product_item);
    }

    // подключим пагинацию
    $total_rows = $product->count();
    $page_url = "{$home_url}product/read_paging.php?";
    $paging = $utilities->getPaging($page, $total_rows, $records_per_page, $page_url);
    $products_arr["paging"] = $paging;

    // установим код ответа - 200 OK
    http_response_code(200);

    // вывод в json-формате
    echo json_encode($products_arr);
} else {

    // код ответа - 404 Ничего не найдено
    http_response_code(404);

    // сообщим пользователю, что товаров не существует
    echo json_encode(array("message" => "Товары не найдены"), JSON_UNESCAPED_UNICODE);
}

10.2 Создание файла core.php

Этот файл содержит нашу базовую конфигурацию, такую как базовый URL и переменные пагинации.

Откройте папку config и создайте в ней файл core.php со следующим содержимым.

<?php

// показывать сообщения об ошибках
ini_set("display_errors", 1);
error_reporting(E_ALL);

// URL домашней страницы
$home_url = "http://localhost/api/";

// страница указана в параметре URL, страница по умолчанию одна
$page = isset($_GET["page"]) ? $_GET["page"] : 1;

// установка количества записей на странице
$records_per_page = 5;

// расчёт для запроса предела записей
$from_record_num = ($records_per_page * $page) - $records_per_page;

10.3 Создание метода readPaging()

В папке objects откройте файл product.php и добавьте метод readPaging(). Этот метод вернет список записей, ограниченный тем, что мы установили в $records_per_page файла core.php.

// получение товаров с пагинацией
public function readPaging($from_record_num, $records_per_page)
{
    // выборка
    $query = "SELECT
            c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
        FROM
            " . $this->table_name . " p
            LEFT JOIN
                categories c
                    ON p.category_id = c.id
        ORDER BY p.created DESC
        LIMIT ?, ?";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // свяжем значения переменных
    $stmt->bindParam(1, $from_record_num, PDO::PARAM_INT);
    $stmt->bindParam(2, $records_per_page, PDO::PARAM_INT);

    // выполняем запрос
    $stmt->execute();

    // вернём значения из базы данных
    return $stmt;
}

10.4 Создание метода count()

Так же в классе Product (файл product.php) добавьте метод count() для подсчёта количества товаров.

// данный метод возвращает кол-во товаров
public function count()
{
    $query = "SELECT COUNT(*) as total_rows FROM " . $this->table_name . "";

    $stmt = $this->conn->prepare($query);
    $stmt->execute();

    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    return $row["total_rows"];
}

10.5 Получение массива пагинации

В корне создайте папку shared, в ней файл utilities.php со следующим кодом.

<?php

class Utilities
{
    public function getPaging($page, $total_rows, $records_per_page, $page_url)
    {
        // массив пагинации
        $paging_arr = array();

        // кнопка для первой страницы
        $paging_arr["first"] = $page > 1 ? "{$page_url}page=1" : "";

        // подсчёт всех товаров в базе данных для подсчета общего количества страниц
        $total_pages = ceil($total_rows / $records_per_page);

        // диапазон ссылок для показа
        $range = 2;

        // отображать диапазон ссылок вокруг текущей страницы
        $initial_num = $page - $range;
        $condition_limit_num = ($page + $range) + 1;
        $paging_arr["pages"] = array();
        $page_count = 0;

        for ($x = $initial_num; $x < $condition_limit_num; $x++) {
            // убедимся, что $x > 0 И $x <= $total_pages
            if (($x > 0) && ($x <= $total_pages)) {
                $paging_arr["pages"][$page_count]["page"] = $x;
                $paging_arr["pages"][$page_count]["url"] = "{$page_url}page={$x}";
                $paging_arr["pages"][$page_count]["current_page"] = $x == $page ? "yes" : "no";
                $page_count++;
            }
        }

        // кнопка для последней страницы
        $paging_arr["last"] = $page < $total_pages ? "{$page_url}page={$total_pages}" : "";

        // формат json
        return json_encode($paging_arr);
    }
}

11. Получение категорий

11.1 Создание класса Category

Откройте папку objects и создайте новый файл category.php со следующим кодом.

<?php

class Category
{
    // соединение с БД и таблицей "categories"
    private $conn;
    private $table_name = "categories";

    // свойства объекта
    public $id;
    public $name;
    public $description;
    public $created;

    public function __construct($db)
    {
        $this->conn = $db;
    }

    // здесь будет метод для получение всех категорий товаров
}

11.2 Создание метода readAll()

С помощью данного метода мы получим все категории товаров из базы данных. замените комментарий // здесь будет метод для получение всех категорий товаров следующим кодом:

// метод для получения всех категорий товаров
public function readAll()
{
    $query = "SELECT
                id, name, description
            FROM
                " . $this->table_name . "
            ORDER BY
                name";

    $stmt = $this->conn->prepare($query);
    $stmt->execute();

    return $stmt;
}

11.3 Создание файла read.php

Создайте новую папку category в корне, и в ней файл read.php со следующим кодом.

<?php

// установим HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// подключение файлов для соединения с БД и файл с объектом Category
include_once "../config/database.php";
include_once "../objects/category.php";

// создание подключения к базе данных
$database = new Database();
$db = $database->getConnection();

// инициализация объекта
$category = new Category($db);

// получаем категории
$stmt = $category->readAll();
$num = $stmt->rowCount();

// проверяем, найдено ли больше 0 записей
if ($num > 0) {

    // массив для записей
    $categories_arr = array();
    $categories_arr["records"] = array();

    // получим содержимое нашей таблицы
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

        // извлекаем строку
        extract($row);
        $category_item = array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description)
        );
        array_push($categories_arr["records"], $category_item);
    }
    // код ответа - 200 OK
    http_response_code(200);

    // покажем данные категорий в формате json
    echo json_encode($categories_arr);
} else {

    // код ответа - 404 Ничего не найдено
    http_response_code(404);

    // сообщим пользователю, что категории не найдены
    echo json_encode(array("message" => "Категории не найдены"), JSON_UNESCAPED_UNICODE);
}

API готов к использованию!

Frontend часть данного приложения (продолжение) — jQuery + AJAX + JSON.

Если вам понравилась данная статья, рекомендую к прочтению создание регистрации и авторизации в php с использованием JWT.

Перевод статьи «Best Practices: API Design».

Хорошо спроектированные API = довольные разработчики.

Прикладные программные интерфейсы (Application Programming Interfaces, API) — это интерфейсы, облегчающие использование в приложениях данных и ресурсов других приложений. Они жизненно необходимы для успеха продукта или даже компании.

Без API большинства ваших любимых программ попросту не существовало бы. Например, API Google Maps позволяет вам использовать карты Google в вашем мобильном или веб-приложении. Без него вам пришлось бы разрабатывать вашу собственную базу данных с картами! Только представьте, сколько времени это заняло бы.

Почему мы используем API?

  • API обеспечивают сторонний доступ к вашим ресурсам.
  • API расширяют возможности вашего приложения.
  • API позволяют разработчикам повторно использовать логику приложения.
  • API «платформонезависимы», т. е., они могут доставлять данные, не оглядываясь на особенности запрашивающей платформы.

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

Моделирование и структурирование данных

Моделирование данных с оглядкой на будущий API это первый шаг на пути к его легкому созданию, поддержке и обновлению.

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

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

Представьте, что вы строите портал, где пользователи смогут просматривать информацию по книгам различных авторов. В вашей компании могут использоваться свои, специфические термины, например, Storytellers, creations, series (букв. авторы рассказов, произведения, серии), при том, что имеются в виду Authors, books и series (авторы, книги, серии). Для простоты и чтобы сторонние разработчики могли использовать ваши API, имеет смысл придерживаться более общей терминологии при создании путей API.

https://api.domain.com/authors
https://api.domain.com/authors/{id}/books

Таким образом любой разработчик сможет куда быстрее разобраться, что из себя представляет ваш API.

Написание ресурсо-ориентированных API

Приложения, использующие ваши API, хотят получить доступ к вашим ресурсам. Поддержка иерархии ресурсов поможет лучше структурировать ваш API. Каждый узел в вашем пути, содержащий какой-либо ресурс или коллекцию ресурсов, должен занимать определнное место в иерархии.

Ресурсом может быть отдельный кусочек данных, скажем, информация об авторе (если продолжать использовать пример, приведенный выше). Коллекцией ресурсов в нашем случае может быть список книг, написанных отдельным автором.

Иерархия ресурсов при этом может выглядеть следующим образом:

Base Path -> Authors (collection) -> profile (resource)
Base Path -> Authors (collection) -> books (collection) -> book (resource)

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

Вот несколько советов по поддержке постоянства и простоты:

  1. В именах коллекций и ресурсов используйте американский английский (например, color, а не colour).
  2. Следите за правописанием.
  3. Используйте более простые, широко употребимые слова, чтобы все было максимально ясно (например, delete, а не remove).
  4. Если используете те же ресурсы, что и в другом API, используйте ту же терминологию.
  5. Для коллекций используйте множественные формы (например, authors, books и т. п.).

REST

REST (Representational State Transfer) — это наиболее широко используемый стандарт для отправки HTTP-запросов к API. Суть этого подхода в том, что каждый URL представляет объект.

У API может быть одно из следующих назначений:

  1. Создание данных.
  2. Чтение данных.
  3. Обновление данных.
  4. Удаление данных.

Да, вы угадали, это CRUD!

Назначения API регулируются набором HTTP-слов, определяющих природу запроса и то, что он должен делать.

Слово GET ассоциируется с получением данных от API. Оно запрашивает представление данных. Запрос GET может включать параметры запроса для фильтрации результатов, полученных от API.

Слово POST связано с отправкой информации к API, в результате чего в базе данных будет создан ресурс.

Слово PUT обычно используется для обновления уже имеющихся на сервере ресурсов.

Слово DELETE служит для удаления ресурса с сервера.

Разбираемся с минорными и мажорными обновлениями

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

Мажорные обновления с большей вероятностью способны сломать приложение. К таким обновлениям можно отнести добавление нового обязательного параметра в полезную нагрузку запроса или изменения полей в ответе.

Есть несколько способов версионирования ваших API.

Самый распространенный способ — включение версии в URI.

https://api.domain.com/v1.0/authors

В основе следующего способа лежит дата. При этом в URI включается дата выпуска версии. Преимущество этого способа в том, что любой разработчик сможет сразу увидеть, насколько часто вы обновляете ваш API.

https://api.domain.com/2020-06-15/authors

Третий способ — включение версии API в заголовки.

https://api.domain.com/authors
x-api-version: v1

Самый рекомендуемый и приемлемый вариант версионирования — использовать имя версии в URI.

Разбивка на страницы

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

Чтобы сделать возможной разбивку на страницы в вашем API, рекомендуется использовать особые фразы в полезной нагрузке запроса и ответа:

  1. STRING page_token (отправляется в запросе)
  2. STRING next_page_token (возвращается API)
  3. INT page_size (отправляется в запросе)

page_token указывает, какую именно страницу должен вернуть API. Обычно это строка. Для первого вызова API page_token = “1”.

page_size определяет, сколько записей должно вернуться в ответе. Например, page_size = 100 вернет максимум 100 записей по одному вызову API.

next_page_token определяет следующий токен в серии. Если после page_token="1" есть дополнительные данные, возвращается значение next_page_token=”2”. Если же данных больше нет, т. е., пользователь исчерпал их до конца, возвращается пустое значение next_page_token=””.

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

Рассказывает Владимир, веб-разработчик Noveo


Большинству разработчиков сайтов, веб-сервисов и мобильных приложений рано или поздно приходится иметь дело с клиент-серверной архитектурой, а именно разрабатывать web API или интегрироваться с ним. Чтобы не изобретать каждый раз что-то новое, важно выработать относительно универсальный подход к проектированию web API, основываясь на опыте разработки подобных систем. Предлагаем вашему вниманию объединенный цикл статей, посвящённых этому вопросу.

Приближение первое: Действующие лица

В один прекрасный момент, в процессе создания очередного веб-сервиса, я решил собрать все свои знания и размышления на тему проектирования web API для обслуживания нужд клиентских приложений и оформить их в виде статьи или серии статей. Разумеется, мой опыт не претендует на абсолют, и конструктивная критика и дополнения более чем приветствуются.

Чтиво получилось больше философское, нежели техническое, но и для любителей технической части здесь будет над чем поразмыслить. Сомневаюсь, что скажу в этой статье что-то принципиально новое, то, о чем вы никогда не слышали, не читали и о чем не думали сами. Просто попытаюсь уложить все в единую систему, в первую очередь в своей собственной голове, а это уже дорогого стоит. Тем не менее, буду рад, если мои измышления будут вам полезны в вашей практике. Итак, поехали.

Клиент и сервер

Сервером в данном случае мы считаем абстрактную машину в сети, способную получить HTTP-запрос, обработать его и вернуть корректный ответ. В контексте данной статьи совершенно не важны его физическая суть и внутренняя архитектура, будь то студенческий ноутбук или огромный кластер из промышленных серверов, разбросанных по всему миру. Нам в той же мере совершенно неважно, что у него под капотом, кто встречает запрос у дверей, Apache или Nginx, какой неведомый зверь, PHP, Python или Ruby выполняет его обработку и формирует ответ, какое хранилище данных используется: Postgresql, MySQL или MongoDB. Главное, чтобы сервер отвечал главному правилу — услышать, понять и простить ответить.

Клиентом тоже может быть все, что угодно, что способно сформировать и отправить HTTP-запрос. До определенного момента в этой статье нам также не особо будут интересны цели, которые ставит перед собой клиент, отправляя этот запрос, как и то, что он будет делать с ответом. Клиентом может быть JavaScript-сценарий, работающий в браузере, мобильное приложение, злой (или не очень) демон, запущенный на сервере, или слишком поумневший холодильник (уже есть и такие).

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

Философия REST

REST (Representational state transfer) изначально был задуман как простой и однозначный интерфейс для управления данными, предполагавший всего несколько базовых операций с непосредственным сетевым хранилищем (сервером): извлечение данных (GET), сохранение (POST), изменение (PUT/PATCH) и удаление (DELETE). Разумеется, этот перечень всегда сопровождался такими опциями, как обработка ошибок в запросе (корректно ли составлен запрос), разграничение доступа к данным (вдруг этого вам знать не следует) и валидация входящих данных (вдруг вы написали ерунду), в общем, всеми возможными проверками, которые сервер выполняет перед тем, как выполнить желание клиента.

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

Независимость сервера от клиента — серверы и клиенты могут быть мгновенно заменены другими независимо друг от друга, так как интерфейс между ними не меняется. Сервер не хранит состояний клиента.
Уникальность адресов ресурсов — каждая единица данных (любой степени вложенности) имеет свой собственный уникальный URL, который, по сути, целиком является однозначным идентификатором ресурса.

Пример: GET /api/v1/users/25/name

Независимость формата хранения данных от формата их передачи — сервер может поддерживать несколько различных форматов для передачи одних и тех же данных (JSON, XML и т.д.), но хранит данные в своем внутреннем формате, независимо от поддерживаемых.

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

Чего нам не хватает

Классический REST подразумевает работу клиента с сервером как с плоским хранилищем данных, при этом ничего не говорится о связанности и взаимозависимости данных между собой. Все это по умолчанию целиком ложится на плечи клиентского приложения. Однако современные предметные области, для которых разрабатываются системы управления данными, будь то социальные сервисы или системы интернет-маркетинга, подразумевают сложную взаимосвязь между сущностями, хранящимися в базе данных. Поддержка этих связей, т.е. целостности данных, находится в зоне ответственности серверной стороны, в то время, как клиент является только интерфейсом для доступа к этим данным. Так чего же нам не хватает в REST?

Вызовы функций

Чтобы не менять данные и связи между ними вручную, мы просто вызываем у ресурса функцию и «скармливаем» ей в качестве аргумента необходимые данные. Эта операция не подходит под стандарты REST, для нее не существует особого глагола, что заставляет нас, разработчиков, выкручиваться кто во что горазд.

Самый простой пример – авторизация пользователя. Мы вызываем функцию login, передаем ей в качестве аргумента объект, содержащий учетные данные, и в ответ получаем ключ доступа. Что творится с данными на серверной стороне – нас не волнует.

Еще вариант – создание и разрыв связей между данными. Например, добавление пользователя в группу. Вызываем у сущности группа функцию addUser, в качестве параметра передаем объект пользователь, получаем результат.

А еще бывают операции, которые вообще не связаны напрямую с сохранением данных как таковых, например, рассылка уведомлений, подтверждение или отклонение каких-либо операций (завершение отчетного периода etc).

В одной из следующих статей я постараюсь классифицировать эти операции и предложить варианты возможных запросов и ответов, основываясь на том, с какими из них мне приходилось сталкиваться на практике.

Множественные операции

Часто бывает так, и разработчики клиентов поймут, о чем я, что клиентскому приложению удобнее создавать/изменять/удалять/ сразу несколько однородных объектов одним запросом, и по каждому объекту возможен свой вердикт серверной стороны. Тут есть как минимум несколько вариантов: либо все изменения выполнены, либо они выполнены частично (для части объектов), либо произошла ошибка. Ну и стратегий тоже несколько: применять изменения только в случае успеха для всех, либо применять частично, либо откатываться в случае любой ошибки, а это уже тянет на полноценный механизм транзакций.

Для web API, стремящегося к идеалу, тоже хотелось бы как-то привести подобные операции в систему. Постараюсь сделать это в одном из продолжений.

Статистические запросы, агрегаторы, форматирование данных

Частенько бывает так, что на основе хранимых на сервере данных нам нужно получить статистическую выжимку или данные, отформатированные особым образом: например, для построения графика на стороне клиента. По сути это данные, генерируемые по требованию, в той или иной мере на лету, и доступные только для чтения, так что имеет смысл вынести их в отдельную категорию. Одной из отличительных особенностей статистических данных, на мой взгляд, является то, что они не имеют уникального ID.

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

Разновидности данных

Объекты

Ключевым типом данных в общении между клиентом и сервером выступает объект. По сути, объект – это перечень свойств и соответствующих им значений. Мы можем отправить объект на сервер в запросе и получить в результат запроса в виде объекта. При этом объект не обязательно будет реальной сущностью, хранящейся в базе данных, по крайней мере, в том виде, в котором он отправлен или получен. Например, учетные данные для авторизации передаются в виде объекта, но не являются самостоятельной сущностью. Даже хранимые в БД объекты склонны обрастать дополнительными свойствами внутрисистемного характера, например, датами создания и редактирования, различными системными метками и флагами. Свойства объектов могут быть как собственными скалярными значениями, так и содержать связанные объекты и коллекции объектов, которые не являются частью объекта. Часть свойств объектов может быть редактируемой, часть системной, доступной только для чтения, а часть может носить статистический характер и вычисляться на лету (например, количество лайков). Некоторые свойства объекта могут быть скрыты, в зависимости от прав пользователя.

Коллекции объектов

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

Скалярные значения

В чистом виде скалярные значения как отдельная сущность на моей памяти встречались крайне редко. Обычно они фигурировали как свойства объектов или коллекций, и в этом качестве они могут быть доступны как для чтения, так и для записи. Например, имя пользователя может быть получено и изменено в индивидуальном порядке GET /users/1/name. На практике эта возможность пригождается редко, но в случае необходимости хотелось бы, чтобы она была под рукой. Особенно это касается свойств коллекции, например числа записей (с фильтрацией или без нее): GET /news/count.

В одной из следующих статей я постараюсь классифицировать эти операции и предложить варианты возможных запросов и ответов, основываясь на том, с какими из них мне приходилось сталкиваться на практике.

Приближение второе: Правильный путь

В этом приближении я хотел бы отдельно поговорить о подходах к построению уникальных путей к ресурсам и методам вашего web API и о тех архитектурных особенностях приложения, которые влияют на внешний вид этого пути и его компоненты.

О чем стоит подумать, стоя на берегу

Версионность

Рано или поздно любая действующая система начинает эволюционировать: развиваться, усложняться, масштабироваться, усовремениваться. Для разработчиков REST API это чревато в первую очередь тем, что необходимо запускать новые версии API при работающих старых. Здесь я говорю больше не об архитектурных изменениях под капотом вашей системы, а о том, что изменяется сам формат данных и набор операций с ними. В любом случае версионность нужно предусмотреть как в изначальной организации исходного кода, так и в принципе построения URL. Что касается URL, здесь существует два наиболее популярных способа указания версии API, которой адресован запрос. Префиксация пути example-api.com/v1/ и разведение версий на уровне субдомена v1.example-api.com . Использовать можно любой из них, в зависимости от потребности и необходимости.

Автономность компонентов

Web API сложных систем, поддерживающих несколько пользовательских ролей, зачастую требует разделения на части, каждая из которых обслуживает свой спектр задач. По сути, каждая часть может быть самостоятельным приложением, работать на разных физических машинах и платформах. В контексте описания API нам совершенно не важно, как сервер обрабатывает запрос и какие силы и технологии в этом замешаны. Для клиента API – система инкапсулированная. Тем не менее разные части системы могут обладать совершенно разной функциональностью, например, административная и пользовательская часть. И методология работы с одними и теми же, казалось бы, ресурсами может существенно отличаться. Поэтому такие части необходимо разделять на уровне домена admin.v1.example-api.com или префикса пути example-api.com/v1/admin/ . Это требование не является обязательным, и многое зависит от сложности системы и её назначения.

Формат обмена данными

Самым удобным и функциональным, на мой взгляд, форматом обмена данными является JSON, но никто не запрещает использовать XML, YAML или любой другой формат, позволяющий хранить сериализованные объекты без потери типа данных. При желании можно сделать в API поддержку нескольких форматов ввода/вывода. Достаточно задействовать HTTP заголовок запроса для указания желаемого формата ответа Accept и Content-Type для указания формата переданных в запросе данных. Другим популярным способом является добавление расширения к URL ресурса, например, GET /users.xml , но такой способ кажется менее гибким и красивым, хотя бы потому, что утяжеляет URL и верен скорее для GET-запросов, нежели для всех возможных операций.

Локализация и многоязычность

На практике многоязычность API чаще всего сводится к переводу сервисных сообщений и сообщений об ошибках на требуемый язык для прямого отображения конечному пользователю. Многоязычный контент тоже имеет место быть, но сохранение и выдача контента на разных языках, на мой взгляд, должна разграничиваться более явно, например, если у вас одна и та же статья существует на разных языках, то по факту это две разных сущности, сгруппированные по признаку единства содержания. Для идентификации ожидаемого языка можно использовать разные способы. Самым простым можно считать стандартный HTTP-заголовок Accept-Language . Я встречал и другие способы, такие, как добавление GET-параметра language="en" , использование префикса пути example-api.com/en/ или даже на уровне доменного имени en.example-api.com . Мне кажется, что выбор способа указания локали зависит от конкретного приложения и задач, стоящих перед ним.

Внутренняя маршрутизация

Итак, мы добрались до корневого узла нашего API (или одного из его компонентов). Все дальнейшие маршруты будут проходить уже непосредственно внутри вашего серверного приложения, в соответствии с поддерживаемым им набором ресурсов.

Пути к коллекциям

Для указания пути к коллекции мы просто используем название соответствующей сущности, например, если это список пользователей, то путь будет таким /users . К коллекции как таковой применимы два метода: GET (получение лимитированного списка сущностей) и POST (создание нового элемента). В запросах на получение списков мы можем использовать множество дополнительных GET параметров, применяемых для постраничного вывода, сортировки, фильтрации, поиска etc, но они должны быть опциональными, т.е. эти параметры не должны передаваться как часть пути!

Элементы коллекции

Для обращения к конкретному элементу коллекции мы используем в маршруте его уникальный идентификатор /users/25 . Это и есть уникальный путь к нему. Для работы с объектом применимы методы GET (получение объекта), PUT/PATCH (изменение) и DELETE (удаление).

Уникальные объекты

Во множестве сервисов существуют уникальные для текущего пользователя объекты, например профиль текущего пользователя /profile , или персональные настройки /settings . Разумеется, с одной стороны, это элементы одной из коллекций, но они являются отправной точкой в использовании нашего Web API клиентским приложением, и к тому же позволяют намного более широкий спектр операций над данными. При этом коллекция, хранящая пользовательские настройки может быть вообще недоступна из соображений безопасности и конфиденциальности данных.

Свойства объектов и коллекций

Для того, чтобы добраться до любого из свойств объекта напрямую, достаточно добавить к пути до объекта имя свойства, например получить имя пользователя /users/25/name . К свойству применимы методы GET (получение значения) и PUT/PATCH (изменение значения). Метод DELETE не применим, т.к. свойство является структурной частью объекта, как формализованной единицы данных.

В предыдущей части мы говорили о том, что у коллекций, как и у объектов, могут быть собственные свойства. На моей памяти мне пригодилось только свойство count, но ваше приложение может быть более сложным и специфичным. Пути к свойствам коллекций строятся по тому же принципу, что и к свойствам их элементов: /users/count . Для свойств коллекций применим только метод GET (получение свойства), т.к. коллекция – это только интерфейс для доступа к списку.

Коллекции связанных объектов

Одной из разновидностей свойств объектов могут быть связанные объекты или коллекции связанных объектов. Такие сущности, как правило, не являются собственным свойством объекта, а лишь отсылками к его связям с другими сущностями. Например, перечень ролей, которые были присвоены пользователю /users/25/roles . По поводу работы с вложенными объектами и коллекциями мы подробно поговорим в одной из следующих частей, а на данном этапе нам достаточно того, что мы имеем возможность обращаться к ним напрямую, как к любому другому свойству объекта.

Функции объектов и коллекций

Для построения пути к интерфейсу вызова функции у коллекции или объекта мы используем тот же самый подход, что и для обращения к свойству. Например, для объекта /users/25/sendPasswordReminder или коллекции /users/disableUnconfirmed. Для вызовов функций мы в любом случае используем метод POST. Почему? Напомню, что в классическом REST не существует специального глагола для вызова функций, а потому нам придется использовать один из существующих. На мой взгляд, для этого больше всего подходит метод POST т.к. он позволяет передавать на сервер необходимые аргументы, не является идемпотентным (возвращающим один и тот же результат при многократном обращении) и наиболее абстрактен по семантике.

Надеюсь, что все более-менее уложилось в систему 🙂 В следующей части мы поговорим подробнее о запросах и ответах, их форматах, кодах статусов.

Приближение третье: Запросы и ответы

В предыдущих приближениях я рассказал о том, как пришла идея собрать и обобщить имеющийся опыт разработки web API. В первой части я постарался описать, с какими видами ресурсов и операций над ними мы имеем дело при проектировании web API. Во второй части были затронуты вопросы построения уникальных URL для обращения к этим ресурсам. А в этом приближении я попробую описать возможные варианты запросов и ответов.

Универсальный ответ

Мы уже проговаривали, что конкретный формат общения сервера с клиентом может быть любым на усмотрение разработчика. Для меня наиболее удобным и наглядным кажется формат JSON, хотя в реальном приложении может быть реализована поддержка нескольких форматов. Сейчас же сосредоточимся на структуре и необходимых атрибутах объекта ответа. Да, все данные, возвращаемые сервером, мы будем оборачивать в специальный контейнер — универсальный объект ответа, который будет содержать всю необходимую сервисную информацию для его дальнейшей обработки. Итак, что это за информация:

Success — маркер успешности выполнения запроса

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

POST /api/v1/articles/22/publish
{
    "success": true
}

Error — сведения об ошибке

В случае, если выполнение запроса завершилось неудачей — о причинах и разновидностях отрицательных ответов сервера поговорим чуть позже, — к ответу добавляется атрибут «error», содержащий в себе HTTP-код статуса и текст сообщения об ошибке. Прошу не путать с сообщениями об ошибках валидации данных для конкретных полей. Правильнее всего, на мой взгляд, возвращать код статуса и в заголовке ответа, но я встречал и другой подход — в заголовке всегда возвращать статус 200 (успех), а детали и возможные данные об ошибках передавать в теле ответа.

GET /api/v1/user
{
    "success": false,
    "error": {
        "code" : 401,
        "message" : "Authorization failed"
    }
}

Data — данные, возвращаемые сервером

Большинство ответов сервера призваны возвращать данные. В зависимости от типа запроса и его успеха ожидаемый набор данных будет разным, тем не менее атрибут«data» будет присутствовать в подавляющем большинстве ответов.

Пример возвращаемых данных в случае успеха. В данном случае ответ содержит запрашиваемый объект user.

GET /api/v1/user
{
    "success": true,
    "data": {
        "id" : 125,
        "email" : "john.smith@somedomain.com",
        "name" : "John",
        "surname" : "Smith",
    }
}

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

PUT /api/v1/user
{
    "success": false,
    "error": {
        "code" : 422,
        "message" : "Validation failed"
    }
    "data": {
        "email" : "Email could not be blank.",
    }
}

Pagination — сведения, необходимые для организации постраничной навигации

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

Минимальный набор значений для пагинации состоит из:

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

Некоторые разработчики web API также включают в пагинацию набор готовых ссылок на соседние страницы, а также первую, последнюю и текущую.

GET /api/v1/articles
Response:
{
    "success": true,
    "data": [
        {
            "id" : 1,
            "title" : "Interesting thing",
        },
        {
            "id" : 2,
            "title" : "Boring text",
        }
    ],
    "pagination": {
        "totalRecords" : 2,
        "totalPages" : 1,
        "currentPage" : 1,
        "perPage" : 20,
        "maxPerPage" : 100,
    }
}

Работа над ошибками

Как уже упоминалось выше, не все запросы к web API завершаются успехом, но это тоже часть игры. Система информирования об ошибках является мощным инструментом, облегчающим работу клиента и направляющим клиентское приложение по правильному пути. Слово «ошибка» в этом контексте не совсем уместно. Здесь больше подойдёт слово исключение, так как на самом деле запрос успешно получен, проанализирован, и на него возвращается адекватный ответ, объясняющий, почему запрос не может быть выполнен.

Каковы же потенциальные причины получаемых исключений?

500 Internal server error — всё сломалось, но мы скоро починим

Это как раз тот случай, когда проблема произошла на стороне самого сервера, и клиентскому приложению остаётся только вздохнуть и уведомить пользователя о том, что сервер устал и прилёг отдохнуть. Например, утеряно соединение с базой данных или в коде завелся баг.

400 Bad request — а теперь у вас всё сломалось

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

401 Unauthorized — незнакомец, назови себя

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

403 Forbidden — вам сюда нельзя

Запрашиваемый ресурс существует, но у пользователя недостаточно прав на его просмотр или модификацию.

404 Not found — по этому адресу никто не живёт

Такой ответ возвращается, как правило, в трёх случаях: путь к ресурсу неверен (ошибочен), запрашиваемый ресурс был удалён и перестал существовать, права текущего пользователя не позволяют ему знать о существовании запрашиваемого ресурса. Например, пока просматривали список товаров, один из них внезапно вышел из моды и был удалён.

405 Method not allowed — нельзя такое делать

Эта разновидность исключения напрямую связана с использованным при запросе глаголом (GET, PUT, POST, DELETE), который, в свою очередь, свидетельствует о действии, которое мы пытаемся совершить с ресурсом. Если запрошенный ресурс не поддерживает указанное действие, сервер говорит об этом прямо.

422 Unprocessable entity — исправьте и пришлите снова

Одно из самых полезных исключений. Возвращается каждый раз, когда в данных запроса существуют логические ошибки. Под данными запроса мы подразумеваем либо набор параметров и соответствующих им значений, переданных методом GET, либо поля объекта, передаваемого в теле запроса методами POST, PUT и DELETE. Если данные не прошли валидацию, сервер в секции «data» возвращает отчет о том, какие именно параметры невалидны и почему.

Протокол HTTP поддерживает намного большее число различных статус-кодов на все случаи жизни, но на практике они используются редко и в контексте web API не несут практической пользы. На моей памяти мне не приходилось выходить за пределы вышеперечисленного списка исключений.

Запросы

Получение элементов коллекции

Одним из наиболее частотных запросов является запрос на получение элементов коллекции. Информационные ленты, списки товаров, различные информационные и статистические таблицы и многое другое клиентское приложение отображает посредством обращения к коллекционным ресурсам. Для осуществления этого запроса мы обращаемся к коллекции, используя метод GET и передавая в строке запроса дополнительные параметры. Как мы уже обозначили выше, в качестве ответа мы ожидаем получить массив однородных элементов коллекции и информацию, необходимую для пагинации — подгрузки продолжения списка или же конкретной его страницы. Содержимое выборки может быть особым способом ограничено и отсортировано с помощью передачи дополнительных параметров. О них и пойдёт речь далее.

Постраничная навигация

page — параметр указывает на то, какая страница должна быть отображена. Если этот параметр не передан, то отображается первая страница. Из первого же успешного ответа сервера будет ясно, сколько страниц имеет коллекция при текущих параметрах фильтрации. Если значение превышает максимальное число страниц, то разумнее всего вернуть ошибку 404 Not found.

GET /api/v1/news?page=1

perPage — указывает на желаемое число элементов на странице. Как правило, API имеет собственное значение по умолчанию, которое возвращает в качестве поля perPage в секции pagination, но в ряде случаев позволяет увеличивать это значение до разумных пределов, предоставив максимальное значение maxPerPage:

GET /api/v1/news?perPage=100

Сортировка результатов

Зачастую результаты выборки требуется упорядочить по возрастанию или убыванию значений определенных полей, которые поддерживают сравнительную (для числовых полей) или алфавитную (для строковых полей) сортировку. Например, нам нужно упорядочить список пользователей по имени или товары по цене. Помимо этого мы можем задать направление сортировки от A до Я или в обратном направлении, причём разное для разных полей.

sortBy — существует несколько подходов к передаче данных о сложной сортировке в GET параметрах. Здесь необходимо четко указать порядок сортировки и направление.

В некоторых API это предлагается сделать в виде строки:

GET /api/v1/products?sortBy=name.desc,price.asc

В других вариантах предлагается использовать массив:

GET /api/v1/products?
sortBy[0][field]=name&
sortBy[0][direction]=desc&
sortBy[1][field]=price&
sortBy[1][direction]=asc

В целом оба варианта равносильны, так как передают одни и те же инструкции. На мой взгляд, вариант с массивом более универсален, но тут, как говорится, на вкус и цвет…

Простая фильтрация по значению

Для того, чтобы отфильтровать выборку по значению какого либо поля, в большинстве случаев достаточно передать в качестве фильтрующего параметра имя поля и требуемое значение. Например, мы хотим отфильтровать статьи по ID автора:

GET /api/v1/articles?authorId=25

Усложнённые варианты фильтрации

Многие интерфейсы требуют более сложной системы фильтрации и поиска. Перечислю основные и наиболее часто встречаемые варианты фильтрации.

Фильтрация по верхней и нижней границе с использованием операторов сравнения from (больше или равно), higher (больше), to (меньше или равно), lower (меньше). Применяется к полям, значения которых поддаются ранжированию.

GET /api/v1/products?price[from]=500&price[to]=1000

Фильтрация по нескольким возможным значениям из списка. Применяется к полям, набор возможных значений которых ограничен, например, фильтр по нескольким статусам:

GET /api/v1/products?status[]=1&status[]=2

Фильтрация по частичному совпадению строки. Применяется к полям, содержащим текстовые данные или данные, которые могут быть приравнены к текстовым, например, числовые артикулы товаров, номера телефонов и т. д.

GET /api/v1/users?name[like]=John
GET /api/v1/products?code[like]=123

Именованные фильтры

В некоторых случаях, когда определенные наборы фильтрационных параметров часто употребимы и подразумеваются системой как нечто целостное, особенно если затрагивают внутреннюю, зачастую сложную механику формирования выборки, целесообразно сгруппировать их в так называемые именованные фильтры. Достаточно передать в запросе имя фильтра, и система построит выборку автоматически.

GET /api/v1/products?filters[]=recommended

Именованные фильтры могут также иметь свои параметры.

GET /api/v1/products?filters[recommended]=kidds

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


За перевод материала выражаем благодарность международной IT-компании Noveo.

В недавней статье мы описывали общие возможности нашего движка внешних API.

В этой статье мы расскажем, как с нуля создать метод внешнего JSON API на платформе ReadyScript. В качестве потребителя данного API может выступать любое приложение, будь то мобильное или Desktop приложение или любой внешний сервис.

В рамках данной статьи мы рассмотрим какие базовые абстрактные методы API нам предлагает ReadyScript и создадим на базе двух из них свои собственные методы.

Задача

Задание, которое мы сегодня будем выполнять звучит так: «Разработать 2 метода внешних API для получения списка брендов, а также получения одного бренда по ID. Получение бренда по ID должно быть доступно только для авторизованных пользователей».

Мы специально упростили задачу до работы с существующими сущностями (бренды уже есть в системе, есть все необходимые контролы для работы с ними из админ.панели), чтобы сосредоточиться только на том, что связано с разработкой API.  В случае, если вы желаете познакомиться с созданием собственных сущностей, рекомендуем прочитать другую нашу статью на эту тему.

Установка ReadyScript

Итак, для начала нам будет необходимо установить локально платформу ReadyScript, например на домен backend.local (Мы будем ссылаться на данный домен далее по тексту). Если вы ведете разработку на Windows, то сперва необходимо установить LAMP-окружение. Наиболее удобным для этого является программа OpenServer. Обратите внимание, что для ReadyScript требуется PHP 7.1+ и Mysql с выключенным Strict Mode, это означает, что в my.cnf файле должно быть установлено пустое значение для параметра sql_mode (sql_mode = “”).

Для установки ReadyScript достаточно скачать файл rs.php и запустить его из браузера на вашем локальном домене (backend.local/rs.php), далее следуйте инструкциям, отображаемым установщиком. Вы можете установить любую редакцию ReadyScript, функция внешних API есть во всех редакциях.

Начало разработки. План действий

  1. Всю разработку мы будем вести в отдельном модуле, соответственно первым делом мы создадим простейший модуль MyApi по инструкции.
  2. Далее мы создадим и зарегистрируем класс приложения, которое будет работать с нашим API, так как нам нужны будут функции требующие авторизации.
  3. Далее мы приступим к разработке непосредственно методов API.

Подготовка к разработке

Для комфортной разработки, произведем следующие действия в административной панели развернутой копии ReadyScript:

  1. В разделе Управление -> Настройка системы установим опции:
    • Подробно отображать информацию об исключениях = Да
    • Всегда проверять шаблоны на предмет модификации = Да
    • Включить кэширование данных = Нет
  2. В разделе Веб-сайт -> Настройка модулей -> Внешнее API установим:
    • Разрешить работу API только на следующем домене = пустое поле
    • API ключ = пустое поле
    • Включить возможность видеть справку по внешнему API по ссылке /api/help = Да
    • Отображать детальную информацию по внутренним ошибкам при вызове API = Да
    • Включить логирование запросов = Да
  3. В разделе Товары -> Бренды заведем несколько брендов, с которыми далее мы будем работать.

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

Создаем модуль MyApi

В папке /modules нашего сайта создаем следующую структуру папок:

  • myapi //Корневая папка модуля
    • config //Папка для конфигурационных классов
    • model //Папка для моделей данных
      • apptypes //Папка для классов внешних приложений 
      • externalapi //Папка для методов внешних API

Чтобы модуль корректно был идентифицирован системой, в папке myapi/config необходимо создать файл file.inc.php, содержащий базовые сведения о модуле и его настройки.

Так как у нас не будет никаких настроек модуля, файл должен содержать пустую декларацию класса, потомка RSOrmConfigObject.

Создадим рядом с файлом file.inc.php манифест (файл описания параметров) модуля module.xml:

Теперь у нас есть минимальный модуль, который определяется системой и мы можем перейти в административную панель в раздел Веб-сайт -> Настройка модулей -> Мой API и нажать установить модуль.

Создаем и регистрируем класс внешнего приложения

С методами API, требующими авторизации, могут работать только заранее определенные и зарегистрированные в системе приложения. Приложение должно быть оформлено специальным классом, наследником RSRemoteAppAbstractAppType.

Опишем класс с именем MyCustomApp в файле modules/myapi/model/apptypes/mycustomapp.inc.php

Как видно из кода выше, класс содержит сведения о названии приложения(его нужно придумать), идентификаторе(нужно придумать), методах API, с которыми будет работать приложение, а также о правах с которыми будут вызываться данные методы. В качестве права может выступать любая константа, так как проверять эти права будете далее вы в своем же коде метода API. Также в классе описаны группы, в которых должен состоять пользователь, чтобы пройти авторизацию.

В случае, если бы наше приложение отправляло push-уведомления, необходимо было бы имплементировать интерфейс PushSenderModelAppInterfaceHasPush и описать все пуши, которые данное приложение отправляет, но в нашем случае это не требуется.

Теперь нам необходимо зарегистрировать в системе наше приложение. Для этого создаем обработчик события getapps в файле /config/handlers.inc.php

Приступаем к разработке методов API

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

Подробно базовые классы методов API описаны у нас в документации. Использование готовых базовых классов в реальных проектах мы конечно же рекомендуем, но чтобы понимать как ими пользоваться нужно знать детально как все работает, поэтому в рамках данной статьи мы будем с нуля писать методы API, наследуясь от низкоуровневых абстракций ExternalApiModelAbstractMethodsAbstractMethod и ExternalApiModelAbstractMethodsAbstractAuthorizedMethod.

Метод mybrand.getList

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

На вход в наш метод, мы будем принимать два параметра: номер страницы и количество элементов на страницу. Итак, приступим к описанию кода, из которого многое будет ясно.

Обратите внимание, что мы могли бы всю выборку объектов описать в методе process, но вместо этого мы разбили ее на несколько небольших методов getDaoObject, setFilter, setOrder, getResultCount, getResultList. Такой подход позволяет в будущем повторно использовать код, используя данный класс в качестве базового. Например, если нужно создать аналогичное API для выборки других объектов, достаточно сделать класс-потомок от MyApiModelExternalApiMyBrandGetList и переопределить в нем только один метод getDaoObject. Именно такого подхода, мы придерживаемся при разработке наших базовых абстрактных классов для различных типов внешнего API.

Проверяем, наш новый метод должен появиться на странице помощи по внешним API. Для этого открываем страницу backend.local/api/help/myBrand.getList и видим следующее.

mybrand_getlist

Использовать метод можно по ссылке backend.local/api/methods/myBrand.getList

Метод mybrand.get

Данный метод должен возвращать нам полную информацию об одном объекте бренда по его ID. Доступ к нему есть только у авторизованных пользователей из группы Администраторы.

В качестве базового класса, мы будем использовать ExternalApiModelAbstractMethodsAbstractAuthorizedMethod. Данный класс обеспечивает необходимые механизмы для проверки авторизационного токена и проверку прав.

При обращении к backend.local/api/help/myBrand.get мы теперь можем увидеть документацию по нашему методу API.

Это возможно благодаря тому, что ReadyScript самостоятельно обходит все файлы, чья маска совпадает с паттерном /modules/*/model/externalapi/*/*.inc.php, находит в таких файлах классы, связанные с внешними методами API и подключает их.

Остается теперь вопрос, как проверять такой метод, ведь он требует Авторизационный токен, который можно получить выполнив POST запрос на backend.local/api/methods/oauth.token с соответствующими данному методу параметрами.

Авторизационный токен также можно получить и с помощью двух кликов через административную панель. Для этого необходимо перейти в раздел Веб-сайт -> Настройка модулей -> Внешнее API -> Авторизационные токены (справа) -> Кнопка Добавить (вверху).

Выберите в открывшемся диалоговом окне ваше приложение “Наше тестовое мобильное приложение”, скопируйте Авторизационный токен в буфер обмена и нажмите на кнопку Сохранить и закрыть.

add-token

Далее вы можете использовать данный токен в запросах к API, требующим авторизацию.

Использовать метод можно по ссылке backend.local/api/methods/myBrand.get?token=ВАШ_ТОКЕН&id=1

Заключение

В отличие от REST подхода, JSON API в таком виде имеет бóльшую гибкость, так как он динамически расширяем и не имеет жесткой привязки к отношениям между объектами, которая обычно в REST жестко закладывается в URL методов.

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

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

Представленный в данной статье код модуля, можно скачать здесь.


28 февраля 2021 19:45, Артем Полторанин

API (App Programming Interface) — это интерфейсная программа, которая помогает взаимодействовать с другим программным обеспечением подобно пользовательскому интерфейсу (UI). Он является основной точкой входа для веб-сайта и приложения, включая интеграцию со сторонними разработчиками, облегчая жизнь разработчикам. Эта статья предназначена для тех, кто собирается использовать API в своих проектах. Давайте посмотрим, что такое API?

API предоставляют приложениям возможность взаимодействовать друг с другом. Например, одно приложение может запросить данные у другого приложения и получить их в ответ. Компонент API (Application Program Interface) может разрешать функции одного приложения, а второе приложение может их использовать. Консорциум Всемирной паутины (W3C) определяет API как «набор определений подпрограмм, протоколов и сервисов для создания программных приложений».

Как научиться интеграции API?

API используются в самых разных приложениях, от небольших проектов, например, школьных, до масштабных глобальных сервисов, таких как Google Maps или Facebook. Например, знаменитая кнопка Tweet в Twitter — это API-сервис, который исполняемый код может вызвать и отправить твит от имени пользователя (хотя в последних версиях это изменилось, и большая часть функциональности стала частью страницы).

Tweet button

Всем интересно узнать об особенностях API и их функциональных возможностях. Для лучшего понимания необходимо изучить API с самых основ. Документация по интерфейсу программирования приложения всегда лучше, но если все, что у вас есть, — это веб-сайт W3C API, вы все равно можете многое узнать — и быстро!

W3C имеет точку входа в API, где вы можете найти ссылки на инструменты, ресурсы, примеры и многие другие ценные вещи. Если вы работаете под Windows или Linux, у W3C есть онлайн-инструмент для документирования служб API, который позволяет тестировать API. Есть также инструменты на базе HTML5 и приложения, удобные для мобильных устройств. Помимо W3C, несколько хороших ресурсов есть у Google:

Google предоставляет множество API для доступа к информации из своих служб. Одним из таких API является API Google Maps. Этот инструмент — отличный способ узнать об API, поскольку в нем используются HTML и JavaScript, которые должны быть знакомы каждому, кто разрабатывает веб-приложение для бизнеса.

Различные этапы изучения интеграции API включают в себя:

  • Понять, что делают API
  • Разберитесь в различных типах API
  • Узнайте об API W3C и Google API
  • Определите доступные сервисы, к которым можно получить доступ с помощью API
  • Интегрировать API для создания рабочей системы или программы с вашим кодом и протестировать API
  • Изучить и понять инструменты, язык сценариев, языки программирования, фреймворки и стиль проектирования для интеграции вашей системы с существующей посредством интеграции API
  • Попробуйте реализовать коллекции, необходимые для взаимодействия с сервисом, реализованным через API, и научитесь тестировать коллекции API в своем коде, например, объектную модель JSON или модель A<name>Object и т.д.

Как создать API?

Существует несколько способов создания API. Один из самых простых — использовать онлайн-сервис. Многие онлайн-сервисы позволяют разрабатывать и тестировать интерфейс программирования приложений бесплатно или за небольшую плату. Одним из таких примеров является no-code AppMaster, отличная и очень надежная платформа для создания и реализации API. Спецификация является неотъемлемой частью создания API при его проектировании и документировании. Спецификация расскажет другим разработчикам, как использовать ваш API и что им нужно сделать, чтобы взаимодействовать с ним.

Что мне нужно для начала работы?

Для начала работы с тестовым API вам понадобится несколько предметов:

  • Доменное имя
  • Место для размещения кода, например, GitHub или SourceForge
  • HTTP-сервер, чтобы вы могли запускать свой код локально

На каком языке должен быть написан мой API?

Не существует правил для создания ключа API. Это зависит от ваших потребностей, но некоторые распространенные варианты включают:

  • PHP
  • Java
  • Ruby
  • Net

В чем преимущество создания API?

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

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

API эндпоинты

API эндпоинты — это методы которые представляют собой некий шлюз, который соединяет серверные приложения со встроенным интерфейсом. Простыми словами, это адрес, на который отправляются сообщения. Эти методы могут включать JSON, XML и другие. Каждый URL должен иметь метод, который запрашивается, например GET или POST.

endpoints

Использование API эндпоинтов

API эндпоинты- это цифровое место, где API получает запрос ресурса. Таким образом, конечная точка — это, по сути, URL (Uniform Resource Locator), который предоставляет информацию о ресурсе. Ниже перечислены некоторые важные моменты, касающиеся характеристик API эндпоинтов.

  • Определите и используйте имя URL эндпоинта
  • Определите и используйте метод HTTP
  • Определите тело запроса и его параметры
  • Определите стратегию аутентификации, если необходимо
  • Для удобства к каждому эндпоинту прикрепляется дополнительный параметр. Точно так же, как при создании функции в других языках программирования, вы должны включить эндпоинты или эти эндпоинты в свой исходный код, поэтому обязательно объявите свои эндпоинты или эти эндпоинты в заголовочном файле в верхней части вашего исходного кода.

Что такое API эндпоинт?

  • Эндпоинт — это компонент API
  • Эндпоинт — это местоположение ресурса
  • API извлекает ресурсы с помощью URL эндпоинта
  • Эндпоинт является одним из концов канала связи

Что такое API для начинающих?

API для начинающих — это набор методов и функций, которые приложение или программа использует для взаимодействия с другим приложением или программой. В компьютерной науке они также известны как «методы».

Пользователи могут использовать API для получения информации от других программ, получения данных и многого другого. Например, в мире iPhone вы можете использовать API, доступный в вашем приложении, для получения данных от другого приложения, например, созданного Facebook, Twitter и т.д., через App Store компании Apple.

Ниже перечислены некоторые из различных типов API:

API на основе XML

Они также известны как API веб-служб и REST API (передача репрезентативного состояния). Единственное различие между API на основе XML и API веб-служб заключается в их синтаксисе. API на основе XML поддерживают все основные веб-браузеры, включая Internet Explorer, Safari, Chrome и Firefox на Windows, OSX и Linux; Internet Explorer 9+ на Windows Phone и другие веб-браузеры. — RESTful API: Это разновидность современного API. Он использует меньшую пропускную способность, чем другие форматы, такие как SOAP. NET.

SOAP APIs

SOAP API

Это более старые типы API для тестирования. Он использует XML, но имеет свой синтаксис. Веб-сервисы загружаются и скачиваются с помощью протокола HTTP. HTTP — один из наиболее часто используемых протоколов в большинстве подключенных к Интернету устройств, от компьютеров до смартфонов.

  • Удаленные вызовы процедур (RPC)
  • Удаленные вызовы процедур используют SOAP поверх HTTP для связи между клиентом и сервером в API-ключе. Это относительно новый способ создания API. Он включает удаленные конечные точки для клиента, чтобы отправлять и получать данные и передавать команды.
  • Обобщенный язык объектного моделирования (GOML): Это более новый формат для создания API, который не сохраняет детали от предыдущих запросов. Вместо этого метод называется функцией в других языках программирования. Единственный недостаток — в нем нет системы событий, но Apple использовала его для создания «swoosh» в iPhone и службы приложений Camera.

Интеграция с API

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

Прежде чем начать интегрировать API-ключ в свой проект, необходимо сделать две вещи:

  1. Узнайте, как использовать и создавать необходимые модели данных.
  2. Узнайте, как работать со всеми различными типами данных.

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

При проектировании вашей системы необходимо продумать, какая информация требуется и какие отношения существуют между ними.

Ниже приведены несколько примеров:

  • Создайте онлайн-систему, которая отслеживает содержимое веб-сайта.
  • На сайте есть несколько статей с названиями и авторами (набор данных).
  • На сайте есть миниатюры для каждой статьи (набор данных).
  • Пользователь выбирает статью на главной странице (команда).

Команда проходит через вашу программу и запрашивает конкретную статью из базы данных.

Легко ли освоить API?

Если вы решили создать приложение, используя в качестве отправной точки API (ключ прикладного программного интерфейса), то да! Каждый день появляются новые приложения. API эндпоинты разрабатываются и создаются с возрастающей сложностью, что делает их простыми в использовании и понимании. Благодаря количеству API на рынке разработчикам легко найти то, что им нужно.

Кроме того, когда вы научитесь использовать API (интерфейс прикладных программ), создание приложения с использованием той же технологии будет очень важным. Разработчик платформы no-code AppMaster является примером такого сервиса для быстрого создания вашего API. API эндпоинты очень важны при изучении API (прикладного программного интерфейса). API эндпоинты многочисленны и разнообразны.

Конечные точки API (эндпоинты) — это важные и ценные методы при интеграции API в проект. При разработке ключа API необходимо продумать различные методы, такие как создание, чтение, обновление и удаление (CRUD), а также параметры. Вы должны описать все эти параметры до начала работы над проектом. Ниже перечислены различные методы, которые вы можете использовать для взаимодействия с конечными точками API (эндпоинтами):

GET

Этот метод используется для получения данных из URL. Он также используется для получения полного содержимого HTML-страницы в API. Например, если вы хотите получить всю информацию об определенном пользователе в API, вы запросите его профиль на сервере. Затем сервер отправит ответ.

POST

Если вы хотите отправить данные на сервер и сохранить их, то этот метод — то, что вам нужно. Он используется для создания новой записи в вашем API. Для этого укажите тип запроса в вашем API (например, запись в блоге).

PUT

Этот метод используется для обновления данных в базе данных. В API, если вы хотите изменить информацию в определенной базе данных, вы будете использовать этот метод. Сервер отправит уведомление об успешном обновлении данных.

DELETE

Этот метод удаляет запись из вашей базы данных. Чтобы сделать этот запрос, вам нужно вызвать соответствующий URL в вашей API-программе. Чтобы понять, как эти методы выполняются браузером, необходимо изучить API и конечные точки API (эндпоинты).

Могу ли я создать свой API?

Да, вы можете создать свой API из широкого выбора доступных API, и создать новый очень просто. Вам нужно загрузить код на GitHub или скачать кроссплатформенный SDK и начать разработку API. Также для создания своего API можно воспользоваться помощью известной платформы AppMaster.

API no-code

Создание пользовательского типа данных или фильтра также возможно, но не обязательно. Если вы хотите работать с уникальным типом данных или функциональностью фильтра, то вам нужен Identity Provider, который будет предоставлять эту функциональность для ваших клиентов при тестировании API (Application Program Interface).

Обучение отнимает много времени, и без соответствующих инструкций или объяснений потребуется время, чтобы понять предмет. Это точно применимо к разработке API; следовательно, лучше проверить, как другие разработчики разработали свои API. Как только вы ознакомитесь с предметом, то с помощью AppMaster вы сможете приступить к созданию своего приложения прикладного программного интерфейса (API) для вашего проекта. Если вы хотите использовать существующий ключ API, то действуйте. Но если вы хотите создать свой собственный и привести его в соответствие со временем, попробуйте использовать AppMaster для разработки приложения для вашего бизнеса.

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

Существует множество преимуществ, которые мы можем получить от разработки API-ключа. Начнем с того, что ваше приложение станет более привлекательным для ваших пользователей, поскольку оно сможет предоставлять информацию о ресурсах. Кроме того, вы сможете лучше управлять своими данными, потому что приложение может легко получить к ним доступ в любом месте. Вопросы безопасности, связанные с тестированием API (Application Program Interface), также решаются благодаря отсутствию необходимости передавать конфиденциальные данные по сети. Это гарантирует, что никто не получит доступ к тестовому API (Application Program Interface), что впоследствии может привести к проблемам.

Заключительные мысли

API могут быть очень полезны для вашего бизнеса, поскольку они отправляют и получают данные из облака и выступают в качестве ключа. Тестировать API (Application Program Interface) очень удобно, поэтому вы можете с легкостью использовать их для создания приложения. Кроме того, нет необходимости в том, чтобы ваш сервис мобильных приложений соединялся с вашим бэк-эндом, поскольку API (Application Program Interface) сделает это за вас. Если вы хотите создать приложение, собирающее данные, начните изучать возможности разработки и тестирования API (Application Program Interface), предлагаемые AppMaster. AppMaster — это платформа, призванная помочь компаниям создать свой API за несколько минут без какого-либо опыта. Цель платформы — упростить процесс создания и тестирования API в кратчайшие сроки.

Платформа предоставляет все ключевые функциональные возможности для тестирования API и разработки исполнения API. После создания API (прикладного программного интерфейса) вы можете использовать его в качестве отправной точки для создания своих приложений. После этого вы сможете продвигать свои услуги и получать больше клиентов. Посетите AppMaster для получения дополнительной информации о no-code приложениях и разработке API.

Russian (Pусский) translation by AlexBioJS (you can also view the original English article)

Разбираемся с REST и RESTful API

Если вы уже имели дело с современной веб-разработкой, то вам уже встречались термины вроде REST и API. Если вы слышали эти термины или работали с API, но еще не до конца поняли принцип их работы или как создать свой собственный API, то эта серия руководств для вас.

В этой серии мы начнем с обзора принципов и понятий REST. Затем мы приступим к созданию нашего собственного полностью работоспособного API, который работает на основе сервера, созданного при помощи Express и Node.js, и подключается к базе данных MySQL. После изучения руководств этой серии вы должны будете чувствовать себя уверенно при создании вашего собственного API или при работе с документацией уже существующего.

Предварительная подготовка

Для того чтобы получить от этого руководства максимум пользы, вы уже должны иметь базовые навыки работы с командной строкой, обладать базовыми знаниями по JavaScript и иметь на своем компьютере установленную глобально Node.js.

Что из себя представляют REST и RESTful API?

При помощи Representational State Transfer (* передача репрезентативного состояния), или REST, описывается архитектурный стиль для веб-сервисов. REST состоит из ряда стандартов или ограничивающих требований по обмену данными между различными системами, и для систем, которые работают согласно принципам REST, используется термин RESTful. REST – абстрактная концепция, это не язык, не фреймворк или тип программного обеспечения.

Для лучшего понимания REST вам могла бы помочь аналогия, когда сравнивается коллекция виниловых пластинок с сервисом для потоковой передачи аудио-данных (* способ воспроизведения аудио- и видеоматериалов из интернета без их предварительного скачивания в компьютер). В случае с виниловыми пластинками для их распространения необходимо, чтобы для каждой записи была создана полная копия. В случае же с сервисом для потоковой передачи аудио-данных та же самая музыка может распространяться неограниченный срок, если известны какие-либо данные, например название песни. В этом случае сервис для потоковой передачи аудио-данных является RESTful сервисом, а коллекция виниловых пластинок – нет.

API (Application Programming Interface) – интерфейс, который позволяет программам общаться друг с другом. RESTful API – просто API, который работает согласно принципам и понятиям REST. В случае с API, работающим в сети, сервер получает запрос при помощи конечной точки, где указан URL-адрес, и отправляет обратно ответ, которым часто являются данные в формате вроде JSON.

Принципы REST

К архитектуре REST выдвигаются шесть основных требований, перечисленных ниже:

  1. Унифицированный интерфейс: интерфейс компонентов должен быть одинаковым. Это достигается за счет использования стандарта URI для идентификации ресурсов – другими словами, путей, которые могли бы быть набраны в адресной строке браузера.
  2. Клиент-Сервер: сервер и клиент выполняют различные задачи: сервер хранит данные и выполняет с ними манипуляции, а клиент отправляет запросы и отображает ответы.
  3. Взаимодействия без запоминания состояния: вся информация о каждом запросе содержится в нем самом и не зависит от состояния сессии.
  4. Возможность использования кэш-памяти: клиент и сервер могут кэшировать ресурсы.
  5. Многоуровневая система: клиент может подключаться к конечному серверу или промежуточному слою вроде компенсатора (* инструмент для эффективного распределения приходящего к группе серверов траффика).
  6. Получение кода по запросу (Не обязательно): клиент может скачать код, за счет чего снижается его видимость в сети.

Запрос и ответ

Вы в курсе, что для всех веб-сайтов используется URL, который начинается с http (или https в случае использования безопасной версии протокола). HyperText Transfer Protocol, или HTTP, – метод коммуникации  клиентов с серверами в Интернете.

Нам наиболее очевидно его существование, когда мы вводим  в адресных строках наших браузеров URL-адреса, однако HTTP может использоваться не только для запрашивания веб-сайтов с серверов. Когда вы переходите по адресу URL, то, собственно, выполняете запрос по методу GET для получения определенного ресурса, и веб-сайт, который вы видите, является телом ответа. Мы скоро рассмотрим запросы, выполняющиеся по методу GET и другим.

HTTP работает путем открытия соединения TCP (Transmission Control Protocol – протокол управления передачей) к порту сервера (80 для http, 443 для https) для выполнения запроса, а прослушивающий запросы сервер отправляет ответы, в которых содержатся код ответа и тело.

Ответ должен состоять из URL, метода, информации заголовка и тела.

Методы запроса

Имеется четыре главных метода HTTP, известных также как глаголы HTTP, которые часто используются для взаимодействия с API в сети. За счет этих методов определяется действие, которое будет выполнено над любым данным ресурсом.

Методы запроса HTTP приблизительно соответствуют парадигме CRUD, что расшифровывается как Create, Update, Read, Delete (* создать, прочесть, обновить, удалить). Хотя CRUD относится к функциям, используемым при выполнении операций над базой данных, мы можем применить те основы конструирования к глаголам HTTP в RESTful API.

Действие Метод запроса Определение
Прочитать GET При помощи него выполняется получение ресурса
Создать POST При помощи него выполняется создание нового ресурса
Обновить PUT При помощи него выполняется обновление существующего ресурса
Удалить DELETE При помощи него выполняется удаление ресурса

GET – безопасная операция, которая используется только для считывания данных и при выполнении которой состояние сервера не изменится. Каждый раз, когда вы вводите в вашем браузере URL-адрес, например https://www.google.com, вы отправляете запрос по методу GET к серверам Google.

POST используется для создания нового ресурса. Знакомым для вас примером использования POST должна послужить регистрация пользователя на веб-сайте или веб-приложении. После отправления формы на сервер мог бы быть отправлен запрос по методу POST с данными пользователя, где эта информация далее была бы добавлена в базу данных.

При помощи метода PUT существующий ресурс обновляется, что могло бы быть использовано для редактирования настроек существующего пользователя. В отличие от POST PUTидемпотентный метод. Это означает, что при повторном выполнении запроса будет получаться тот же самый результат. Например если бы вы отправили тот же самый запрос по методу POST для создания нового пользователя в базе данных множество раз, то в результате был бы создан новый пользователь с теми же данными. Однако в результате выполнения того же запроса по методу PUT для того же самого пользователя неизменно выходил бы одинаковый результат.

При помощи запроса по методу DELETE, как должно быть понятно из его названия, будет просто удален существующий ресурс.

Коды ответа

Как только запрос поступит на сервер, тот отправит ответ HTTP, в котором будут содержаться метаданные об ответе (заголовки) и тело. Первая и наиболее важная часть ответа – код состояния, который указывает на то, был ли запрос выполнен успешно, была ли совершена ошибка при его обработке или же на то, что должно быть выполнено иное действие.

Наиболее известным кодом ответа для вас должен быть 404, что означает Not Found (* сообщает, что запрашиваемый ресурс не существует на сервере). 404 входит в состав класса 4xx кодов состояния, при помощи которых указывается, что при обработке запроса произошла ошибка. Имеется пять классов кодов состояния, в каждом из которых имеется ряд кодов.

  • 1xx: Information (* Информация о процессе передачи)
  • 2xx: Success (* Информация об успешном принятии и обработке запроса клиента)
  • 3xx: Redirection (* Перенаправление)
  • 4xx: Client Error (* Информация об ошибках со стороны клиента)
  • 5xx: Server Error (* Информация об ошибках со стороны сервера)

Другими распространенными кодами ответов, с которыми вы, скорее всего, знакомы, являются 301 Moved Permanently, который используется для перенаправления браузера по другому URL-адресу, и 500 Internal Server Error, что указывает на то, что при обработке запроса произошла непредвиденная ошибка на стороне сервера, из-за чего текущий запрос не может быть выполнен.

Что касается API RESTful и их соответственных глаголов HTTP, то коды всех ответов должны находиться в диапазоне 2xx.

Запрос Ответ
GET 200 (OK)
POST 201 (Created) (* Создано)
PUT 200 (OK)
DELETE 200 (OK), 202 (Accepted) (* Принято) или 204 (No Content) (* Нет содержимого; тело сообщения не передается)

200 OK – код ответа, который указывает на то, что запрос был выполнен успешно. Он используется в качестве кода ответа при отправлении запроса по методу GET или PUT. При выполнении запросов по методу POST будет возвращен ответ с кодом состояния 201 Created для указания того, что новый ресурс был создан, и при выполнении запросов по методу DELETE возможны несколько вариантов кодов ответа, благодаря которым подтверждается, что либо запрос был успешно принят (202), либо отправлять нечего, поскольку ресурс более не существует (204).

Мы можем протестировать код состояния ответа на запрос ресурса при помощи cURL – инструмента командной строки, который используется для передачи данных при помощи URL-адресов. При помощи команды curl, за которой следует флажок  -i или  --include будет отправлен запрос по методу GET по указанному URL-адресу и отображены заголовки и тело. Мы можем протестировать это, если откроем консоль и отправим запросы к Google.

1
curl -i https://www.google.com

Сервера Google пришлют ответ со следующей информацией:

1
HTTP/2 200 
2
date: Tue, 14 Aug 2018 05:15:40 GMT 
3
expires: -1 
4
cache-control: private, max-age=0 
5
content-type: text/html; charset=ISO-8859-1 
6
...

Как видно, в ответ на запрос curl было отправлено множество заголовков и все тело HTML-ответа. Поскольку запрос был выполнен успешно, то первой частью ответа является код состояния 200 вместе с версией HTTP (ею может быть либо HTTP/1.1, либо HTTP/2).

Поскольку в ответ на этот конкретный запрос в ответе приходит веб-сайт, то в качестве значения заголовка content-type (тип MIME (* Multipurpose Internet Mail Extensions – многоцелевые расширения почты (почтовой службы)) указывается text/html. В RESTful API вы, скорее всего, увидите вместо этого application/json, что указывает на то, что форматом ответа является JSON.

Интересно то, что мы можем увидеть иной тип ответа, слегка изменив вводимый URL-адрес. Выполните команду curl для отправления запроса к Google без www.

1
curl -i https://google.com
1
HTTP/2 301 
2
location: https://www.google.com/ 
3
content-type: text/html; 
4
charset=UTF-8

Google перенаправит клиент на www.google.com, и в ответе будет прислан код состояния 301 для указания того, что должно быть выполнено перенаправление.

Конечные точки RESTful API

Когда API располагается на сервере, то предоставляемые им данные доступны при помощи конечных точек. Конечная точка – отдельная функция API, которая запускается при обращении к серверу по определенному URL-адресу и может принимать и обрабатывать запросы по методу GET, POST, PUT или DELETE.

URL API будет состоять из корня, пути и необязательной строки запроса.

  • Корень, например https://api.example.com или  https://api.example.com/v2, – корень URL, который может состоять из протокола, домена и версии протокола.
  • Путь, например /users/ или /users/5, – уникальное местоположение определенного ресурса.
  • Параметры запроса (указывать необязательно), например ?location=chicago&age=29, – необязательные пары ключ=значение, которые используются для сортировки, фильтрации и разбиения контента на страницы.
    Мы можем собрать эти компоненты воедино для получения нечто подобного тому, что показано в примере ниже, благодаря чему был бы возвращен список всех пользователей и использован параметр запроса limit для того, чтобы в результате фильтрации в ответ были включены только 10 результатов.

https://api.example.com/users?limit=10

Обычно, когда люди ссылаются на API как на RESTful API, они имеют ввиду соглашения о названиях, которые используются для построения URL конечной точки API. Несколько важных соглашений для стандартного RESTful API представлены ниже:

  • Названия путей должны быть указаны во множественном числе: например для того чтобы получить данные о пользователе с id 5, мы бы воспользовались /users/5, а не /user/5.
  • В URL не должно содержаться расширения файла: несмотря на то что при обращении к конечной точке API, скорее всего, будет возвращаться файл в формате JSON, URL не должен оканчиваться на .json.
  • В URL должны использоваться существительные, а не глаголы: слова вроде add и delete не должны содержаться в URL, используемом в RESTful API. Для добавления нового пользователя вы могли бы просто отправить POST-запрос по адресу, в котором используется /users, а не что-то вроде /users/add. API должен разрабатываться так, чтобы был способен обрабатывать различные типы запросов по тому же самому URL-адресу.
  • Пути чувствительны к регистру (заглавным или строчным буквам) и должны быть написаны в нижнем регистре с использованием дефисов, а не символов подчеркивания.

Все эти соглашения являются рекомендациями, поскольку нет строгих стандартов REST, которых необходимо придерживаться. Однако если будете придерживаться их, то ваш API будет единообразным по стилю, узнаваемым для других разработчиков и легким для прочтения и понимания.

Заключение

В этом руководстве мы рассмотрели, что из себя представляют REST и RESTful API, как работают методы запроса HTTP и коды ответа, структуру URL API и основные соглашения RESTful API. В следующем руководстве мы рассмотрим, как применять всю эту теорию на практике на примере создания сервера при помощи Express и Node.js и создания нашего собственного API.

Понравилась статья? Поделить с друзьями:
  • Как написать and значком
  • Как написать ambient
  • Как написать ahk скрипт для samp
  • Как написать ahk на нажатие клавиш
  • Как написать abstract научной статьи на английском