Планируете начать работу над новым веб-приложением? В этом уроке мы рассмотрим, как создать API-ориентированное веб-приложение, и объясним, почему это необходимо в современном мульти-платформенном мире .
Введение
API?
Для тех, кто не знаком с термином, API является сокращением от Application Programming Interface(Интерфейс прикладного программирования). Согласно Википедии :
Интерфейс программирования приложений (англ. application programming interface) — набор готовых классов, процедур, функций, структур и констант, предоставляемых приложением (библиотекой, сервисом) для использования во внешних программных продуктах. Используется программистами для написания всевозможных приложений.
Визуализация API
Изображение предоставлено https://blog.zoho.com
Проще говоря, API ссылается на набор функций, встроенных в приложения, которые могут быть использованы другими приложениями (или сам по себе, как мы увидим позже), чтобы взаимодействовать с приложением. API является отличным способом надежно и безопасно раскрыть функциональность приложения для внешних приложений. Все функциональные возможности, доступные внешним приложениям, ограничиваются предоставляемым API.
Что такое API-ориентированное веб-приложение?
API-ориентированное веб-приложение - это веб-приложение, в котором весь или большую часть функционала реализуется через вызовы API. Например, если вы должны авторизоваться, то вы отправляете свои данные через функцию API. Результатом выполнения этой функции будет либо авторизация, либо сообщение об ошибке.
Другой характерной особенностью API-ориентированного веб-приложения является то, что API не зависит от состояния пользователя. Это ограничение на самом деле хорошее - это «заставляет» разработчика создавать API, который не зависит от текущего состояния пользователя, а представляет собой чистую функциональность, что, в свою очередь, облегчает тестирование, так как текущее состояние пользователя не нужно имитировать.
Зачем все это нужно?
Время не стоит на месте. Общеизвестно, что люди сегодня используют приложения не только через браузер, но и с помощью других устройств, например мобильных телефонов и планшетов. Например, в статьe на Mashable “Consumers Now Spending More Time on Mobile Apps Than the Web” , говорится:
Потребители впервые начали тратить больше времени на мобильныe приложения, чем на веб-серфинг.
Flurry сравнивали свои данные статистики по мобильным приложениям от ComScore и Алекса, и обнаружили, что в июне пользователи потратили 81 минуту в день на мобильные приложения и только 74 минуты веб-серфинг.
Вот статья ReadWriteWeb "More People Browse On Mobile Than Use IE6 & IE7 Combined" :
Последние данные о тенденциях браузеров от Sitepoint показывают, что количество людей, просматриваюших веб-страницы на смартфонах больше количества пользователей Internet Explorer 6 и 7 вместе взятых. Эти два старых драндулета были комком геморроя для веб-разработчиков в течение многих лет, требуя разрабатывать деградацию для сайтов. Но все изменилось сейчас; 6,95% веб-активности в ноябре 2011 года была на мобильных браузерах, и только 6,49% было на IE 6 или 7.
Очевидно, все больше и больше людей получают новости нанапрямую с сайтов а, в частности, с мобильных устройств.
Какое это имеет отношение к созданию API-ориентированного веб-приложения?
Это неизбежно приведет к более частому использованию нашего приложения, так как он может использоваться на любом устройстве и платформе.
Одним из основных преимуществ создания API-ориентированных веб-приложений, является то, что это поможет вам построить функциональность, которая может быть использована любым устройством, будь то браузер, мобильный телефон, планшет или даже desktop приложение. Все, что нужно сделать, это создать API, таким образом, чтобы все эти устройства могли взаимодействовать с ним, и вуаля! Вы создали централизованное приложение, которое может принимать вызовы с любого устройства, которое есть у пользователя!
API-ориентированное веб-приложение
Создавая приложения таким образом, мы можем легко воспользоваться им в любой среде. Это неизбежно приведет к более частому использованию приложения, так как он может использоваться везде.
Вот статья о редизайне сайта Твиттера , в которой рассказыватеся о том, как они теперь используют их API на сайте Twitter.com, по сути делая его API-ориентированным:
Одним из самых значительных архитектурных изменений является то, что Twitter.com сейчас является пользователем нашего собственного API. Он извлекает данные так же как и мобильный сайт, наши приложения для iPhone, iPad, Android, и любое другое приложение стороннего использования. Это изменение позволило нам выделить больше ресурсов для команды API. При первоначальной загрузки страницы и при каждом запросе клиента, все данные теперь извлекаются из оптимизированного кэша.
В этом уроке мы будем создавать простое TODO приложение и клиент, который взаимодействует с нашими TODO приложением. По окончанию урока, вы будете знать основные составные части API-ориентированного веб-приложения, а также способы их безопасного взаимодействия. Давайте начнем!
Шаг 1: Планирование функциональности приложения
В TODO приложении, которое мы будем делать будут реализованы основные CRUD функции:
- Создание TODO элементов
- Чтение TODO элементов
- Обновление TODO элементов (переименование, отметить как "выполнено", пометить как "не выполнено")
- Удаление TODO элементов
Каждый пункт будет иметь:
- Название
- Дата окончания
- Описание
- Флаг, означающий, что дело сделано
Давайте нарисуем макет разрабатываемого нами приложения:
SimpleTODO Макет
Шаг 2: Создайте Server API
Так как мы разрабатываем API-ориентированное веб-приложение, мы будем создавать две «проекта»: Server API, а клиент. Давайте начнем с создания сервера API.
В папке вашего веб-сервера создайте папку с именем simpletodo_api
, с файлом index.php
. Это файл будет выступать в качестве фронт-контроллера для API, так что все запросы к серверу API будет осуществляться через этот файл. Откройте его и поместите следующий код:
<?php // Define path to data folder define('DATA_PATH', realpath(dirname(__FILE__).'/data')); //include our models include_once 'models/TodoItem.php'; //wrap the whole thing in a try-catch block to catch any wayward exceptions! try { //get all of the parameters in the POST/GET request $params = $_REQUEST; //get the controller and format it correctly so the first //letter is always capitalized $controller = ucfirst(strtolower($params['controller'])); //get the action and format it correctly so all the //letters are not capitalized, and append 'Action' $action = strtolower($params['action']).'Action'; //check if the controller exists. if not, throw an exception if( file_exists("controllers/{$controller}.php") ) { include_once "controllers/{$controller}.php"; } else { throw new Exception('Controller is invalid.'); } //create a new instance of the controller, and pass //it the parameters from the request $controller = new $controller($params); //check if the action exists in the controller. if not, throw an exception. if( method_exists($controller, $action) === false ) { throw new Exception('Action is invalid.'); } //execute the action $result['data'] = $controller->$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();
Это контроллер фронт, который выполняет следующие действия:
- Принимает вызовы API с любым числом параметров
- Извлекает
Controller
иAction
для вызова API - Проводит необходимые проверки, чтобы убедиться, что
Controller
иAction
существуют - Выполняет вызов API
- Отлавливает ошибки, если таковые имеются
- Отправляет результат пользователю
Помимо файла index.php
, создайте три папки: controllers, models и data .
- Папка controllers будет содержать все контроллеры, которые мы будем использовать для сервера API. Мы будем создавать их, используя архитектуру MVC , чтобы сделать структуру сервера API более чистой и структурированной.
- Папка models будет содержать все модели данных для сервера API.
- Папка data создана для хранения любых данных.
Перейти в папку контроллеров и создает файл под названием Todo.php
. Это будет наш контроллер для решения задач, связанных с TODO списками. Создаем заготовку класса и функций для нашего приложения:
<?php class Todo { private $_params; public function __construct($params) { $this->_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
. Я приведу код для методаcreateAction
. За вами остается написание кода для других методов. Если вы не в настроении, вы можете просто скачать исходный код демо.
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
внутри папки models
, чтобы мы могли создать код, реализующий "создание пункта списка". Примите к сведению, что я не будут подключаться к базе данных, я буду сохранения информацию в файлы. Он должен быть относительно легко.
<?php class TodoItem { public $todo_id; public $title; public $description; public $due_date; public $is_done; public function save($username, $userpass) { //get the username/password hash $userhash = sha1("{$username}_{$userpass}"); if( is_dir(DATA_PATH."/{$userhash}") === false ) { mkdir(DATA_PATH."/{$userhash}"); } //if the $todo_id isn't set yet, it means we need to create a new todo item if( is_null($this->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
метод вызывает две функции моделиTodoItem
:
- save() - сохранение
TodoItem
в файл, а также при необходимости создаетtodo_id
дляTodoItem
. - ToArray () - возвращает
TodoItem
в виде массива, индексами которого служат названия переменных.
Поскольку API вызывается через запросы HTTP, давайте проверим API-вызовов, вызвав его через браузер:
https://localhost/simpletodo_api/?controller=todo&action=create&title=test%20title&description=test%20description&due_date=12/08/2011&username=nikko&userpass=test1234
Если все работает, вы должны увидеть новую папку внутри папки data
, и внутри этой папки, Вы должны увидеть файл со следующим содержанием:
createAction()
результат
Поздравляем! Вы успешно создали сервер API и сделали вызов API!
Шаг 3: Обеспечение безопасности API сервера при помощи APP ID
и APP SECRET
В настоящее время сервер API готов принять все API запросы. Нам нужно дать использовать его только нашим собственным приложениям, чтобы гарантировать, что только наши собственные клиенты имеют возможность сделать API запросы. Кроме того, вы можете создать систему, в которой пользователи могут создавать свои собственные приложения, которые имеют доступ к API сервера, подобно тому, как работают приложения для Facebook и Twtter.
Начнем с создания пары id-ключ для клиентов, которые будут использовать сервер API. Так как это всего лишь демо, мы можем использовать любую случайную, 32 символьную строку. Для APP ID
, примем что это приложение APP001.
Откройте файл index.php еще раз, а затем обновите его следующим кодом:
<?php // Define path to data folder define('DATA_PATH', realpath(dirname(__FILE__).'/data')); //Define our id-key pairs $applications = array( 'APP001' => '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; ... ... ...
Мы только что сделали простую реализацию очень простого способа проверки подлинности наших пользователей, используя аутентификации, похожую на систему публичного и личного ключа . Вот пошаговое описание этого процесса:
Шифрование с открытым ключом
- API вызов сделан, в нем предоставляется $ APP_ID и $ enc_request .
- $ enc_request содержит параметры вызова API, зашифрованные с помощью
APP KEY
.APP KEY
никогда не отправляется на сервер, он используется только для шифрования запроса. Кроме того, запрос может быть расшифровано только с помощьюAPP KEY
. - Когда вызов API приходит на сервер API, то сервер проверять свой собственный список приложений на наличие
APP ID
. - При такое приложение обнаружено, то сервер API пытается расшифровать запрос, используя ключ, который соответствует
APP ID
. - Если она расшифровка была успешной, то продолжаем работать с программой.
Теперь, когда сервер API защищен APP ID
и APP SECRET
, мы можем приступать к программированию тонкого клиента.
Шаг 4: Создание тонкого клиента
Мы начнем с создания новой папки для тонкого клиента. Создайте папку с именемsimpletodo_client_browser
в папке веб-сервера. После этого, создайте файл index.php и поместите этот код в него:
<!DOCTYPE html> <html> <head> <title>SimpleTODO</title> <link rel="stylesheet" href="css/reset.css" type="text/css" /> <link rel="stylesheet" href="css/bootstrap.min.css" type="text/css" /> <script src="js/jquery.min.js"></script> <script src="js/jquery-ui-1.8.16.custom.min.js"></script> <style> body { padding-top: 40px; } #main { margin-top: 80px; text-align: center; } </style> </head> <body> <div class="topbar"> <div class="fill"> <div class="container"> <a class="brand" href="index.php">SimpleTODO</a> </div> </div> </div> <div id="main" class="container"> <form class="form-stacked" method="POST" action="login.php"> <div class="row"> <div class="span5 offset5"> <label for="login_username">Username:</label> <input type="text" id="login_username" name="login_username" placeholder="username" /> <label for="login_password">Password:</label> <input type="password" id="login_password" name="login_password" placeholder="password" /> </div> </div> <div class="actions"> <button type="submit" name="login_submit" class="btn primary large">Login or Register</button> </div> </form> </div> </body> </html>
Это должно выглядеть примерно так:
SimpleTODO страница авторизации
Обратите внимание, что я включил 2 JS и 2 CSS файла:
- reset.css стандартный CSS Reset.
- bootstrap.min.css это Twitter Bootstrap
- jquery.min.js последняя версия 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 сервером. Прежде чем мы начнем писать код в файл todo.php
, давайте сначала создадим класс ApiCaller, который будет инкапсулировать все методы вызова API, которые нам понадобится, включая шифрование запросов.
Создайте apicaller.php
и скопируйте следующий код:
<?php class ApiCaller { //some variables for the object private $_app_id; private $_app_key; private $_api_url; //construct an ApiCaller object, taking an //APP ID, APP KEY and API URL parameter public function __construct($app_id, $app_key, $api_url) { $this->_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 будет в одном месте, и мы не должны повторять наш код.
-
__construct
функция принимает три параметра:- $ APP_ID -
APP ID
для клиента (который APP001 для браузера) - $ App_key -
APP KEY
для клиента (28e336ac6c9423d946ba02d19c6a2632 для браузера ) - $ Api_url - URL сервера API,
https://localhost/simpletodo_api/
- $ APP_ID -
- функция
sendRequest()
:- шифрует параметры запроса, используя библиотеку
mcrypt
в том же порядке, что сервер API расшифровывает его - генерирует параметры
$_POST
, которые будут отправлены на сервер API - выполняет вызов API через CURL
- проверяет результат вызова API был успешным или нет
- возвращает данные, когда все прошло успешно
- шифрует параметры запроса, используя библиотеку
Теперь, давайте разберемся со страницей todo.php
. Во-первых, давайте создадим какой-то код, чтобы получить текущий список TODO для пользователя nikko
с паролем test1234
(эту комбинацию имени пользователя / пароля мы использовали ранее для тестирования сервера API).
<?php session_start(); include_once 'apicaller.php'; $apicaller = new ApiCaller('APP001', '28e336ac6c9423d946ba02d19c6a2632', 'https://localhost/simpletodo_api/'); $todo_items = $apicaller->sendRequest(array( 'controller' => 'todo', 'action' => 'read', 'username' => $_SESSION['username'], 'userpass' => $_SESSION['userpass'] )); echo ''; var_dump($todo_items);
Вернитесь к файлу index.php
, войдите как nikko/test1234, и вы должны увидеть var_dump()
из TODO, который мы создали ранее.
Поздравляю, вы успешно сделали вызов API к серверу API! В этом коде мы:
- создали сессию, таким образом мы имеем доступ к
username
иuserpass
в$_SESSION
- экземпляр класса
ApiCaller
, передавая емуAPP ID
,APP KEY
и URL сервера API - отправили запрос через метод
sendRequest()
Теперь, давайте переформатируем данные, чтобы они выглядели лучше. Добавьте следующий HTML-код в todo.php
. Не забудьте удалить var_dump()
!
<!DOCTYPE html> <html> <head> <title>SimpleTODO</title> <link rel="stylesheet" href="css/reset.css" type="text/css" /> <link rel="stylesheet" href="css/bootstrap.min.css" type="text/css" /> <link rel="stylesheet" href="css/flick/jquery-ui-1.8.16.custom.css" type="text/css" /> <script src="js/jquery.min.js"></script> <script src="js/jquery-ui-1.8.16.custom.min.js"></script> <style> body { padding-top: 40px; } #main { margin-top: 80px; } .textalignright { text-align: right; } .marginbottom10 { margin-bottom: 10px; } #newtodo_window { text-align: left; display: none; } </style> <script> $(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'); }); }); </script> </head> <body> <div class="topbar"> <div class="fill"> <div class="container"> <a class="brand" href="index.php">SimpleTODO</a> </div> </div> </div> <div id="main" class="container"> <div class="textalignright marginbottom10"> <span id="newtodo" class="btn info">Create a new TODO item</span> <div id="newtodo_window" title="Create a new TODO item"> <form method="POST" action="new_todo.php"> <p>Title:<br /><input type="text" class="title" name="title" placeholder="TODO title" /></p> <p>Date Due:<br /><input type="text" class="datepicker" name="due_date" placeholder="MM/DD/YYYY" /></p> <p>Description:<br /><textarea class="description" name="description"></textarea></p> <div class="actions"> <input type="submit" value="Create" name="new_submit" class="btn primary" /> </div> </form> </div> </div> <div id="todolist"> <?php foreach($todo_items as $todo): ?> <h3><a href="#"><?php echo $todo->title; ?></a></h3> <div> <form method="POST" action="update_todo.php"> <div class="textalignright"> <a href="delete_todo.php?todo_id=<?php echo $todo->todo_id; ?>">Delete</a> </div> <div> <p>Date Due:<br /><input type="text" id="datepicker_<?php echo $todo->todo_id; ?>" class="datepicker" name="due_date" value="12/09/2011" /></p> <p>Description:<br /><textarea class="span8" id="description_<?php echo $todo->todo_id; ?>" class="description" name="description"><?php echo $todo->description; ?></textarea></p> </div> <div class="textalignright"> <?php if( $todo->is_done == 'false' ): ?> <input type="hidden" value="false" name="is_done" /> <input type="submit" class="btn" value="Mark as Done?" name="markasdone_button" /> <?php else: ?> <input type="hidden" value="true" name="is_done" /> <input type="button" class="btn success" value="Done!" name="done_button" /> <?php endif; ?> <input type="hidden" value="<?php echo $todo->todo_id; ?>" name="todo_id" /> <input type="hidden" value="<?php echo $todo->title; ?>" name="title" /> <input type="submit" class="btn primary" value="Save Changes" name="update_button" /> </div> </form> </div> <?php endforeach; ?> </div> </div> </body> </html>
Это должно выглядеть примерно так:
Довольно прикольно, да? Но это в настоящее время ничего не делает, так что давайте добавим некоторые функциональные возможности. Я приведу код для new_todo.php
, который будет вызывать todo/create
вызов API , чтобы создать новый пункт TODO. Создание других страниц (update_todo.php
и delete_todo.php
) должно быть очень похоже на создание этой страницы, так что я оставлю это до Вас. Откройте new_todo.php
и добавить следующий код:
<?php session_start(); include_once 'apicaller.php'; $apicaller = new ApiCaller('APP001', '28e336ac6c9423d946ba02d19c6a2632', 'https://localhost/simpletodo_api/'); $new_item = $apicaller->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.
Поздравляем, это работает! Вы успешно создали API-ориентированное веб-приложение!
Заключение
Есть много преимуществ в разработке приложений, строящихся вокруг своего API. Хотите создать Android версию применения SimpleTODO? Все функциональность, которая может вам потребоваться, уже есть в сервере API, так что все, что вам нужно сделать, это просто создать Android клиент! Хотите, реорганизовать или оптимизировать некоторые классы? Нет проблем - просто убедитесь, что выходные данные будут теми же. Нужно добавить больше функциональности? Вы можете сделать это без изменения кода клиента!
Хотя есть и некоторые недостатки, какие как более долгая разработка и сложность. Но преимущества перевешивают недостатки.
Планируете ли вы использовать сервер API для вашего следующего веб-приложения, или вы уже использовали ту же самую технику для проекта в прошлом? Дайте мне знать в комментариях!
Оригинал статьи: https://net.tutsplus.com/tutorials/php/creating-an-api-centric-web-application/
0 Комментариев