Простое REST api для сайта на php хостинге
Иногда бывает необходимо развернуть не большое рест апи для своего сайта, сделанного по технологии СПА (Vue, React или др.) без использования каких-либо фреймворков, CMS или чего-то подобного, и при этом хочется воспользоваться обычным php хостингом с минимальными усилиями на внедрение и разработку. При этом там же желательно разместить и сам сайт СПА (в нашем случае на vue).
Использование php позволяет для построения ендпоинтов апи использовать даже статические php файлы, размещаемые просто в папках на хостинге, которые предоставляют результат при непосредственном обращении к ним. И хотя, видимо в своё время, такой подход послужил широкому распространению php мы рассмотрим далее более программистский подход к созданию апи, который очень похож на используемый в библиотеке Node.js Express и поэтому интуитивно понятен, и прост для освоения. Для это нам понадобиться библиотека «pecee/simple-router».
Далее мы предполагаем, что у вас уже есть среда для запуска кода локально (LAMP, XAMP, docker) или как-то иначе и у вас настроено перенаправление всех запросов на индексный файл (index.php). Кроме, того мы предполагаем, что вы можете устанавливать зависимости через composer.
Структура проекта

index.phpв папке web. Сама папка webявляется публично доступной папкой, и должна быть указана в настройках сервера как корневая. В папке configбудут находится настройки роутов наших ендпоинтов. В папке controllerбудут обработчики ендпоинтов маршрутов. В папке middlewaresмы разместим промежуточные обработчике роутов для выполнения авторизации перед началом основного кода ендпоинта. В папках exceptions, views и models будут соответственно исключения, html шаблон и объектные модели. Полный код проекта тут.
Инсталляция и запуск
Для работы необходимо инсталлировать следующее содержимое composer.json (composer install в корне проекта).
// composer.json < "require": < "pecee/simple-router": "*", "lcobucci/jwt": "^3.4", "ext-json": "*" >, "autoload": < "psr-4": < "app\\": "" >> >
Обратите внимание, что ‘app\’ объявлено как префикс для namespace. Данный префикс будет использоваться при объявлении неймспейсов классов.
Запуск всего остального кода происходит вызовом статического метода Router::route() в файле index.php
Так же тут подключаются роуты определённые в файле config/routes.php.
Подключение SPA на Vue.js 2 к проекту на php
Если вы развёртываете сборку vue отдельно от апи, то этот раздел можно пропустить.
Рассмотрим теперь то, как подключить проект на vue в данной конфигурации с использованием соответствующих маршрутов. Для этого содержимое сборки необходимо поместить в папку web. В файле маршрутов (‘/config/routes.php’) прописываем два правила:
; Router::setDefaultNamespace('app\controllers'); Router::get('/', 'VueController@run'); // правило 1 Router::get('/controller', 'VueController@run') ->setMatch('/\/([\w]+)/'); // правило 2
Для пустого (корневого) маршрута ‘/’ вызывается метод run класса VueController. Второе правило указывает что для любого явно незаданного пути будет тоже вызываться VueController, чтобы обработка маршрута происходила на стороне vue. Это правило всегда должно быть последним, чтобы оно срабатывало только тогда, когда другие уже не сработали. Метод run представляет собой просто рендеринг файла представления с помощью метода renderTemplate(), определённого в родительском классе контроллера. Здесь мы также устанавливаем префикс для классов методы которых используются в роутах с помощью setDefaultNamespace.
renderTemplate('../views/vue/vue_page.php'); > >
В свою очередь представление vue_page.php тоже просто отрисовка индексного файла сборки vue.
Итого мы подключили проект на vue к проекту на php, который уже готов к развертыванию на хостинге. Данный подход можно использовать для любых проектов на php. Осталось только рассмотреть, что собой представляет родительский класс AbstractController.
request = Router::router()->getRequest(); $this->response = new Response($this->request); > public function renderTemplate($template) < ob_start(); include $template; return ob_get_clean(); >public function setCors() < $this->response->header('Access-Control-Allow-Origin: *'); $this->response->header('Access-Control-Request-Method: OPTIONS'); $this->response->header('Access-Control-Allow-Credentials: true'); $this->response->header('Access-Control-Max-Age: 3600'); > >
В конструкторе класса AbstractController определяются поля $request и $response. В $request хранится распарсенный классом Pecee\Http\Router запрос. А $response будет использоваться для создания ответов на запросы к апи. Определённый здесь метод renderTemplate используется для рендеринга представлений (html страниц). Кроме того, здесь определён метод устанавливающий заголовки для работы с политикой CORS. Его следует использовать если запросы к апи происходят не с того же адреса, т.е. если сборка vue запускается на другом веб-сервере. Теперь перейдём непосредственно к созданию апи.
Создание REST API эндпоинтов
Для работы с апи нам нужно произвести дополнительную обработку входящего запроса, потому что используемая библиотека не производит парсинг сырых данных. Для этого создадим промежуточный слой ProccessRawBody и добавим его как middleware в роуты для запросов к апи.
$value) < $request->$key = $value; > > catch (\Throwable $e) < >> > >
Здесь мы считываем из входного потока и помещаем полученное в объект $request для дальнейшего доступа из кода в контроллерах. ProccessRawBody реализует интерфейс IMIddleware обязательный для всех middleware.
Теперь создадим группу роутов для работы с апи использующее данный промежуточный слой.
'api/v1', 'middleware' => [ ProccessRawBody::class ] ], function () < Router::post('/auth/sign-in', 'AuthController@signin'); Router::get('/project', 'ProjectController@index'); >);
У этой группы определён префикс «api/v1» (т.е. полный путь запроса должен быть например ‘/api/v1/auth/sign-in’), и ранее определённое нами middleware ProccessRawBody::class, так что в контроллерах наследованных от AbstractController доступны входные переменные через $request. AuthController рассмотрим чуть позже сейчас же мы уже можем воспользоваться методами не требующими авторизации, как например ProjectController::index.
response->json([ [ 'name' => 'project 1' ], [ 'name' => 'project 2' ] ]); > >
Как видим, на входящий запрос, в ответе возвращаются данные о проектах.
Остальные роуты создаются аналогичным образом.
Авторизация по JWT токену
Теперь перейдём к роутам требующим авторизации. Но перед этим реализуем вход и получение jwt-токена. Для создания токена и его валидации мы будем использовать библиотеку “ lcobucci/jwt” Всё это будет у нас выполнятся по роуту определённому ранее ‘/auth/sign-in’. Соответственно в AuthController::singin у нас прописана логика выдачи jwt-токена после авторизации пользователя.
builder() // Configures the issuer (iss claim) ->issuedBy('http://example.com') // Configures the audience (aud claim) ->permittedFor('http://example.org') // Configures the id (jti claim) ->identifiedBy('4f1g23a12aa') // Configures the time that the token was issue (iat claim) ->issuedAt($now) // Configures the expiration time of the token (exp claim) ->expiresAt($now->modify('+2 minutes')) // Configures a new claim, called "uid" ->withClaim('uid', $user->id) // Configures a new header, called "foo" ->withHeader('foo', 'bar') // Builds a new token ->getToken($config->signer(), $config->signingKey()); return $this->response->json([ 'accessToken' => $token->toString() ]); > >
Здесь используется симметричная подпись для jwt с использованием секретного ключа ‘секретный_ключ’. По нему будет проверятся валидность токена при запросах к апи. Ещё можно использовать асимметричную подпись с использованием пары ключей.
Можно также отметить, что можно создавать сколько угодно клаймов ->withClaim(‘uid’, $user->id) и сохранять там данные которые можно будет потом извлекать из ключа. Например, id пользователя для дальнейшей идентификации запросов от этого пользователя. Токен выдан на 2 минуты (->expiresAt($now->modify(‘+2 minutes’))) после чего он становится не валидным. ->issuedBy и ->permittedFor используются для oath2.
Теперь создадим группу роутов защищённую авторизацией. Для этого определим для группы роутов промежуточный слой Authenticate::class.
'api/v1', 'middleware' => [ ProccessRawBody::class ] ], function () < Router::post('/auth/sign-in', 'AuthController@signin'); Router::get('/project', 'ProjectController@index'); Router::group([ 'middleware' =>[ Authenticate::class ] ], function () < // authenticated routes Router::post('/project/create', 'ProjectController@create'); Router::post('/project/update/', 'ProjectController@update') ->where(['id' => '[\d]+']); >); >);
Как видите, группа с авторизацией объявлена внутри группы с префиксом “api/v1 ”. Рассмотрим роут ‘/project/update/’. Здесь объявлен параметр id который определён как число. В метод update, контроллера Projectcontroller будет передана переменная $id содержащая значение этого параметра. Ниже приведён пример запроса и ответ.
> */ public function update(int $id): string < // код обновляющий проект return $this->response->json([ [ 'response' => 'OK', 'request' => $this->request->project, 'id' => $id ] ]); > >
Вернёмся теперь к промежуточному слою Authenticate::class с помощью которого происходит авторизация запросов к апи.
parser()->parse($tokenString); if ( !$config->validator()->validate( $token, new SignedWith( new Sha256(), InMemory::plainText('секретный_ключ') ), new ValidAt(new FrozenClock(new DateTimeImmutable())) ) ) < throw new NotAuthorizedHttpException('Токен доступа не валиден или просрочен'); >$userId = $token->claims()->get('uid'); $request['uid'] = $userId; > >
Здесь, считывается заголовок ‘Authorization: Bearer [token]’ (так называемая bearer авторизация) и извлекается оттуда токен, которые клиенты получают после логина и должны посылать со всеми запросами, требующими авторизацию. Далее с помощью парсера jwt-токен-строчка парсится. И дальше с помощью валидатора распарсенный токен валидируется. Метод validate() возвращает true or false. В случае не валидного токена выбрасывается исключение NotAuthorizedException. Если токен валидный, то мы извлекаем из него id пользователя $token->claims()->get(‘uid’) и сохраняем в переменную запроса $request, чтобы его можно было использовать дальше в контроллере. NotAuthorizedException определяется следующим образом:
В завершении рассмотрим ещё обработку ошибок. В файле routes.php запишем следующие строчки:
httpCode(401); break; > case Exception::class: < $response->httpCode(500); break; > > if (PROD) < return $response->json([]); > else < return $response->json([ 'status' => 'error', 'message' => $exception->getMessage() ]); > >);
В итоге файл routes.php будет выглядеть следующим образом:

; use app\middlewares\< Authenticate, ProccessRawBody >; use Pecee\< Http\Request, SimpleRouter\SimpleRouter as Router >; const PROD = false; Router::setDefaultNamespace('app\controllers'); Router::get('/', 'VueController@run'); Router::group([ 'prefix' => 'api/v1', 'middleware' => [ ProccessRawBody::class ] ], function () < Router::post('/auth/sign-in', 'AuthController@signin'); Router::get('/project', 'ProjectController@index'); Router::group([ 'middleware' =>[ Authenticate::class ] ], function () < // authenticated routes Router::post('/project/create', 'ProjectController@create'); Router::post('/project/update/', 'ProjectController@update') ->where(['id' => '[\d]+']); >); >); Router::get('/controller', 'VueController@run') ->setMatch('/\/([\w]+)/'); Router::error(function(Request $request, Exception $exception) < $response = Router::response(); switch (get_class($exception)) < case NotAuthorizedHttpException::class: < $response->httpCode(401); break; > case Exception::class: < $response->httpCode(500); break; > > if (PROD) < return $response->json([]); > else < return $response->json([ 'status' => 'error', 'message' => $exception->getMessage() ]); > >);
Заключение
В итоге у нас получилось небольшое, простое REST api для небольших проектов которое можно использовать на обычном php хостинге с минимальными трудозатратами на его (хостинга) настройку. Полный код проекта тут.
Больше настроек роутов можно найти здесь. Вместо рассмотренной библиотеки «pecee/simple-router» можно использовать любую другую аналогичную библиотеку или даже микрофреймворк Slim.
Пс. Если вы используете публичный репозиторий или придерживаетесь бестпрактис, то не следует хранит секретный ключ в коде. Для этого можно использовать переменные среды или локальные файлы, которые не добавляются в репозиторий. Код работы с jwt токенами можно выделить в отдельный класс в папке services.
Как сделать API запрос на PHP?
Дали пример того как должен выглядеть php запрос и все никак не дойдет что с ним надо делать, как оборачивать и как отправлять, можете подсказать?
Пример: HTTPie-cli: http --form POST https://api.saures.ru/login email=demo@saures.ru password=demo -v
HTTP-запрос POST /login HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 36 Content-Type: application/x-www-form-urlencoded; charset=utf-8 Host: api.saures.ru User-Agent: HTTPie/0.9.8 email=demo%40saures.ru&password=demo
HTTP-ответ HTTP/1.1 200 OK Connection: keep-alive Content-Length: 98 Content-Type: application/json; charset=utf-8 < "data": < "role": 1, "sid": "b731f212-f5ee-43de-a076-55f6de9fd690" >, "errors": [], "status": "ok" >
все что смог дак это собрать вот такую штуку, она естественно не работает, ошибку говорит
class ApiController extends Controller < public function api1()< $url='https://api.saures.ru/login'; // массив для переменных, которые будут переданы с запросом $opts = array( 'http' =>array( 'method' => 'POST', // метод передачи данных 'header' => 'Content-type: application/x-www-form-urlencoded;charset=utf-8', // заголовок 'email' => 'demo@40saures.ru', 'password' => 'demo' ) ); $context = stream_context_create($opts); // создаём контекст потока $result = file_get_contents($url, false, $context); //отправляем запрос dd($result) ; > >
- Вопрос задан более трёх лет назад
- 6741 просмотр
1 комментарий
Простой 1 комментарий
GET Запросы. Получение данных
вы определитесь, на чем вывести на js или на php. отредактируйте свой вопрос (править) и укажите только нужные тэги. php, json или js, json , остальное тут мусор. Приведите код как вы получаете данные и планируете выводить их.
26 сен 2017 в 8:28
Спасибо, подправил
26 сен 2017 в 11:26
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
function file_get_contents_curl($url) < $ch = curl_init(); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //Устанавливаем параметр, чтобы curl возвращал данные, вместо того, чтобы выводить их в браузер. curl_setopt($ch, CURLOPT_URL, $url); $data = curl_exec($ch); curl_close($ch); return json_decode($data); >$json = file_get_contents_curl('http://api.bus62.ru/api7/?cm=gsf&cd=yakutsk&sr=mob&f=json&sid=129&type=0&lim=80&deep=4&devid=1439865001'); foreach($json->forecast as $v)< echo $v->num.'
'.$v->arrTime.'
'.$v->lastStation.'
'.$v->whereGo .'
'; echo '------------
'; >
Отслеживать
ответ дан 26 сен 2017 в 8:37
newProgrammer newProgrammer
887 10 10 серебряных знаков 26 26 бронзовых знаков
Спасибо большое!
27 сен 2017 в 3:14
Создайте одну функцию, с которым в дальнейшем сможете отправлять все запросы
function sendRequest($url, $params = null, $method = 'GET') < if (!empty ($url)) < $handler = curl_init ($url); $options = [ CURLOPT_RETURNTRANSFER =>true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_CUSTOMREQUEST => $method, CURLOPT_CONNECTTIMEOUT => 0, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_TIMEOUT => 0, CURLOPT_ENCODING => true, ]; if (in_array ($method, ['POST'])) < if ($method == 'POST') $options[CURLOPT_POST] = true; $options[CURLOPT_POSTFIELDS] = http_build_query ($params); >curl_setopt_array($handler, $options); $result = curl_exec ($handler); curl_close ($handler); return json_decode($result); > return false; >
а дальше сделайте просто запрос в нужном месте
$obj = sendRequest('http://api.bus62.ru/api7/?cm=gsf&cd=yakutsk&sr=mob&f=json&sid=129&type=0&lim=80&deep=4&devid=1439865001');
$obj = sendRequest('URL', ['key'=>'val','key1'=>'val1'], 'POST');
Как сделать HTTP-запрос или HTTPS-запрос в PHP скрипте
В программированию, очень часто встречается задача, когда нужно обратиться к другому сайту через HTTP или HTTPS. В этой статье мы рассмотрим простой способ, как с помощью языка программирования PHP выполнить данную задачу.
Для чего обращаться с помощью PHP через HTTP или HTTPS к другому сайту?
Причин может быть несколько, например, на вашем сайте, есть часть функционала, которая отвечает за добавление новых товаров или каталогов фильмов на ваш сайт.
Новые фильмы выходят на экран практически ежедневно и этот процесс лучше автоматизировать, чтобы этим занимался робот, который сделает это бесплатно.

С товарами, ситуация такая же — у вашего интернет-магазина могут быть партнеры, которые захотят разместить на вашем сайте свои товары и с каждой продажи товара, партнеры будут отдавать вам, часть своих доходов. Если у партнера несколько тысяч товаров и база постоянно обновляется, процесс добавления товаров на сайт также необходимо автоматизировать с помощью языка программирования PHP.
Кстати, в нашем курсе Профессия веб-программист, мы подробно на практическом примере рассматриваем, как можно быстро добавить из одного источника в базу данных вашего сайта тысячи фильмов.
Пример обращения к другому сайту с помощью PHP
В этом простом примере, мы будем использовать стандартную функцию PHP под названием file_get_contents().
Для начала, давайте попробуем через API социальной сети ВКонтакте получить информацию о пользователе с ID 210700286. Просто введите в браузер ссылку ниже:
https://api.vk.com/method/users.get.json?user_ids=210700286&fields=bdate&v=5.68
В ответ от сервера VK, вы увидите следующую информацию:
где, мы получили Имя, Фамилию и дату рождения пользователя с ID 210700286.
Если вы зарегистрированы в социальной сети ВКонтакте, вы можете попробовать по ID, получить информацию о вас или вашем друге.
Как теперь мы может с помощью PHP получить эту информацию и преобразовать ее в массив, для удобной дальнейшей работы?
С помощью языка программирования PHP и функции file_get_contents(), это сделать очень просто!
Если у вас не установлен и не настроен веб-сервер для работы с PHP, посмотрите бесплатно онлайн видео «Как установить и настроить веб-сервер» из курса PHP/MySQL.
Напишите следующий код в файле index.php:
$user_id — это переменная, в которую вы записываете ID пользователя VK,
$info — в этой переменной мы сохраняем результат обращения к API сайта VK.COM
Далее, с помощью PHP-функции json_decode, мы преобразуем JSON, полученный от VK в массив PHP, для удобной дальнейшей работы и функцией print_r() мы выводим массив в браузер.
Имея массив с этой информацией, вы можете на вашем сайте, отображать интересных вам пользователей, для какой-либо цели, а также с помощью HTML/CSS, вы можете красиво оформить вывод данной информации на вашем сайте.
В нашем курсе PHP/MySQL мы подробно рассматриваем работу с массивами, переменными, базой данных, функциями и многое другое.
Вывод
Как вы видите, с помощью PHP вы можете очень легко делать запросы к HTTP и HTTPS сайтам и мы рассмотрели лишь одну функцию языка программирования PHP с помощью которой можно получить данные из внешнего сайта.
В следующих статьях, мы рассмотрим еще одну интересную, но более мощную функцию, в которую вы сможете задавать дополнительные параметры, такие как браузер, операционная система и другие, для более изящной работы с внешними сайтами.