PSR-7 в примери
Стандартът PSR-7 е успешно завършен. Тази седмица бяха добавени финалните щрихи. И сега версия 0.6.0 на пакета http-message е готова за използване. Опитайте се да следвате този стандарт във вашите приложения.
HTTP съобщения
HTTP е доста прост протокол. Ето защо той се използва успешно от много години. Съобщенията в него имат следната структура:
Заглавките са двойки ключ-стойност. Клавишите са чувствителни към главни и малки букви. Стойностите са низове. Едно заглавие може да има множество стойности. В този случай стойностите обикновено се представят като списък, разделен със запетая. Тялото на съобщението е низ. Въпреки че обикновено се обработва като нишка от сървъра и клиента, за да се намали потреблението на памет и натоварването при обработката. Това е изключително важно, когато се прехвърлят големи масиви от данни и особено когато се прехвърлят файлове. Например, извън кутията PHP излага тялото на входящата заявка като php://input поток и използва изходен буфер (също технически поток), за да върне отговора. Редът за съобщение е мястото, което отличава HTTP заявка от отговор. Низът на съобщението на заявката (наричан по-нататък низът на заявката) има следния формат:
"схема" в http заявка ще бъде или http, или https. „път“ също е част, която всеки разбира. Но какво е "авторитет"?
където паролата не е задължителна. Всъщност в съществуващите спецификации се препоръчва изобщо да не се използва парола в URI. По-добре е да принудите клиента да поиска парола. Низът на заявката е набор от двойки ключ-стойност, разделени с амперсанд:
В зависимост от езика за изпълнение, той може също да моделира списъци или масиви:
PHP ще преобразува дадения низ в двуизмерен масив:
Така,ако гъвкавостта при оформянето на целта на заявката не беше достатъчна за нас, URI би предоставил своя собствена. За щастие отговорите на HTTP сървъра са по-прости. Отговорният низ изглежда така:
"ВЕРСИЯ", както споменахме по-рано, обикновено е 1.0 или по-често 1.1. „статус“ е число между 100 и 599 включително; "причина" - обяснение за статуса, стандартно за всеки статус. Това беше бърз преглед на HTTP съобщенията. Нека сега да видим как ги моделира PSR-7.
Заглавки на съобщения
По подразбиране имената на заглавките на съобщенията не са чувствителни към главни и малки букви. За съжаление повечето езици и библиотеки ги принуждават да използват един и същи регистър. Като пример, PHP ги съхранява в масива $_SERVER с главни букви, с префикс HTTP_ и замествайки _за - (това е в съответствие със спецификацията на Common Gateway Interface (CGI)). PSR-7 опростява достъпа до заглавките, като предоставя обектно-ориентиран слой над тях
Цялата горна логика е независима от начина, по който е указан заглавката; accept, ACCEEPT или дори aCCePt ще бъдат валидни имена на заглавки и ще върнат същия резултат. PSR-7 предполага, че анализирането на всички заглавки ще върне структурата като масив:
След като структурата бъде дефинирана, потребителите ще знаят точно какво получават и могат да обработват заглавките по какъвто начин искат, независимо от изпълнението. Но какво ще стане, ако искате да добавите заглавки към съобщението - например, за да създадете заявка и да я предадете на HTTP клиент? Съобщенията в PSR-7 се моделират като стойностни обекти; това означава, че всяка промяна в състоянието всъщност е различна стойност. По този начин дефинирането на нов хедър ще доведе до нов обект на съобщение.
Ако просто трябва да актуализирате стойността, можете просто да я замените:
Ако искате да добавитедруга стойност към вече съществуваща заглавка, можете да продължите по следния начин:
Или дори премахнете заглавката:
Основен текст на съобщението
Този пример и всички следващи примери за HTTP съобщения в тази публикация ще използват phly/http, библиотека, написана от мен, която отразява еволюцията на PSR-7. В този случай Stream имплементира StreamableInterface. По същество получавате тънък, обектно-ориентиран интерфейс за взаимодействие с тялото на съобщението, което ви позволява да добавяте информация към него, да го четете и много повече. Искате ли да промените съобщението? Създайте ново тяло на съобщението:
Досега разгледахме функции, които са общи за всички съобщения. Сега ще се съсредоточа върху отговорите по-специално. Отговорът има код на състоянието и обяснителна фраза:
Лесно се помни. Ами ако сами формираме отговора? Обяснителната фраза се счита за незадължителна (но в същото време стандартна за всеки статус на код). За него интерфейсът предоставя специфичен за отговор withStatus() мутатор:
Отново съобщенията се моделират като стойностни обекти; промяната на която и да е стойност ще създаде нов екземпляр като резултат, който трябва да бъде обвързан с отговора или заявката. Но в повечето случаи просто ще преназначите текущия екземпляр.
$uri в този случай ще бъде екземпляр на UriInterface и ще ви позволи да използвате URI:
Точно като HTTP съобщенията, URI адресите се представят като стойностни обекти и промяната на която и да е част от URI променя стойността му, мутиращите методи връщат нов екземпляр:
Тъй като промяната на URI означава създаване на нов екземпляр, ако искате промяната да бъде отразена във вашата заявка, трябва да съобщите тези промени на обекта на заявката; и, както при всеки другсъобщение, ако искате да промените метод или URI на конкретен екземпляр, трябва да използвате следните методи:
Сървърни заявки
Да приемем, че пишете API и искате да приемате заявки във формат JSON; изпълнението на това може да изглежда така:
Примерът по-горе демонстрира няколко функции. Първо, показва извличане на заглавка от заявка и разклоняване на логиката въз основа на тази заглавка. Второ, показва формирането на обекта на заявката в случай на грешка (функцията emit() е хипотетична, тя взема обекта на заявката и излъчва заглавките и тялото на заявката). И накрая, примерът демонстрира получаване на тялото на заявката, нейното десериализиране и инжектирането му обратно в заявката.
Друга характеристика на заявките към сървъра са атрибутите. Те са предназначени да съхраняват стойности, които са получени от текущата заявка. Обичаен случай на употреба е съхраняване на резултати от маршрутизиране (разделяне на URI на двойки ключ/стойност). Работата с атрибути се състои от следните методи:
- getAttribute($name, $default = null), за да получите конкретен атрибут и да върнете стойността по подразбиране, ако атрибутът не бъде намерен.
- getAttributes() получава всички атрибути.
- withAttribute($name, $value) за връщане на нов екземпляр на ServerRequestInterface, който съдържа дадения атрибут.
- withoutAttribute(($name) за връщане на екземпляр на ServerRequestInterface без посочения атрибут.
Екземплярът на заявката в този случай се използва за подреждане на данните и предаването им на маршрута. След това резултатите от маршрутизирането се използват за инстанциране на отговора.
Случаи на употреба
Сега след бърза обиколка на различните компонентиPSR-7, нека се върнем към конкретни случаи на употреба.
Тъй като съобщенията и URI адресите са моделирани като стойностни обекти, това също означава, че разработчиците могат да създават основни заявки и URI екземпляри и да създават отделни заявки и URI от тях:
Това, което PSR-7 предлага, е стандартен начин за взаимодействие със заявките, които изпращате като клиент, и отговорите, които получавате. Чрез имплементирането на стойностни обекти ние откриваме някои интересни случаи на употреба с оглед на опростяване на модела „нулиране на заявка“ – модифицирането на заявка винаги води до нов екземпляр, което ни позволява да имаме базов екземпляр с известно състояние, което винаги можем да разширим.
Свързваща връзка
Няма да се спирам дълго на това, т.к. вече го направи в статията. Основната идея накратко е следната:
Функцията взема две HTTP съобщения и извършва някои трансформации върху тях (което може да включва делегиране на следващата налична връзка). Обикновено тези връзки връщат обект на отговор. Друга опция, която често се използва, са ламбда изразите (благодаря на Лари Гарфийлд, че ми изпрати термина по имейл!):
В ламбда връзката композирате едно в друго:
И накрая, има метод, насърчаван от Rack и WSGI, в който всяко стъпало е обект и се предава на изхода:
Използването на междинен елемент е, че той осъществява комуникацията между заявка и отговор и следва стандарта: предвидим модел с предвидимо поведение. Това е чудесен начин за писане на уеб компоненти, които могат да се използват повторно.
Рамки
Едно нещо, което рамките предлагат през годините, е... абстракционен слой върху HTTP съобщения.Целта на PSR-7 е да предостави общ набор от интерфейси за рамки, така че рамките да могат да използват едни и същи абстракции. Това ще позволи на разработчиците да пишат многократно използваем код, независим от рамката, или поне това бих искал да видя! Нека разгледаме Zend Framework 2. Той дефинира интерфейса Zend\Stdlib\DispatchableInterface, който е основата за всеки контролер, който ще използвате в рамката:
Това е само междинната връзка, описана от нас по-горе; единствената разлика е, че използва специфични за рамката реализации на HTTP съобщения. Ами ако вместо това поддържа PSR-7? Повечето реализации на HTTP съобщения в рамки са изградени по такъв начин, че можете да промените състоянието на съобщението по всяко време. Понякога това може да не е напълно вярно, особено като се има предвид, че състоянието на съобщението може вече да не е валидно. Но това е може би единственият недостатък на този метод. PSR-7 съобщенията са стойностни обекти. По този начин не е необходимо да съобщавате на приложението по никакъв начин за промяна в съобщенията. Това прави внедряването по-ясно и по-лесно за проследяване във вашия код (както чрез преминаване през програмата за отстраняване на грешки, така и чрез използване на статични анализатори на код). Като пример, ако ZF2 се актуализира до PSR-7, от разработчиците няма да се изисква да информират MvcEvent за промени, които желаят да разпространят на следните клиенти:
Горният код ясно показва, че променяме състоянието на приложението. Използването на стойностни обекти улеснява една конкретна практика: разпределение на подзаявки или реализация на йерархичен MVC (HMVC). В този случай можете да създавате нови заявки въз основа натекущо, без да информирате приложението за това и да сте сигурни, че състоянието на приложението няма да се промени. Като цяло, за повечето рамки, използването на PSR-7 съобщения ще се превърне в преносима абстракция върху HTTP съобщения. Това ще позволи реализирането на универсална междинна връзка. Персонализирането на съобщенията обаче ще изисква малки промени. Разработчиците трябва да актуализират кода, който отговаря за следенето на състоянието на приложението.
Hardcore conf в C++. Каним само професионалисти.