Vytvoření RESTful API v PHP. Jak pracovat s VKontakte API Vytvoření vlastního API

Není to tak dávno, co se mě jeden z mých návštěvníků na něco zeptal e-mailem: "". Rozhodl jsem se, že by to bylo velmi užitečné pro ostatní uživatele, zejména proto, že navzdory zjevné složitosti procesu je vše velmi, velmi jednoduché. Stačí mít ty nejzákladnější znalosti PHP.

Pokud najednou nerozumíte, o čem mluvíme, přečtěte si nejprve článek:. Pokračuj. Podívejme se, na jaké weby jsou potřeba? API:

První věc, kterou musíte pochopit, je to API Ne každý web to potřebuje (i když patří do jedné z výše uvedených skupin).

Pokud si to myslíte API je na vašem webu nezbytný, pak se podívejme na příklad, jak se vytváří. Mějme následující úkol: existuje EPS (jako např. WebMoney). A my chceme, aby uživatel mohl ze svého kódu pomocí našeho API, zjistěte zůstatek na svém účtu.

Vytvoříme soubor (např. api.php), které obdržíme požadavky GET od uživatelů získat různé informace. Zapišme následující kód do tohoto handleru:

if ($_GET["action"] == "getbalance") (
$zůstatek;
//Zjistit stav účtu z databáze a zapsat jej do proměnné zůstatek
echo $balance;
}
?>

Nyní vývojáři API by měl uživatelům poskytnout informace o tom, jak podat žádost, aby uživatel mohl zjistit zůstatek na svém účtu:

Http://mysite.ru/api.php?action=getbalance&key=fa9sgwlgjs9gdsjlgjdsjglsdlgs

Uživatelé tvoří tento požadavek ve svých skriptech (například prostřednictvím kučera). Parametr klíč je jedinečný klíč pro každého uživatele. A odpovědí na tento požadavek bude číslo zodpovědné za zůstatek uživatele. Všechny ostatní možnosti jsou vytvořeny stejným způsobem. API. Můžete přidat různé další parametry: například získat seznam transakcí doplňování účtu z jednoho data do druhého. Samotné seznamy je vhodné vrátit ve formátu JSON.

Při vývoji projektu jsem se potýkal s potřebou zorganizovat interakci klient-server mezi aplikacemi na platformách iOS a Android s mým webem, na kterém byly uloženy veškeré informace – vlastní databáze mysql, obrázky, soubory a další obsah.
Problémy, které bylo třeba vyřešit, byly poměrně jednoduché:
registrace/autorizace uživatele;
odesílání/příjem určitých údajů (například seznamu produktů).

A pak jsem chtěl napsat své vlastní API pro interakci se serverovou stranou – většinou pro praktické účely.

Vstupní data

Měl jsem k dispozici:
Server – Apache, PHP 5.0, MySQL 5.0
Klient – ​​zařízení Android, iOS, libovolný prohlížeč

Rozhodl jsem se, že pro požadavky na server a odpovědi z něj budu používat datový formát JSON - pro jeho jednoduchost a nativní podporu v PHP a Androidu. Zde mě iOS naštval - nemá nativní podporu JSON (zde jsem musel použít vývoj třetích stran).

Bylo také rozhodnuto, že požadavky lze zasílat jak prostřednictvím požadavků GET, tak POST (zde pomohl $_REQUEST v PHP). Toto řešení umožnilo testovat API prostřednictvím požadavků GET v libovolném dostupném prohlížeči.

Bylo rozhodnuto, že vzhled dotazů bude vypadat takto:
http://[adresa serveru]/[cesta ke složce api]/?[název_api].[název_metody]=

Cesta ke složce api je adresář, do kterého je potřeba zadávat požadavky, v jehož kořeni leží soubor index.php – ten je zodpovědný za volání funkcí a obsluhu chyb
Název api - pro pohodlí jsem se rozhodl oddělit skupiny API - uživatel, databáze, obsah atd. V tomto případě má každé API svůj vlastní název
Název metody – název metody, kterou je třeba volat v zadaném rozhraní API
JSON - řetězcová reprezentace objektu JSON pro parametry metody

API kostry

Kostra API na straně serveru se skládá z několika základních tříd:
index.php - indexový soubor adresáře v Apache; obsahuje všechna volání API, analyzuje parametry a volá metody API
MySQLiWorker – jediná třída pro práci s databází MySQL přes MySQLi
apiBaseCalss.php je nadřazená třída pro všechna rozhraní API v systému – každé rozhraní API musí zdědit z této třídy, aby správně fungovalo
apiEngine.php - hlavní třída systému - analyzuje předané parametry (po jejich předběžné analýze v index.php), připojí požadovanou api třídu (prostřednictvím metody require_once), zavolá v ní požadovanou metodu a vrátí výsledek v JSON formát
apiConstants.php - třída s konstantami pro volání api a přenos chyb
apitest.php - otestujte api pro testování nových metod před jejich zařazením do produkční verze

Celý mechanismus vypadá takto:
Odešleme požadavek na server – například www.example.com/api/?apitest.helloWorld= ()
Na straně serveru analyzuje soubor index.php předané parametry. Index.php vždy vezme pouze první prvek ze seznamu předávaných parametrů $_REQUEST – to znamená, že konstrukce jako www.example.com/api/?apitest.helloWorld= ()&apitest.helloWorld2 – zavolá pouze metodu helloWorld v apitest. Metoda helloWorld2 nebude volána

Nyní více podrobností o každé z nich

Snažil jsem se soubory dostatečně zdokumentovat, aby nezabíraly mnoho místa pro text. Nicméně v těch souborech, kde nejsou žádné komentáře, přesto poskytnu popis.

Index.php

Jak jsem řekl dříve, toto je vstupní indexový soubor pro Apache, což znamená, že bude přijímat všechna volání jako www.example.com/api.

0)( required_once "apiEngine.php"; foreach ($_REQUEST jako $apiFunctionName => $apiFunctionParams) ( $APIEngine=new APIEngine($apiFunctionName,$apiFunctionParams); echo $APIEngine->call)else;Function)) ( $jsonError->error="Není volána žádná funkce"; echo json_encode($jsonError); ) ?>

V první řadě nastavíme typ obsahu - text/html (ten pak můžete změnit v samotných metodách) a kódování - UTF-8.
Dále zkontrolujeme, že nás o něco žádají. Pokud ne, vypíšeme JSON s chybou.
Pokud existují parametry požadavku, pak připojíme soubor API motoru - apiEngine.php a vytvoříme třídu motoru s předanými parametry a zavoláme metodu api.
Opustíme smyčku, protože jsme se rozhodli, že zpracujeme pouze jeden hovor.

apiEngine.php

Druhou nejdůležitější třídou je třída apiEngine – je to engine pro volání api a jejich metod.
apiFunctionParams = stripcslashes($apiFunctionParams); //Analyzujte pole dvou prvků - název API, - název metody v API $this->apiFunctionName = explode("_", $apiFunctionName); ) //Vytvoření funkce odezvy JSON createDefaultJson() ( $retObject = json_decode("()"); $response = APIConstants::$RESPONSE; $retObject->$response = json_decode("()"); return $retObject ; ). register if (file_exists($apiName . ".php")) ( $apiClass = APIEngine::getApiEngineByName($apiName);//Získání objektu API $apiReflection = new ReflectionClass($apiName);//Through reflection získáme informace o objektu třídy try ( $functionName = $this->apiFunctionName;//Název metody pro volání $apiReflection->getMethod($functionName);//Zkontrolujte přítomnost metody $response = APIConstants ::$RESPONSE; $jsonParams = json_decode($this- >apiFunctionParams);//Dekódujte parametry požadavku do objektu JSON if ($jsonParams) ( if (isset($jsonParams->responseBinary))(//Aby bylo možné nevrací JSON, ale binární data jako zip, png a další obsah vrací $apiClass->$functionName($jsonParams);//Volání metody v rozhraní API )else( $resultFunctionCall->$response = $apiClass->$ functionName($jsonParams);//Volání metody v API, která vrátí objekt JSON) ) else ( //Pokud dojde k chybě při dekódování parametrů požadavku JSON $resultFunctionCall->errno = APIConstants::$ERROR_ENGINE_PARAMS; $resultFunctionCall->error = "Chyba dané parametry"; ) ) catch (Výjimka $ex) ( //Neočekávaná výjimka $resultFunctionCall->error = $ex->getMessage(); ) ) else ( //Pokud nebylo požadované API nalezeno $resultFunctionCall->errno = APIConstants::$ ERROR_ENGINE_PARAMS; $resultFunctionCall->error = "Soubor nenalezen"; $resultFunctionCall->REQUEST = $_REQUEST; ) return json_encode($resultFunctionCall); )) ?>

apiConstants.php

Tato třída se používá pouze k ukládání konstant.

MySQLiWorker.php

Jediná třída pro práci s databází. Jinak se jedná o obyčejného samotáře - takových příkladů je na internetu spousta.

dbName = $dbName; self::$instance->dbHost = $dbHost; self::$instance->dbUser = $dbUser; self::$instance->dbPassword = $dbPassword; self::$instance->openConnection(); ) return self::$instance; ) //Určete typy parametrů dotazu do databáze a vraťte řetězec pro vazbu přes ->bind function PrepareParams($params) ( $retSTMTString = ""; foreach ($params jako $value) (​if (is_int( $value) || is_double ($value)) ( $retSTMTString.="d"; ) if (is_string($value)) ( $retSTMTString.="s"; ) ) return $retSTMTString; ) //Připojte se k veřejná funkce databáze openConnection() ( if (is_null($this->connectLink)) ( $this->connectLink = new mysqli($this->dbHost, $this->dbUser, $this->dbPassword, $this-> dbName); $this-> connectLink->query("SET NAMES utf8"); if (mysqli_connect_errno()) ( printf("Připojení není možné: %s\n", mysqli_connect_error()); $this->connectLink = null; ) else ( mysqli_report(MYSQLI_REPORT_ERROR ); ) ) return $this->connectLink; ) //Uzavření připojení k databázi veřejné funkce closeConnection() ( if (!is_null($this->connectLink)) ( $this- >connectLink->close(); ) ) //Převede odpověď na veřejnou funkci asociativního pole stmt_bind_assoc(&$stmt, &$out) ( $data = mysqli_stmt_result_metadata($stmt); $pole = pole(); $out = pole(); $pole = $stmt; $počet = 1; $currentTable = ""; while ($field = mysqli_fetch_field($data)) ( if (strlen($currentTable) == 0) ( $currentTable = $field->table; ) $fields[$count] = &$out[$field->name ]; $count++; ) call_user_func_array("mysqli_stmt_bind_result", $fields); )) ?>

apiBaseClass.php

No, dostáváme se k jedné z nejdůležitějších tříd systému – základní třídě pro všechna API v systému.

mySQLWorker = MySQLiWorker::getInstance($dbName,$dbHost,$dbUser,$dbPassword); ) ) funkce __destruct() ( if (isset($this->mySQLWorker))( //Pokud bylo navázáno připojení k databázi, $this->mySQLWorker->closeConnection(); //poté ji zavřete, když je naše třída již není potřeba ) ) //Vytvořte výchozí JSON pro funkci odpovědí createDefaultJson() ( $retObject = json_decode("()"); return $retObject; ) //Vyplňte objekt JSON odpovědí z funkce MySQLiWorker fillJSON(&$ jsonObject, &$stmt, &$mySQLWorker) ( $row = array(); $mySQLWorker->stmt_bind_assoc($stmt, $row); while ($stmt->fetch()) ( foreach ($row jako $key = > $value) ( ​​​$ klíč = strtolower($key); $jsonObject->$key = $hodnota; ) break; ) return $jsonObject; ) ) ?>

Jak můžete vidět, tato třída obsahuje několik „utilitních“ metod, jako například:
konstruktor, ve kterém se provádí připojení k databázi, pokud bude aktuální API pracovat s databází;
destruktor - hlídá uvolňování zdrojů - přerušení navázaného spojení s databází
createDefaultJson - vytvoří výchozí JSON pro odpověď metody
fillJSON - pokud se předpokládá, že požadavek vrátí pouze jeden záznam, pak tato metoda naplní JSON pro odpověď daty z prvního řádku odpovědi z databáze

Pojďme si vytvořit vlastní API

To je vlastně celá páteř tohoto API. Nyní se podíváme, jak toto vše využít na příkladu vytvoření prvního API s názvem apitest. A napíšeme si do něj pár jednoduchých funkcí:
jeden bez parametrů
jeden s parametry a ona nám je vrátí, aby bylo jasné, že je přečetla
ten, který nám vrátí binární data

A tak vytvoříme třídu apitest.php s následujícím obsahem

createDefaultJson(); $retJSON->withoutParams = "Je to volána metoda bez parametrů"; return $retJSON; ) //http://www.example.com/api/?apitest.helloAPIWithParams=("TestParamOne":"Text prvního parametr") funkce helloAPIWithParams($apiMethodParams) ( $retJSON = $this->createDefaultJson(); if (isset($apiMethodParams->TestParamOne))( //Vše je v pořádku, parametry jsou správné, vrátíme je $retJSON ->retParameter=$ apiMethodParams->TestParamOne; )else( $retJSON->errorno= APIConstants::$ERROR_PARAMS; ) return $retJSON; ) //http://www.example.com/api/?apitest.helloAPIResponseBinary= ("responseBinary": 1) function helloAPIResponseBinary($apiMethodParams)( header("Content-type: image/png"); echo file_get_contents("http://habrahabr.ru/i/error-404-monster.jpg") ;)) ?>

Pro pohodlí testovacích metod k nim přidávám adresu, kde mohu rychle požádat o testování.

A tak máme tři způsoby

Funkce helloAPI() ( $retJSON = $this->createDefaultJson(); $retJSON->withoutParams = "Je to metoda volaná bez parametrů"; return $retJSON; )

Jedná se o jednoduchou metodu bez parametrů. Jeho adresa pro volání GET je www.example.com/api/?apitest.helloAPI= ()

Výsledkem spuštění bude taková stránka (v prohlížeči)

Tato metoda přebírá parametry. TestParamOne je vyžadován a my to zkontrolujeme. Nelze jej přenést, JSON bude vrácen s chybou

Funkce helloAPIWithParams($apiMethodParams) ( $retJSON = $this->createDefaultJson(); if (isset($apiMethodParams->TestParamOne))( //Vše je v pořádku, parametry jsou správné, vrátíme je $retJSON->retParameter =$apiMethodParams-> TestParamOne; )else( $retJSON->errorno= APIConstants::$ERROR_PARAMS; ) return $retJSON; )
Výsledek provedení

ahojAPIResponseBinary

A poslední metoda helloAPIResponseBinary - vrátí binární data - obrázek habr o neexistující stránce (jako příklad)
function helloAPIResponseBinary($apiMethodParams)( header("Content-type: image/jpeg"); echo file_get_contents("http://habrahabr.ru/i/error-404-monster.jpg"); )
Jak vidíte, došlo k nahrazení názvu pro zobrazení grafického obsahu.
Výsledek bude takový

Sedm koz a vlk bez oka

Vytvoření RESTful API v PHP

REST nebo v plné formě, Převod reprezentativního státu se stala standardní architekturou návrhu pro vývoj webových API.

REST je velmi jednoduché rozhraní pro správu informací bez dalších back-endových vrstev. Každá informace je jednoznačně identifikována globálním identifikátorem, jako je adresa URL. Každá URL má zase přesně definovaný formát.

REST používá metody požadavků HTTP, aby se začlenil do existující architektury HTTP. Tyto operace jsou následující:

GET - používá se pro základní požadavky na čtení na server

PUT- Používá se k úpravě existujícího objektu na serveru

POST- Používá se k vytvoření nového objektu na serveru

DELETE - používá se k odstranění objektu na serveru

A teď k věci, tzn. psaní jednoduchého API, které můžete použít ve svých projektech.

Co je API

V širším smyslu je API rozhraní webové aplikace, které umožňuje veřejný přístup k metodám a jejich ovládání zvenčí. Běžné použití API je, když potřebujete získat data z aplikace (například článek nebo jiná data), aniž byste skutečně navštívili zdroj (například web). Aby to bylo možné, je pro aplikaci implementováno API, které umožňuje aplikacím třetích stran zadávat požadavky a vracet specifikovaná data uživateli externě. Na webu se to často provádí pomocí RESTful.

V příkladu požadavku na článek může rozhraní API obsahovat identifikátor URI:

example.com/api/v1/recipe/article

Pokud odešlete požadavek GET na toto URL, odpovědí může být seznam nejnovějších zpráv, požadavek PUT může přidat novinky do databáze.

Pokud si vyžádáte /článek /141, bude to definitivní novinka. Tyto příklady ukazují, jak pracovat s aplikací.

Vytvoření vlastního API

Vytvořme soubor .htaccess pro převod požadavků GET na parametry srozumitelné ovladači.

RewriteEngine On RewriteCond %(REQUEST_FILENAME) !-f RewriteCond %(REQUEST_FILENAME) !-d RewriteRule api/v1/(.*)$ api/v1/api.php?request=$1

Co znamenají řádky v tomto souboru?

První řádek kontroluje existenci přepisovacího modulu. Pokud běží, provedou se následující řádky.

Dále je oznámena možnost předefinovat adresy URL. To znamená, že cesta api/v1/ bude přesměrována na api/v1/index.php. Symboly (.*) označují proměnné, které je třeba zpracovat, a $ je oddělovač, po kterém začíná další logika. Také - znamená bez ohledu na velikost písmen - že proměnné budou připojeny k nové URL, L - mod_rewrite již nezpracovává nic jiného než zadanou.

Deklarujeme třídu, vlastnosti a konstruktor.

abstraktní třída API ( /** * Vlastnost: metoda * GET, POST, PUT, DELETE */ protected $method = ""; /** * Vlastnost: koncový bod * Model požadovaný v URI. např.: /files */ protected $endpoint = ""; /** * Vlastnost: verb * Volitelný další deskriptor o koncovém bodu, používaný pro věci, které * nelze zpracovat základními metodami. např.: /files/process */ protected $verb = "" ; /** * Vlastnost: args * Všechny další komponenty URI po koncovém bodu a slovesu byly odstraněny, v našem případě * celočíselné ID zdroje. např.: / ///* nebo / /*/ chráněné $args = Array(); /** * Vlastnost: soubor * Ukládá vstup požadavku PUT */ protected $file = Null; /** * Konstruktor: __construct * Povolit CORS, sestavit a předzpracovat data */ veřejná funkce __construct($request) ( header("Access-Control-Allow-Orgin: *"); header("Access-Control -Allow-Methods: *"); header("Content-Type: application/json"); $this->args = explode("/", rtrim($request, "/")); $this->endpoint = array_shift($this->args); if (array_key_exists(0, $this->args) && !is_numeric($this->args)) ( $this->verb = array_shift($this->args); ) $this->method = $_SERVER["REQUEST_METHOD"]; if ($this->method == "POST" && array_key_exists("HTTP_X_HTTP_METHOD", $_SERVER)) ( if ($_SERVER["HTTP_X_HTTP_METHOD"] == " DELETE") ( $this->method = "DELETE"; ) else if ($_SERVER["HTTP_X_HTTP_METHOD"] == "PUT") ( $this->method = "PUT"; ) else ( vyvolá novou výjimku(" Neočekávané záhlaví"); ) ) switch($this->method) ( případ "DELETE": případ "POST": $this->request = $this->_cleanInputs($_POST); break; případ "GET": $ this->request = $this->_cleanInputs($_GET); break; case "PUT": $this->request = $this->_cleanInputs($_GET); $this->file = file_get_contents("php://input"); přestávka; výchozí: $this->_response("Neplatná metoda", 405); přestávka; ) ) public function processAPI() ( if (method_exists($this, $this->endpoint)) ( return $this->_response($this->($this->endpoint)($this->args)); ) return $this->_response("No Endpoint: $this->endpoint", 404); ) private function _response($data, $status = 200) ( header("HTTP/1.1 " . $status . " " . $this->_requestStatus($status)); return json_encode($data); ) soukromá funkce _cleanInputs($data) ( $clean_input = Array(); if (is_array($data)) ( foreach ($data jako $k => $v) ( $clean_input[$k] = $this->_cleanInputs($v); ) ) else ( $clean_input = trim(strip_tags($data)); ) return $clean_input; ) soukromá funkce _requestStatus($ kód) ( $status = array(200 => "OK", 404 => "Nenalezeno", 405 => "Metoda není povolena", 500 => "Interní chyba serveru",); návrat ($status[$code ])?$status[$code]:$status; ) )

Deklarováním této abstraktní třídy jsme zabránili PHP ve vytvoření konkrétní instance této třídy. Odtud můžeme používat pouze metody děděním v jiné třídě. K chráněné metodě lze přistupovat pouze v samotné třídě a jejích potomcích.

Vytvoření třídy API

Vytvoříme třídu MyAPI, která zdědí abstraktní třídu API.

require_once "API.class.php"; class MyAPI rozšiřuje API ( chráněný $User; veřejná funkce __construct($request, $origin) ( parent::__construct($request); // Vyřazeno například $APIKey = nové modely\APIKey(); $User = nové modely \User(); if (!array_key_exists("apiKey", $this->request)) ( vyvolá novou výjimku("Žádný klíč API není poskytnut"); ) else if (!$APIKey->verifyKey($this->request ["apiKey"], $origin)) ( vyvolá novou výjimku("Neplatný klíč API"); ) else if (array_key_exists("token", $this->request) && !$User->get("token", $this->request["token"])) ( throw new Exception("Invalid User Token"); ) $this->User = $User; ) /** * Příklad koncového bodu */ příklad chráněné funkce() ( if ($this->method == "GET") ( return "Vaše jméno je " . $this->User->name; ) else ( return "Pouze přijímá požadavky GET"; ) ) )

Pomocí API

// Požadavky ze stejného serveru nemají hlavičku HTTP_ORIGIN if (!array_key_exists("HTTP_ORIGIN", $_SERVER)) ( $_SERVER["HTTP_ORIGIN"] = $_SERVER["SERVER_NAME"]; ) zkuste ( $API = new MyAPI($_REQUEST["request"], $_SERVER["HTTP_ORIGIN"]); echo $API->processAPI(); ) catch (Výjimka $e) ( echo json_encode(Array("error" => $e- >getMessage())); )

Nemotorný překlad tohoto: http://coreymaynard.com/blog/creating-a-restful-api-with-php/

PHP nabízí tři různá API pro připojení k MySQL. Níže uvádíme rozhraní API poskytovaná rozšířeními mysql, mysqli a PDO. Každý fragment kódu vytváří připojení k serveru MySQL běžícímu na „example.com“ pomocí uživatelského jména „user“ a hesla „password“. A spustí se dotaz, aby pozdravil uživatele.

Příklad č. 1 Porovnání tří rozhraní MySQL API

//mysqli
$mysqli = new mysqli ("example.com" , "uživatel" , "heslo" , "databáze" );
$vysledek = $mysqli -> dotaz();
$row = $result -> fetch_assoc();

//CHOP
$pdo = nové PDO ( "mysql:host=example.com;dbname=database", "uživatelské heslo" );
$statement = $pdo -> dotaz ( "SELECT "Dobrý den, milý uživateli MySQL!" JAKO _zpráva FROM DUAL");
$row = $statement -> fetch (PDO::FETCH_ASSOC);
echo htmlentities($row["_message"]);

// mysql
$c = mysql_connect("example.com" , "uživatel" , "heslo" );
mysql_select_db("databáze");
$result = mysql_query ( "SELECT "Dobrý den, milý uživateli MySQL!" JAKO _zpráva FROM DUAL");
$row = mysql_fetch_assoc ($vysledek);
echo htmlentities($row["_message"]);
?>

Doporučené API

Doporučuje se používat buď rozšíření mysqli nebo PDO_MySQL. Pro nový vývoj se nedoporučuje používat staré rozšíření mysql, protože bylo zastaralé v PHP 5.5.0 a bylo odstraněno v PHP 7. Podrobná matice porovnání funkcí je uvedena níže. Celkový výkon všech tří rozšíření je považován za přibližně stejný. Přestože výkon rozšíření přispívá pouze zlomkem celkové doby běhu webového požadavku PHP. Často je dopad jen 0,1 %.

Srovnání funkcí

ext/mysqli PDO_MySQL ext/mysql
Představena verze PHP 5.0 5.1 2.0
Součástí PHP 5.x Ano Ano Ano
Součástí PHP 7.x Ano Ano Ne
Stav vývoje Aktivní Aktivní Údržba pouze v 5.x; odstraněno v 7.x
Životní cyklus Aktivní Aktivní Zastaráno v 5.x; odstraněno v 7.x
Doporučeno pro nové projekty Ano Ano Ne
OOP rozhraní Ano Ano Ne
Procedurální rozhraní Ano Ne Ano
podporuje API neblokující, asynchronní dotazy s mysqlnd Ano Ne Ne
Trvalá připojení Ano Ano Ano
API podporuje znakové sady Ano Ano Ano
API podporuje připravená prohlášení na straně serveru Ano Ano Ne
podporuje připravená prohlášení API na straně klienta Ne Ano Ne
API podporuje uložené procedury Ano Ano Ne
API podporuje více příkazů Ano Většina Ne
podporuje API transakce Ano Ano Ne
Transakce lze ovládat pomocí SQL Ano Ano Ano
Podporuje všechny funkce MySQL 5.1+ Ano Většina Ne

Při vývoji projektu jsem se potýkal s potřebou zorganizovat interakci klient-server mezi aplikacemi na platformách iOS a Android s mým webem, na kterém byly uloženy veškeré informace – vlastní databáze mysql, obrázky, soubory a další obsah.
Problémy, které bylo třeba vyřešit, byly poměrně jednoduché:
registrace/autorizace uživatele;
odesílání/příjem určitých údajů (například seznamu produktů).

A pak jsem chtěl napsat své vlastní API pro interakci se serverovou stranou – většinou pro praktické účely.

Vstupní data

Měl jsem k dispozici:
Server – Apache, PHP 5.0, MySQL 5.0
Klient – ​​zařízení Android, iOS, libovolný prohlížeč

Rozhodl jsem se, že pro požadavky na server a odpovědi z něj budu používat datový formát JSON - pro jeho jednoduchost a nativní podporu v PHP a Androidu. Zde mě iOS naštval - nemá nativní podporu JSON (zde jsem musel použít vývoj třetích stran).

Bylo také rozhodnuto, že požadavky lze zasílat jak prostřednictvím požadavků GET, tak POST (zde pomohl $_REQUEST v PHP). Toto řešení umožnilo testovat API prostřednictvím požadavků GET v libovolném dostupném prohlížeči.

Bylo rozhodnuto, že vzhled dotazů bude vypadat takto:
http://[adresa serveru]/[cesta ke složce api]/?[název_api].[název_metody]=

Cesta ke složce api je adresář, do kterého je potřeba zadávat požadavky, v jehož kořeni leží soubor index.php – ten je zodpovědný za volání funkcí a obsluhu chyb
Název api - pro pohodlí jsem se rozhodl oddělit skupiny API - uživatel, databáze, obsah atd. V tomto případě má každé API svůj vlastní název
Název metody – název metody, kterou je třeba volat v zadaném rozhraní API
JSON - řetězcová reprezentace objektu JSON pro parametry metody

API kostry

Kostra API na straně serveru se skládá z několika základních tříd:
index.php - indexový soubor adresáře v Apache; obsahuje všechna volání API, analyzuje parametry a volá metody API
MySQLiWorker – jediná třída pro práci s databází MySQL přes MySQLi
apiBaseCalss.php je nadřazená třída pro všechna rozhraní API v systému – každé rozhraní API musí zdědit z této třídy, aby správně fungovalo
apiEngine.php - hlavní třída systému - analyzuje předané parametry (po jejich předběžné analýze v index.php), připojí požadovanou api třídu (prostřednictvím metody require_once), zavolá v ní požadovanou metodu a vrátí výsledek v JSON formát
apiConstants.php - třída s konstantami pro volání api a přenos chyb
apitest.php - otestujte api pro testování nových metod před jejich zařazením do produkční verze

Celý mechanismus vypadá takto:
Odešleme požadavek na server – například www.example.com/api/?apitest.helloWorld= ()
Na straně serveru analyzuje soubor index.php předané parametry. Index.php vždy vezme pouze první prvek ze seznamu předávaných parametrů $_REQUEST – to znamená, že konstrukce jako www.example.com/api/?apitest.helloWorld= ()&apitest.helloWorld2 – zavolá pouze metodu helloWorld v apitest. Metoda helloWorld2 nebude volána

Nyní více podrobností o každé z nich

Snažil jsem se soubory dostatečně zdokumentovat, aby nezabíraly mnoho místa pro text. Nicméně v těch souborech, kde nejsou žádné komentáře, přesto poskytnu popis.

Index.php

Jak jsem řekl dříve, toto je vstupní indexový soubor pro Apache, což znamená, že bude přijímat všechna volání jako www.example.com/api.

0)( required_once "apiEngine.php"; foreach ($_REQUEST jako $apiFunctionName => $apiFunctionParams) ( $APIEngine=new APIEngine($apiFunctionName,$apiFunctionParams); echo $APIEngine->call)else;Function)) ( $jsonError->error="Není volána žádná funkce"; echo json_encode($jsonError); ) ?>

V první řadě nastavíme typ obsahu - text/html (ten pak můžete změnit v samotných metodách) a kódování - UTF-8.
Dále zkontrolujeme, že nás o něco žádají. Pokud ne, vypíšeme JSON s chybou.
Pokud existují parametry požadavku, pak připojíme soubor API motoru - apiEngine.php a vytvoříme třídu motoru s předanými parametry a zavoláme metodu api.
Opustíme smyčku, protože jsme se rozhodli, že zpracujeme pouze jeden hovor.

apiEngine.php

Druhou nejdůležitější třídou je třída apiEngine – je to engine pro volání api a jejich metod.
apiFunctionParams = stripcslashes($apiFunctionParams); //Analyzujte pole dvou prvků - název API, - název metody v API $this->apiFunctionName = explode("_", $apiFunctionName); ) //Vytvoření funkce odezvy JSON createDefaultJson() ( $retObject = json_decode("()"); $response = APIConstants::$RESPONSE; $retObject->$response = json_decode("()"); return $retObject ; ). register if (file_exists($apiName . ".php")) ( $apiClass = APIEngine::getApiEngineByName($apiName);//Získání objektu API $apiReflection = new ReflectionClass($apiName);//Through reflection získáme informace o objektu třídy try ( $functionName = $this->apiFunctionName;//Název metody pro volání $apiReflection->getMethod($functionName);//Zkontrolujte přítomnost metody $response = APIConstants ::$RESPONSE; $jsonParams = json_decode($this- >apiFunctionParams);//Dekódujte parametry požadavku do objektu JSON if ($jsonParams) ( if (isset($jsonParams->responseBinary))(//Aby bylo možné nevrací JSON, ale binární data jako zip, png a další obsah vrací $apiClass->$functionName($jsonParams);//Volání metody v rozhraní API )else( $resultFunctionCall->$response = $apiClass->$ functionName($jsonParams);//Volání metody v API, která vrátí objekt JSON) ) else ( //Pokud dojde k chybě při dekódování parametrů požadavku JSON $resultFunctionCall->errno = APIConstants::$ERROR_ENGINE_PARAMS; $resultFunctionCall->error = "Chyba dané parametry"; ) ) catch (Výjimka $ex) ( //Neočekávaná výjimka $resultFunctionCall->error = $ex->getMessage(); ) ) else ( //Pokud nebylo požadované API nalezeno $resultFunctionCall->errno = APIConstants::$ ERROR_ENGINE_PARAMS; $resultFunctionCall->error = "Soubor nenalezen"; $resultFunctionCall->REQUEST = $_REQUEST; ) return json_encode($resultFunctionCall); )) ?>

apiConstants.php

Tato třída se používá pouze k ukládání konstant.

MySQLiWorker.php

Jediná třída pro práci s databází. Jinak se jedná o obyčejného samotáře - takových příkladů je na internetu spousta.

dbName = $dbName; self::$instance->dbHost = $dbHost; self::$instance->dbUser = $dbUser; self::$instance->dbPassword = $dbPassword; self::$instance->openConnection(); ) return self::$instance; ) //Určete typy parametrů dotazu do databáze a vraťte řetězec pro vazbu přes ->bind function PrepareParams($params) ( $retSTMTString = ""; foreach ($params jako $value) (​if (is_int( $value) || is_double ($value)) ( $retSTMTString.="d"; ) if (is_string($value)) ( $retSTMTString.="s"; ) ) return $retSTMTString; ) //Připojte se k veřejná funkce databáze openConnection() ( if (is_null($this->connectLink)) ( $this->connectLink = new mysqli($this->dbHost, $this->dbUser, $this->dbPassword, $this-> dbName); $this-> connectLink->query("SET NAMES utf8"); if (mysqli_connect_errno()) ( printf("Připojení není možné: %s\n", mysqli_connect_error()); $this->connectLink = null; ) else ( mysqli_report(MYSQLI_REPORT_ERROR ); ) ) return $this->connectLink; ) //Uzavření připojení k databázi veřejné funkce closeConnection() ( if (!is_null($this->connectLink)) ( $this- >connectLink->close(); ) ) //Převede odpověď na veřejnou funkci asociativního pole stmt_bind_assoc(&$stmt, &$out) ( $data = mysqli_stmt_result_metadata($stmt); $pole = pole(); $out = pole(); $pole = $stmt; $počet = 1; $currentTable = ""; while ($field = mysqli_fetch_field($data)) ( if (strlen($currentTable) == 0) ( $currentTable = $field->table; ) $fields[$count] = &$out[$field->name ]; $count++; ) call_user_func_array("mysqli_stmt_bind_result", $fields); )) ?>

apiBaseClass.php

No, dostáváme se k jedné z nejdůležitějších tříd systému – základní třídě pro všechna API v systému.

mySQLWorker = MySQLiWorker::getInstance($dbName,$dbHost,$dbUser,$dbPassword); ) ) funkce __destruct() ( if (isset($this->mySQLWorker))( //Pokud bylo navázáno připojení k databázi, $this->mySQLWorker->closeConnection(); //poté ji zavřete, když je naše třída již není potřeba ) ) //Vytvořte výchozí JSON pro funkci odpovědí createDefaultJson() ( $retObject = json_decode("()"); return $retObject; ) //Vyplňte objekt JSON odpovědí z funkce MySQLiWorker fillJSON(&$ jsonObject, &$stmt, &$mySQLWorker) ( $row = array(); $mySQLWorker->stmt_bind_assoc($stmt, $row); while ($stmt->fetch()) ( foreach ($row jako $key = > $value) ( ​​​$ klíč = strtolower($key); $jsonObject->$key = $hodnota; ) break; ) return $jsonObject; ) ) ?>

Jak můžete vidět, tato třída obsahuje několik „utilitních“ metod, jako například:
konstruktor, ve kterém se provádí připojení k databázi, pokud bude aktuální API pracovat s databází;
destruktor - hlídá uvolňování zdrojů - přerušení navázaného spojení s databází
createDefaultJson - vytvoří výchozí JSON pro odpověď metody
fillJSON - pokud se předpokládá, že požadavek vrátí pouze jeden záznam, pak tato metoda naplní JSON pro odpověď daty z prvního řádku odpovědi z databáze

Pojďme si vytvořit vlastní API

To je vlastně celá páteř tohoto API. Nyní se podíváme, jak toto vše využít na příkladu vytvoření prvního API s názvem apitest. A napíšeme si do něj pár jednoduchých funkcí:
jeden bez parametrů
jeden s parametry a ona nám je vrátí, aby bylo jasné, že je přečetla
ten, který nám vrátí binární data

A tak vytvoříme třídu apitest.php s následujícím obsahem

createDefaultJson(); $retJSON->withoutParams = "Je to volána metoda bez parametrů"; return $retJSON; ) //http://www.example.com/api/?apitest.helloAPIWithParams=("TestParamOne":"Text prvního parametr") funkce helloAPIWithParams($apiMethodParams) ( $retJSON = $this->createDefaultJson(); if (isset($apiMethodParams->TestParamOne))( //Vše je v pořádku, parametry jsou správné, vrátíme je $retJSON ->retParameter=$ apiMethodParams->TestParamOne; )else( $retJSON->errorno= APIConstants::$ERROR_PARAMS; ) return $retJSON; ) //http://www.example.com/api/?apitest..jpg "); )) ?>

Pro pohodlí testovacích metod k nim přidávám adresu, kde mohu rychle požádat o testování.

A tak máme tři způsoby

Funkce helloAPI() ( $retJSON = $this->createDefaultJson(); $retJSON->withoutParams = "Je to metoda volaná bez parametrů"; return $retJSON; )

Jedná se o jednoduchou metodu bez parametrů. Jeho adresa pro volání GET je www.example.com/api/?apitest.helloAPI= ()

Výsledkem spuštění bude taková stránka (v prohlížeči)

Tato metoda přebírá parametry. TestParamOne je vyžadován a my to zkontrolujeme. Nelze jej přenést, JSON bude vrácen s chybou

Funkce helloAPIWithParams($apiMethodParams) ( $retJSON = $this->createDefaultJson(); if (isset($apiMethodParams->TestParamOne))( //Vše je v pořádku, parametry jsou správné, vrátíme je $retJSON->retParameter =$apiMethodParams-> TestParamOne; )else( $retJSON->errorno= APIConstants::$ERROR_PARAMS; ) return $retJSON; )
Výsledek provedení

ahojAPIResponseBinary

A poslední metoda helloAPIResponseBinary - vrátí binární data - obrázek habr o neexistující stránce (jako příklad)
function helloAPIResponseBinary($apiMethodParams)( header("Content-type: image/jpeg"); echo file_get_contents("http://site/i/error-404-monster.jpg"); )
Jak vidíte, došlo k nahrazení názvu pro zobrazení grafického obsahu.
Výsledek bude takový