PHP / [Из песочницы] Создание “API-Centric” Web Application на PHP

в 12:48, , рубрики: api, mvc, php, ооп, метки: , , ,

PHP / [Из песочницы] Создание “API-Centric” Web Application на PHP
Что такое “API-Centric” Web Application?
Это веб приложение которое большая часть функционала реализуется через API. Например: если вы авторизовываетесь, то вы отправляете свои данные через функции API, а API уже возвращает результат success или же ошибку. Другой характеристикой API является то что API не зависит от состояния пользователя.Зачем это нужно ?
Как веб разработчики мы видим, что технологии развиваются каждый день. И все знают, что люди используют не только браузеры на pc, но и android, apple, windows phone и прочие гаджеты для доступа в интернет к своим любимым сайтам.Так причём же тут API ?
Одним из преимуществ создания api-centric приложения это помощь в построении функциональности, которая может быть использована на любом другом девайсе, будь это браузер, мобильник, планшет или даже десктопное приложение. Все, что нужно сделать, это создать API, таким образом, чтобы все эти устройства могли взаимодействовать с ним.
Создавая приложения таким способом, мы можем воспользоваться им в различных средах и различными людьми. В этой статье мы создадим простое TODO приложение и клиентскую часть взаимодействующую с серверной. let’s begin!Шаг 1. Планирование функциональности
В todo приложении которое мы будем сейчас делать мы реализуем crud операции.Create TODO items

Read TODO items

Update TODO items

Delete TODO items

Каждый TODO item будет иметь:Заголовок

Дата окончания

Описание

Флажок, показывающий, что TODO item выполнено
Давайте создадим макет нашего приложенияШаг 2. Создание API сервера
Создадим 2 проекта: API сервер (back end) и клиентскую часть (front end)
Создайте папку simpletodo_api и в ней создайте index.php файл. Он будет являться главным контроллером нашего API (front controller) так что все запросы к серверу API будет осуществляться через этот файл. Откройте его и вставьте следующий код:
$action();
$result['success'] = true;

} catch( Exception $e ) {
//catch any exceptions and report the problem
$result = array();
$result['success'] = false;
$result['errormsg'] = $e->getMessage();
}

//echo the result of the API call
echo json_encode($result);
exit();

Мы по существу построили простой front controller который:Принимает вызовы API с любым числом параметров

Извлекает Controller и Action для вызова API

Делает необходимую проверку на существование контроллера и действия

Выполняет вызов API

Отлавливает ошибки

Отправляет результат

Так же создайте папки controllers, models и data.
Папка controllers будет содержать все контроллеры используемые API сервером. Мы построим mvc архитектуру, чтобы сделать наш api server чистым и гибким.
Папка models будет содержать все модели api server’a
Папка data будет хранить все данные
Зайдём в папку контроллеров и создадим todo.php
Это будет наш контроллер для других TODO списка связанных задач. Создаем заготовку класса и функционал для нашего приложения:
_params = $params;
}

public function createAction()
{
//create a new todo item
}

public function readAction()
{
//read all the todo items
}

public function updateAction()
{
//update a todo item
}

public function deleteAction()
{
//delete a todo item
}
}

Теперь добавим необходимый функционал действию action. Мы создаём метод createAсtion. Если вы в хорошем настроении то можете создать и для других действий функционал, я лишь предоставил интерфейс.
public function createAction()
{
//create a new todo item
$todo = new TodoItem();
$todo->title = $this->_params['title'];
$todo->description = $this->_params['description'];
$todo->due_date = $this->_params['due_date'];
$todo->is_done = 'false';

//pass the user's username and password to authenticate the user
$todo->save($this->_params['username'], $this->_params['userpass']);

//return the todo item in array format
return $todo->toArray();
}

Создаём todoItem.php в моделях, чтобы мы могли создать код, реализующий «Создание пункта списка». Кстати говоря в данном примере – без БД (на файлах). Вы, конечно же можете не ограничиваться.
todo_id) || !is_numeric($this->todo_id) ) {
//the todo id is the current time
$this->todo_id = time();
}

//get the array version of this todo item
$todo_item_array = $this->toArray();

//save the serialized array version into a file
$success = file_put_contents(DATA_PATH."/{$userhash}/{$this->todo_id}.txt", serialize($todo_item_array));

//if saving was not successful, throw an exception
if( $success === false ) {
throw new Exception('Failed to save todo item');
}

//return the array version
return $todo_item_array;
}

public function toArray()
{
//return an array version of the todo item
return array(
'todo_id' => $this->todo_id,
'title' => $this->title,
'description' => $this->description,
'due_date' => $this->due_date,
'is_done' => $this->is_done
);
}
}

Метод createAction вызывает 2 функции модели todoItem:
Save() – сохраняет todoItem в файл, а также при необходимости создает todo_id для todoItem.

toArray() – возвращает todoItem в виде массива, индексами которых служат переменные

localhost/simpletodo_api/?controller=todo&action=create&title=test%20title&description=test%20description&due_date=12/08/2011&username=nikko&userpass=test1234 Если всё работает, вы должны увидеть в папке data новую папку, в которой будет текстовый документ такого содержания:
Congratulations! Мы успешно создали API server и сделали вызов API.Шаг 3. Защищаем API server с app id и app secret.
Сейчас наш api server принимает все запросы. Мы должны ограничить доступ к серверу, чтобы гарантировать, что только наши собственные клиенты имеют возможность сделать API запросы. Так же вы можете создать систему в которой пользователи могут создавать свои собственные приложения, которые имеют доступ к API серверу, подобно тому, как работает Facebook и Twtter приложения.
Начнём с создание пары id-key для клиентов, которые буду использовать наш сервер. Так как это демо, мы можем использовать рандомную 32 символьную строку. Для APP ID, скажем ‘app001'.
Откройте index.php и измените его:
'28e336ac6c9423d946ba02d19c6a2632', //randomly generated app key
);
//include our models
include_once 'models/TodoItem.php';

//wrap the whole thing in a try-catch block to catch any wayward exceptions!
try {
//*UPDATED*
//get the encrypted request
$enc_request = $_REQUEST['enc_request'];

//get the provided app id
$app_id = $_REQUEST['app_id'];

//check first if the app id exists in the list of applications
if( !isset($applications[$app_id]) ) {
throw new Exception('Application does not exist!');
}

//decrypt the request
$params = json_decode(trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $applications[$app_id], base64_decode($enc_request), MCRYPT_MODE_ECB)));

//check if the request is valid by checking if it's an array and looking for the controller and action
if( $params == false || isset($params->controller) == false || isset($params->action) == false ) {
throw new Exception('Request is not valid');
}

//cast it into an array
$params = (array) $params;
...
...
...

Мы сделали простую реализацию очень простого способа проверки подлинности пользователей, используя аутентификацию public-private key. Вот как это выглядит:Делается вызов api, в нем предоставляется $ APP_ID и $ enc_request

$ enc_request содержит параметры вызова API, зашифрованные с помощью APP KEY. APP KEY никогда не отправляется на сервер, он используется только для шифрования запроса. Кстати, запрос может быть расшифрован с помощью APP KEY .

Когда вызов доходит до сервера, то сервер проверяет свой собственный список приложений на наличие APP ID.

Когда он его находит, api server пытается расшифровать запрос используя ключ.

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

Шаг 4. Создание front end
Давайте создадим новую директорию simpletodo_client_browser.
И index.php:

SimpleTODO

body {
padding-top: 40px;
}
#main {
margin-top: 80px;
text-align: center;
}

Это должно выглядеть так:
Кстати говоря мы включили 2 js и 1 css файловbootstrap.min.css is the Twitter Bootstrap

jquery.min.js is последняя jQuery библиотека

jquery-ui-1.8.16.custom.min.js последняя jQuery UI библиотека

Далее создадим login.php для сохранения логина и пароля внутри сессии:
<?php
//get the form values
$username = $_POST['login_username'];
$userpass = $_POST['login_password'];

session_start();
$_SESSION['username'] = $username;
$_SESSION['userpass'] = $userpass;
header('Location: todo.php');
exit();

Здесь мы просто создаём новую сессию пользователя, основанную на введённом логине и пароле. Это действие является простой комбинацией ключа, по которому мы можем получить доступ к хранящимся TODO спискам. Далее перенаправление на todo.php для начала взаимодействия с api server’ом. Прежде чем создать todo.php давайте сначала определим класс ApiCaller, который инкапсулирует все методы вызова API, которые нам понадобится, включая шифрование запросов.
Создаём apicaller.php:
_app_id = $app_id;
$this->_app_key = $app_key;
$this->_api_url = $api_url;
}

//send the request to the API server
//also encrypts the request, then checks
//if the results are valid
public function sendRequest($request_params)
{
//encrypt the request parameters
$enc_request = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->_app_key, json_encode($request_params), MCRYPT_MODE_ECB));

//create the params array, which will
//be the POST parameters
$params = array();
$params['enc_request'] = $enc_request;
$params['app_id'] = $this->_app_id;

//initialize and setup the curl handler
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->_api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, count($params));
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);

//execute the request
$result = curl_exec($ch);

//json_decode the result
$result = @json_decode($result);

//check if we're able to json_decode the result correctly
if( $result == false || isset($result['success']) == false ) {
throw new Exception('Request was not correct');
}

//if there was an error in the request, throw an exception
if( $result['success'] == false ) {
throw new Exception($result['errormsg']);
}

//if everything went great, return the data
return $result['data'];
}
}

Мы будем использовать класс ApiCaller для отправки запросов к нашему API серверу. Таким образом, все необходимое шифрование и инициализация CURL будет в одном месте, а так же не будет повторения кода.
Конструктор содержит 3 параметра:$ app_id — APP ID для клиента (APP001)

$ app_key — APP KEY для клиента (28e336ac6c9423d946ba02d19c6a2632)

$ api_url — URL сервера API, localhost/simpletodo_api/

А функция sendRequest():шифрует параметры запроса, используя библиотеку mcrypt в том же порядке, что сервер API расшифровывает его

генерирует $_POST параметры, которые будут отправлены на api server

выполняет вызов API через CURL

проверяет результат вызова (успешен или нет)

возвращает данные, когда всё было успешно отправлено.

Сейчас, давайте создадим todo.php. А именно создадим код который возвратит list of todo items для юзера anton и пароля habr_hello.
sendRequest(array(
'controller' => 'todo',
'action' => 'read',
'username' => $_SESSION['username'],
'userpass' => $_SESSION['userpass']
));

echo '';
var_dump($todo_items);

Заходим на главную и логинимся под anton и habr_hello и вы должны увидеть результат var_dump’a. Поздравляю! Вы успешно выполнили api запрос к api server’y. В этом коде мы:создали сессию

создали экземпляр класса ApiCaller, передавая app id, app key в todo.php.

Давайте изменим нашу страничку добавив некоторых возможностей.
Кстати не забудьте удалить вар дамп ^_^.

SimpleTODO

body {
padding-top: 40px;
}
#main {
margin-top: 80px;
}

.textalignright {
text-align: right;
}

.marginbottom10 {
margin-bottom: 10px;
}
#newtodo_window {
text-align: left;
display: none;
}

$(document).ready(function() {
$("#todolist").accordion({
collapsible: true
});
$(".datepicker").datepicker();
$('#newtodo_window').dialog({
autoOpen: false,
height: 'auto',
width: 'auto',
modal: true
});
$('#newtodo').click(function() {
$('#newtodo_window').dialog('open');
});
});

Create a new TODO item

Title:

Date Due:

Description:

title; ?>

<a href="delete_todo.php?todo_id=todo_id; ?>">Delete

Date Due:
<input type="text" id="datepicker_todo_id; ?>" class="datepicker" name="due_date" value="12/09/2011" />

Description:
<textarea class="span8" id="description_todo_id; ?>" class="description" name="description">description; ?>

is_done == 'false' ): ?>

<input type="hidden" value="todo_id; ?>" name="todo_id" />
<input type="hidden" value="title; ?>" name="title" />

Это должно выглядеть так:
Приятно смотрится, неправда ли? Добавим теперь той самой функциональности, new_todo.php
Который содержит вызов todo/create, создавая новый TODO item элемент. Создание других страниц: (update_todo.php и delete_todo.php) должно быть простым.
Откроем new_todo.php:
sendRequest(array(
'controller' => 'todo',
'action' => 'create',
'title' => $_POST['title'],
'due_date' => $_POST['due_date'],
'description' => $_POST['description'],
'username' => $_SESSION['username'],
'userpass' => $_SESSION['userpass']
));
header('Location: todo.php');
exit();
?>

Как вы видете new_todo.php использует ApiCaller для отправки todo/create запроса к API server’y.стартует сессия

инициализация ApiCaller класса

отправка запроса с помощью sendRequest()

редирект на todo.php

Поздравляю! Мы успешно создали API-centric web application.Заключение:
Есть много преимуществ в разработке приложений, сделанных с API. Если вы хотите создать Android версию SimpleTODO, то вся функциональность, которая может вам потребоваться, уже есть в API server, так что все, что вам нужно сделать, это просто создать Android клиент! Хотите, реорганизовать или оптимизировать некоторые классы? Нет проблем — просто убедитесь, что выходные данные будут теми же. Нужно добавить больше функциональности? Вы можете сделать это без изменения кода клиента!
Вдохновением служила статья nettuts
Пишите если где ошибся, и комментарии по поводу где что лучше можно было бы сделать


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js