Skip to content

Latest commit

 

History

History
163 lines (110 loc) · 20.1 KB

File metadata and controls

163 lines (110 loc) · 20.1 KB

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

Установка

Для демонстрации работы проекта во время лекции, я использовал 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 или домен.