API (программный интерфейс приложения, интерфейс прикладного программирования) (англ. application programming interface, API) — описание способов (набор классов, процедур, функций, структур или констант), которыми одна компьютерная программа может взаимодействовать с другой программой.
WEB-сервер — сервер, принимающий HTTP-запросы от клиентов, обычно веб-браузеров, и выдающий им HTTP-ответы, как правило, вместе с HTML-страницей, изображением, файлом, медиа-потоком или другими данными.
HTTP (англ. HyperText Transfer Protocol — «протокол передачи гипертекста») — протокол прикладного уровня передачи данных, изначально — в виде гипертекстовых документов в формате HTML, в настоящее время используется для передачи произвольных данных.
Основой HTTP является технология «клиент-сервер», то есть предполагается существование:
Каждое HTTP-сообщение состоит из трёх частей, которые передаются в указанном порядке:
Стартовые строки различаются для запроса и ответа. Строка запроса выглядит так:
Метод URI HTTP/Версия
Здесь:
Чтобы запросить страницу данной статьи, клиент должен передать строку (задан всего один заголовок):
GET /wiki/HTTP HTTP/1.0
Host: ru.wikipedia.org
Стартовая строка ответа сервера имеет следующий формат: HTTP/Версия КодСостояния Пояснение, где:
Например, стартовая строка ответа сервера на предыдущий запрос может выглядеть так:
HTTP/1.0 200 OK
Метод HTTP (англ. HTTP Method) — последовательность из любых символов, кроме управляющих и разделителей, указывающая на основную операцию над ресурсом. Обычно метод представляет собой короткое английское слово, записанное заглавными буквами. Обратите внимание, что название метода чувствительно к регистру.
Сервер может использовать любые методы, не существует обязательных методов для сервера или клиента. Если сервер не распознал указанный клиентом метод, то он должен вернуть статус 501 (Not Implemented). Если серверу метод известен, но он неприменим к конкретному ресурсу, то возвращается сообщение с кодом 405 (Method Not Allowed). В обоих случаях серверу следует включить в сообщение ответа заголовок Allow со списком поддерживаемых методов.
GET
Используется для запроса содержимого указанного ресурса.
Клиент может передавать параметры выполнения запроса в URI целевого ресурса после символа «?»:
GET /path/resource?param1=value1¶m2=value2 HTTP/1.1
POST
Применяется для передачи пользовательских данных заданному ресурсу.
Код состояния является частью первой строки ответа сервера. Он представляет собой целое число из трёх цифр. Первая цифра указывает на класс состояния. За кодом ответа обычно следует отделённая пробелом поясняющая фраза на английском языке, которая разъясняет человеку причину именно такого ответа. Примеры:
201 Webpage Created
403 Access allowed only for registered users
507 Insufficient Storage
Заголовки HTTP (англ. HTTP Headers) — это строки в HTTP-сообщении, содержащие разделённую двоеточием пару параметр-значение. Заголовки должны отделяться от тела сообщения хотя бы одной пустой строкой.
Примеры заголовков:
Server: Apache/2.2.11 (Win32) PHP/5.3.0
Last-Modified: Sat, 16 Jan 2010 21:16:42 GMT
Content-Type: text/plain; charset=windows-1251
Content-Language: ru
Тело HTTP-сообщения (message-body), если оно присутствует, используется для передачи тела объекта, связанного с запросом или ответом.
В принципе WEB-сервер можно написать на любом языке. Но удобнее разрабатывать сервер на тех языках, где есть нативная (на уровне языка) или внешняя (с помощью подключаемых модулей) поддержка HTTP-протокола.
К таким относятся (список не полный, тут только то с чем я сам работал или "на слуху"):
Пробежимся по верхушкам:
Переменные - зяык динамически типизируемый, поэтому типы при объявлении переменных можно не использовать. Переменной одного типа в любой момент может быть присвоено значение другого типа. Ключевых слов для объявления переменных тоже нет - переменная создается в момент присваивания ей значения (но если попытаться считать переменную до её объявления, то получим исключение). Первым символом в названии переменной должен быть знак "$"
$myVariable = 0;
$myVariable = "а может не 0";
Массивы - пустой массив можно создать либо функцией array, либо просто присвоив пустой массив
$myArray = array();
$myArray = [];
Массивы бывают обычные и ассоциативные (пара ключ - значение)
$simpleArray = [1, 2, 3];
$associativeArray = [
'one' => 'value',
'two' => 'value'
];
Литералы - строки могут быть как в одинарных, так и в двойных кавычках. Двойные кавычки отличаются тем, что в них можно использовать управляющие символы и переменные
$string = 'это строка';
$anotherString = "это тоже строка, но она поддерживает перенос\n и может включать переменные $string";
Функции - функции объявляются ключевым словом function, тело функции заключается в фигурные скобки - обычный Си-подобный синтаксис
function someFunction($firstParam, $secondParam)
{
return $firstParam.$secondParam;
}
$concat = someFunction('раз', 'два');
Обратите внимание, для склеивания строк используется символ точки, знак "+" используется только с числовыми переменными.
Классы
class ApiServer extends ParentClass
{
// свойство класса
private $var;
// конструктор класса
public function __construct(){
// ЛОКАЛЬНАЯ переменная
$var = 0;
$this->var = 1;
}
}
Обратите внимание, обращение к свойствам и методам класса производится через ключевое слово $this
API будем писать похожее на то, что использовалось для проекта "база" (то апи написано на Node.js, в конце я приведу исходный код). Отличия обусловлены тем, что сервер на PHP является stateless (не хранящим состояние). Поэтому без использования дополнительных механизмов (Redis, Mongo) нам негде хранить токен и будем использовать "базовую" авторизацию.
Таким образом, запросы login и logout нам не понадобятся, сразу реализуем методы получения данных (примеры запросов в формате плагина REST Client редактора VSCode).
### Запрос списка продукции
GET {{url}}/Product
Authorization: Basic ZXNtaXJub3Y6MTExMTAz
Обратите внимание, вместо токена используется заголовок Authorization. В этом заголовке первое слово обозначает алгоритм авторизации (они бывают разные), а второе это закодированная base64 строка логин:пароль (позже, когда мы вернёмся к C#, я покажу как сформировать эту строку программно, а пока можете её получить используя онлайн кодировщики base64).
Точкой входа сервера по-умолчанию являются файлы index.html или index.php.
Создайте файл index.php:
<?php
// тут можно писать код
?>
Описываем класс сервера и создаём его (при этом вызовется конструктор)
class ApiServer
{
public function __construct(){
print_r($_SERVER);
}
}
new ApiServer();
Функция print_r выводит в консоль содержимое переменной
Переменная $_SERVER внутренняя глобальная переменная языка PHP, она содержит параметры запроса и возвращает примерно такое:
Array
(
[DOCUMENT_ROOT] => /home/kei/[ЙОТК]/API_PHP
[REMOTE_ADDR] => 127.0.0.1
[REMOTE_PORT] => 39956
[SERVER_SOFTWARE] => PHP 7.4.3 Development Server
[SERVER_PROTOCOL] => HTTP/1.1
[SERVER_NAME] => localhost
[SERVER_PORT] => 8000
[REQUEST_URI] => /Product
[REQUEST_METHOD] => GET
[SCRIPT_NAME] => /index.php
[SCRIPT_FILENAME] => /home/kei/[ЙОТК]/API_PHP/index.php
[PATH_INFO] => /Product
[PHP_SELF] => /index.php/Product
[HTTP_USER_AGENT] => vscode-restclient
[HTTP_AUTHORIZATION] => Basic ZXNtaXJub3Y6MTExMTAz
[HTTP_ACCEPT_ENCODING] => gzip, deflate
[HTTP_HOST] => localhost:8000
[HTTP_CONNECTION] => close
[PHP_AUTH_USER] => esmirnov
[PHP_AUTH_PW] => 111103
[REQUEST_TIME_FLOAT] => 1638434071.8106
[REQUEST_TIME] => 1638434071
)
Нам, для начала, интересны параметры:
/Product). Есть ещё параметр REQUEST_URI, но в нём хранится путь вместе с параметрами запроса (например, /Product?id=1)Разберем на примере простой скрипт:
class ApiServer
{
// шаблон ответа
private $response = ['notice'=>[]];
private $db = null;
public function __construct(){
// результат в формате JSON
header('Content-Type: application/json; utf-8');
try {
switch($_SERVER['REQUEST_METHOD'])
{
case 'GET':
$this->processGet($_SERVER['PATH_INFO']);
break;
// case 'POST':
// $this->processPost($_SERVER['PATH_INFO']);
// break;
}
} catch (\Throwable $th) {
$this->response['notice']['answer'] = $th->getMessage();
}
// выводим в stdout JSON-строку
echo json_encode($this->response, JSON_UNESCAPED_UNICODE);
}
private function processGet($path)
{
switch($path)
{
case '/Product':
$this->auth();
// получаем данные
$this->response['notice']['data'] = $this->db
->query("SELECT * FROM Product")
->fetchAll(PDO::FETCH_ASSOC);
break;
default:
header("HTTP/1.1 404 Not Found");
}
}
private function auth()
{
if(!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']))
throw new Exception('Не задан логин/пароль');
// пытаемся подключиться к MySQL серверу
$this->db = new PDO(
"mysql:host=kolei.ru;port=3306;dbname={$_SERVER['PHP_AUTH_USER']};charset=UTF8",
$_SERVER['PHP_AUTH_USER'],
$_SERVER['PHP_AUTH_PW']);
}
}
Запустить локальный сервер для отладки можно из командной строки в каталоге проекта
php -S localhost:8000
Параметры GET-запроса передаются прямо в URL. Отделяются от пути знаком вопроса. Между собой разделяются знаком &. Представляют собой пары ключ=значение. Например, так может выглядеть запрос материала по нужному продукту:
GET {{url}}/Material?product_id=1
PHP автоматически разбирает URL и параметры GET-запроса нам доступны через глобальную переменную $_GET:
$productId = $_GET['product_id'];
POST-запросы отличаются тем, что содержат данные в "теле" запроса
Формат данных определяется заголовком Content-Type
application/x-www-form-urlencoded - формат по-умолчанию, представляет собой те же пары ключ=значение, что и в GET-запросе (только без знака вопроса). Автоматически распознается PHP и заносится в глобальный массив переменных $_POST
application/json - данные передаются в виде JSON-строки (сериализованы). Автоматически не распознаются, приходится программно читать из потока данных:
$rawData = file_get_contents('php://input');
$json = json_decode($rawData);
В переменной $json будет JSON-объект. Данные из него извлекаются как из класса -> стрелочным синтаксисом.
Если кому-то удобнее работать с ассоциативными массивами, то можно в функции json_decode добавить второй параметр true
$json = json_decode($rawData, true);
Рассказать про докер...
В каталоге data этого репозитория лежит архив DockerPhp.zip, в нём настроены контейнеры PHP и NGINX.
PHP проект складывать в подкаталог www/yotc.kei - он будет доступен на локальной машине по адресу http://localhost:8080. Если вам нужно более одного проекта, то нужно добавить в NGINX конфиг для нового проекта:
в каталоге hosts скопировать файл настроек yotc_kei.conf в, например, myproject.conf и исправить в нём 2 строки (я их специально вынес в начало конфига)
server {
listen 8081;
root /var/www/html/myproject;
www (/var/www/html/ - на этот путь каталог монтируется в контейнере)в файле docker-compose.yml в секцию nginx -> ports добавьте порт вашего нового проекта
services:
nginx:
# используем последний стабильный образ nginx
image: nginx:latest
# маршрутизируем порты
ports:
- "8080:80"
- "8081:8081"
Как запустить/остановить контейнеры написано в read.me (находится в архиве)