Skip to content

Latest commit

 

History

History

conference.backend

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

Серверная часть приложения

Установка

Для демонстрации работы проекта во время лекции, я использовал OpenServer. В нём нужно добавить эту папку (conference.backend) внутрь директории domains и добавить в файл hosts запись вида: 127.0.0.1 conference.backend

Файл hosts на OS Windows находится тут: C:\Windows\System32\drivers\etc

После этих действий, нужно перезапустить OpenServer, тогда обращение к домену http://conference.backend будет обращаться к нашему серверу.

Люди, которые умеют запускать Apache сервер без OpenServer в предыдущей инструкции не нуждаются ;-)

Документация

Настройка сервера

Для корректного поддержания роутинга в нашем бекенд приложении, необходимо внести изменения в файл .htaccess, чтобы сервер передавал роут и метод запроса в суперглобальный массив $_GET в едином обработчике index.php.

Для этого наш файл .htaccess содержит следующий блок:

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) /index.php?method=%{REQUEST_METHOD}&page=$1 [QSA,L]
</IfModule>

Здесь мы делаем следующее:

  • RewriteCond %{REQUEST_FILENAME} !-f - предотвращаем чтение файлов через адресную строку
  • RewriteCond %{REQUEST_FILENAME} !-d - предотвращаем чтение директорий через адресную строку
  • RewriteRule (.*) /index.php?method=%{REQUEST_METHOD}&page=$1 [QSA,L] - эта строка творит основную магию, она перенаправляет все запросы на наш index.php файл, куда в query-параметры подставляет метод запроса в поле method=, а в поле page= подставляет страницу, к которой мы обращались

Эти параметры будут доступны в файле index.php в массиве $_GET['method'] и $_GET['page'].

В приложении мы используем обрабатываем лишь два вида URL:

  • http://conference.backend/users
  • http://conference.backend/news

Соответственно, если мы обращаемся к первому URL, то $_GET['page'] будет равно строке 'users', если ко второму, то 'news'.

Обработка запросов

Наш сервер перенаправляет все запросы на единую точку входа в приложение, на файл index.php. В самом начале файла мы проставляем HTTP-заголовки, нужные для корректной работы с нашим Фронтендом.

Первые два заголовка нужны для того, чтобы защита браузера не мешала обращаться к нашему бекенду (Access-Control-Allow-Origin: *), при чём здесь звёздочка означает вообще все возможные домены. В реальных приложениях лучше ограничивать только своими доменами или вообще чтобы бекенд и фронтенд находились на одном домене.

Следующий заголовок позволяет защите браузера корректно обрабатывать запросы с перечисленными методами. Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE означает, что браузер не будет препятствовать при совершении запросов с этими методами к нашему серверу.

Далее, весь код обёрнут в конструкцию

try {
    ...
} catch (...) {
    ...
}

Это сделано для того, чтобы обрабатывать непредвиденные ошибки на сервере. Если возникает какая-то непредвиденная ошибка в коде, то наш интерпретатор попадает в блок catch, который проставляет код ответа 500 и возвращает строку 'Internal server error'.

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

Схожие конструкции можно увидеть далее в коде сервера повсеместно, я не буду их ещё раз описывать. Они могут содержать коды 400, 404 и соответствующие коды ошибок.

Примечание!

После строки `exit();` скрипт прекращает своё действие! 
Поэтому, эта функция используется, чтобы обрабатывать ошибки.

Внутри блока try {...} сперва идёт как раз таки подобная проверка, она смотрит, был ли вообще добавлен какой-то роут, если нет, возвращает ошибку 404.

После этого, мы сохраняем название роута в переменную $route и инициализируем переменную $data/, куда мы запишем данные, полученные после обработки запроса и вернём их на клиент.

После этого следует конструкция switch ()...case, которая заменяет множественный вызов if() {...} else if () {...} else {}, где мы обрабатываем каждый метод запроса по-своему.

В конце файла index.php мы проверяем, появилось ли что-то в переменной $data, если да, возвращаем это. Данные в эту переменную записываются с помощью обработчиков, находящихся в файле functions.php.

Доступные URL

  • GET http://conference.backend/users - получить всех пользователей

  • GET http://conference.backend/users?id=1 - получить пользователя с id=1

  • POST http://conference.backend/users?login=логин&name=имя&password=пароль - добавить пользователя, передав логин, имя и пароль

  • DELETE http://conference.backend/users?id=1 - удалить пользователя с id=1

  • GET http://conference.backend/news - получить все новости

  • GET http://conference.backend/news?id=1 - получить новости с id=1

  • POST http://conference.backend/news?title=заголовок&text=текст&author=автор - добавить новость, передав заголовок, текст и имя автора

  • DELETE http://conference.backend/news?id=1 - удалить новость с id=1

Хранение данных

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

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

Функции-обработчики приложения

Все наши функции-обработчики и вспомогательные функции находятся в файле functions.php. Здесь находятся обработчик GET-запросов, обработчик POST-запросов, обработчик DELETE-запросов и вспомогательные функции.

Вспомогательные функции

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

Здесь присутствуют всего лишь две вспомогательные функции:

  • string makeRoutePath(string $route) - функция принимает 1 аргумент (строку) и делает путь до нашей директории data. При этом, роутом должно быть название нужного файла без расширения (без .json в конце). Возвращает функция строку, являющуюся абсолютным путём до директории с файлами. При этом, мы используем суперглобальную переменную __DIR__, с помощью которой нам не важно, какая OS используется на сервере, путь будет корректным.
  • mixed array_find(array $haystack, callable $callback) - функция принимает два аргумента, это массив $haystack, в котором мы будем что-то искать, и функция $callback, которая будет следить за корректностью поиска. Внутри функции мы перебираем входящий массив с помощью цикла foreach, внутри мы выполняем нашу функцию $callback и делаем проверку, если функция вернула истину, мы возвращаем элемент, на котором находимся, прерывая выполнение цикла. Это значит, что функция $callback проверяет соответствие элемента нашему сложному условию, находящемуся в функции.

Обработчик GET-запросов

Этот обработчик должен брать данные на сервере и возвращать их.

Функция array getData(string $route, array $params) принимает два аргумента, это $route, по которому мы будем искать, и $params - прочие параметры, которые мы будем передавать в обработчик.

В самом начале функции мы создаём путь до файла с данными по переданному роуту.

Далее следует проверка на существование этого файла и, если его нет, то мы возвращаем ошибку 404 с текстом Not found.

Далее, мы конвертируем полученный JSON-файл с данными в php-массив и делаем проверку на существование поля id в переданном массиве $params. Если он там есть, то это означает, что запрос был выполнен на сервер таким образом: http://conference.backend/news?id=1 (например id=1).

Это будет означать, что нужно вернуть только новость с id 1, иначе - вернуть все.

Примечание!

В настоящих приложениях с большими данными обычно есть такие переменные, как `limit` и `offset`. 
Переменная лимит указывает сколько записей возвращать за один запрос, а оффсет - откуда стоит начать. 
Чтобы понять, как оно должно работать - представьте себе "Пагинацию" любого интернет-магазина.

Обработчик POST-запросов

Этот обработчик должен добавлять новые записи в наше хранилище.

Функция array postData(string $route, array $params) - принимает такие же аргументы, как и getData(). Начало функции выполняет такие же действия - получает путь до нужного файла и проверяет его существование. Дальше - преобразует полученный JSON-файл в php-массив.

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

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

Каждый обработчик делает следующее:

  1. В самом начале проверяет наличие всех необходимых полей, переданных в переменной $params для новостей тут, для пользователей тут и, если чего-то не хватает, возвращает ошибку 400, обозначающую проблему в запросе.
  2. Записывает в переменную $newData новые данные для записи

После этих двух действий, наш обработчик выйдет из конструкции switch ()...case и создаст новый id для новой записи, взяв id последней записи в массиве $data и прибавив к нему 1.

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

В конце обработчика проверяется корректность записи в файл и возвращаются на клиент новые данные.

Обратите внимание, что новые данные вернутся с новым id, а с клиента id не должен приходить!

Обработчик DELETE-запросов

Этот обработчик должен удаляет записи из хранилища по id.

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

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

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

Заключение

Этих функций должно хватить, чтобы можно было построить приложение, способное управлять данными так, как нам это нужно. Обычно добавляют ещё два метода PUT, для полной перезаписи существующих данных, и PATCH для частичной записи данных в ту или иную сущность.

Так же, используют СУБД и проверку авторизации. С помощью нашей не сложной архитектуры, можно с лёгкостью добавить проверки аутентификации пользователей в файле index.php.

Деплой

Писать бекенд на php было выбрано не случайно, ведь вы с лёгкостью сможете найти хостинг, на котором настроен php сервер и можете утомиться искать хостинг на node.js\python\ruby\java\etc...

Чтобы начать пользоваться приложением в реальной жизни, достаточно арендовать любой хостнг, с сервером Apache, поддерживающий PHP и загрузить файлы бекенда туда, привязав к ним какой-нибудь IP или домен.