Upload
max-timokhin
View
354
Download
1
Embed Size (px)
Citation preview
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ
íà îñíîâå àðõèòåêòóðû REST
Ìàðêåòèíãîâàÿ ãðóïïà Òåêàðò
14 èþëÿ 2009 ã.
Университет Текарт
Университет Текарт
×òî ìû õîòèì îò ñëîÿ êîíòðîëëåðà
àáñòðàêöèÿ HTTP-çàïðîñà è îòêëèêà, êîìïåíñèðîâàíèåíåóäîáñòâ âñòðîåííîé ðåàëèçàöèè;
âîçìîæíîñòü ñáîðêè îáðàáîò÷èêîâ çàïðîñîâ èç îòäåëüíûõìîäóëåé (middleware-êîìïîíåíòû);
äèñïåò÷åðèçàöèÿ URL, ïðîñòàÿ ñòðóêòóðà íàáîðà ïðàâèëäèñïåò÷åðèçàöèè;
REST êàê íàèáîëåå óíèâåðñàëüíàÿ àðõèòåêòóðà.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 2
Университет Текарт
Àáñòðàêöèÿ HTTP-çàïðîñà è îòêëèêà
Net.HTTP.Request: ïàðàìåòðû çàïðîñà ÷åðåç èíäåêñèðîâàííûéäîñòóï, îáúåêòíûå uploads, upload â ìàññèâàõ ïîëåé.
$id = $request['id'];
$auth = $request->headers['Authorization'];
foreach ($request['upload'] as $line) { ... }
$stored = $request['upload']->copy_to($path);
Net.HTTP.Response: ðàáîòà ñ çàãîëîâêàìè îòêëèêà, body:ñòðîêà, èòåðàòîð, ôàéëîâûé îáúåêò.
$file = IO_FS::File($path);
return Net_HTTP::Response()->
status(Net_HTTP::OK)->
content_type(MIME::type_for_file($file))
body($file);
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 3
Университет Текарт
Îáðàáîò÷èê çàïðîñîâ
Îáúåêò êëàññà, ðåàëèçóþùåãî ñòàíäàðòíûé èíòåðôåéñ:
interface WS_ServiceInterface {
public function run(WS_Environment $env);
}
Ðåçóëüòàò âûïîëíåíèÿ run() � Net.HTTP.Response.
WS.Environment:
åäèíñòâåííûé ñïîñîá îáìåíà èíôîðìàöèåé ìåæäóîáðàáîò÷èêàìè;
ìîæåò ïîðîæäàòü äî÷åðíèå îáúåêòû îêðóæåíèÿ,ïåðåîïðåäåëÿþùèå çíà÷åíèÿ îòäåëüíûõ ïàðàìåòðîâðîäèòåëüñêîé ñðåäû;
ïî óìîë÷àíèþ ñîäåðæèò $request òèïàNet.HTTP.Request.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 4
Университет Текарт
Middleware-êîìïîíåíòû
abstract class WS_MiddlewareService
implements WS_ServiceInterface {
protected $application;
public function __construct(WS_ServiceInterface $application) {
$this->application = $application;
}
}
çàïîìèíàåò ñëåäóþùèé îáðàáîò÷èê è âûçûâàåò åãî;
ìîæåò ìîäèôèöèðîâàòü environment, request, response.
Ñòàíäàðòíûå êîìïîíåíòû: êîíôèãóðàöèÿ, ïîäêëþ÷åíèå êÁÄ, êåøèðîâàíèå, ñåññèè, àâòîðèçàöèÿ, ðîóòèíã àäðåñîâ.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 5
Университет Текарт
Îáùàÿ ñòðóêòóðà ïðèëîæåíèÿ
WS.Middleware.*
REST Applications
Status
Environment
Config
Cache
ORM
Auth
REST
FrontNewsPhotoAPI
öåïî÷êà îáðàáîò÷èêîâ èç
ñòàíäàðòíûõ ìîäóëåé;
ïðîåêò � íàáîð ìàêñèìàëüíî
íåçàâèñèìûõ ïðèëîæåíèé;
êàæäîå ïðèëîæåíèå � äîìåí èëè
êàòàëîã ïåðâîãî óðîâíÿ;
äèñïåò÷åðèçàöèÿ àäðåñîâ � âíóòðè
ïðèëîæåíèÿ.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 6
Университет Текарт
Êîä îïèñàíèÿ ñòðóêòóðû ïðèëîæåíèÿ
static public function main() {
WS::Runner()->run(
WS_DSL::status(array(404 => 'http/not-found', 500),
WS_DSL::environment(array('urls' => App_URL::mapper()),
WS_DSL::config('../etc/p2.php',
WS_DSL::cache(array( /* caching parameters */ ),
WS_DSL::orm(P2::db(),
WS_DSL::session(
WS_DSL::auth_session(App::db(),
WS_DSL::application_dispatcher(array(
'/' => 'App.Front.Application',
// ...
'news' => 'App.News.Application'),
'App.WS.Pages.Application' )))))))));
}
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 7
Университет Текарт
JAX RS � îñíîâà ðåàëèçàöèè REST-ñåðâèñîâ
Ïî÷åìó ìû âûáðàëè JAX RS?
Ïðîñòîé ñòàíäàðò;
Ïðîçðà÷íî ïåðåíîñèò ìîäåëü REST íà óðîâåíü êîäà;
Óäîáíàÿ ðàáîòà ñ âëîæåííûìè ðåñóðñàìè;
Íå íàêëàäûâàåò îãðàíè÷åíèé íà êëàññû ðåñóðñîâ.
Íàøå ðåøåíèå íà ïðèíöèïàõ JAX RS:
Ñîáñòâåííûé DSL îïèñàíèÿ ðåñóðñîâ âìåñòî àííîòàöèé;
Îòêàç îò ïðîèçâîëüíîãî ïîðÿäêà îïðåäåëåíèÿ ðåñóðñîâ äëÿóïðîùåíèÿ àëãîðèòìà;
Íåêîòîðûå âîçìîæíîñòè îòñóòñòâóþò � ðåàëèçóåì ïîçæå.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 8
Университет Текарт
Îáúåêòû ðåñóðñîâ
Ðåñóðñ � ïðîñòî PHP îáúåêò;
Íå îáÿçàòåëüíî íàñëåäîâàòü îò ðîäèòåëüñêîãî êëàññà èëèðåàëèçîâûâàòü èíòåðôåéñ.
class App_Stories_Index {public function index() { ... }public function draft() { ... }public function create() { ... }public function story($id) { return App_Stories::Story($this->db->stories->find($id));
}
Íà ïðàêòèêå èìååò ñìûñë âûíîñèòü îáùóþôóíêöèîíàëüíîñòü â ðîäèòåëüñêèé êëàññ, íî ýòî íåòðåáîâàíèå ôðåéìâîðêà.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 9
Университет Текарт
Ìåòîäû êëàññà ðåñóðñà
Êëàññ ðåñóðñà ðåàëèçóåò íàáîð ìåòîäîâ:
âñïîìîãàòåëüíûå ìåòîäû äëÿ âíóòðåííåãî èñïîëüçîâàíèÿ;
HTTP-ìåòîäû: ìåòîäû îáðàáàòûâàþùèå ðàçëè÷íûå âèäûçàïðîñîâ;
ñóáëîêàòîðû: ìåòîäû, ïîðîæäàþùèå ýêçåìïëÿðû êëàññîââëîæåííûõ ðåñóðñîâ.
íà ïàðàìåòðû ìåòîäîâ íå íàêëàäûâàåòñÿ íèêàêèõîãðàíè÷åíèé.
çíà÷åíèÿ ïàðàìåòðîâ ïîäñòàâëÿþòñÿ àâòîìàòè÷åñêè èñõîäÿèç èìåíè ïàðàìåòðà.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 10
Университет Текарт
ßçûê îïèñàíèÿ ðåñóðñîâ
Äëÿ ïðèíÿòèÿ ðåøåíèÿ î âûçîâå ìåòîäà ðåñóðñàíåîáõîäèìî èìåòü ïîëíîå îïèñàíèå äîñòóïíûõ ðåñóðñîâ èèõ ìåòîäîâ;
Ðåàëüíî âûïîëíÿåòñÿ êîä îäíîãî èëè íåñêîëüêèõ ðåñóðñîâ,íåò íåîáõîäèìîñòè çàãðóæàòü âñå ðåñóðñû;
Îïèñàíèå ðåñóðñîâ, ñîñòàâëÿþùèõ ïðèëîæåíèå, äîëæíîáûòü îòäåëåíî îò êîäà ðåñóðñîâ;
Ñîçäàåì ñâîé DSL äëÿ îïèñàíèÿ ñòðóêòóðû ïðèëîæåíèÿ.
×àñòî ïðèëîæåíèå ìîæíî ðàçáèòü íà îòäåëüíûåíåçàâèñèìûå ïðèëîæåíèÿ � ìèíèìèçàöèÿ îáúåìà îïèñàíèÿäëÿ îòäåëüíîãî çàïðîñà.
PHP � íå ñàìûé óäîáíûé ÿçûê äëÿ íàïèñàíèÿ DSL, íî êîå-÷òî ñäåëàòü ìîæíî.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 11
Университет Текарт
Îïèñàíèå ðåñóðñîâ ïðèëîæåíèÿ
Ïðèìåð: íîâîñòíûå ñòàòüè è RSS-ëåíòû äëÿ íîâîñòåé,ïðèíàäëåæàùèõ ê ðàçëè÷íûì ðóáðèêàì.
WS_REST_DSL::Application($this)->media_type('rss', 'application/xhtml+xml')->begin_resource('category', 'P2.WS.News.Category', '{name:[a-zA-Z-]+/[a-zA-Z]+}')->
get_for('{page_no:\d+}', 'index')->get_for('', 'index_rss', 'rss')->get_for('top', 'top_rss', 'rss')->get_for('most-popular', 'most_popular_rss', 'rss')->index()->
end->begin_resource('story', 'P2.WS.News.Story', 'stories/{id:\d+}')->
get_for('{page_no:\d+}', 'index')->index()->
end->begin_resource('index', 'P2.WS.News.Index')->
get_for('most-popular', 'most_popular_rss', 'rss')->get_for('', 'index_rss', 'rss')->index('html')->
end->end;
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 12
Университет Текарт
Îïèñàíèå ïðèëîæåíèÿ
resource($name, $classname, $path)
$name èìÿ ðåñóðñà;
$classname èìÿ êëàññà, ðåàëèçóþùåãî ðåñóðñ;
$path ïóòü ê ðåñóðñó â âèäå øàáëîíà URL.
begin_resource('category',
'P2.WS.News.Category',
'{name:[a-zA-Z-]+/[a-zA-Z]+}');
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 13
Университет Текарт
Øàáëîíû URL
Ðåãóëÿðíûå âûðàæåíèÿ ñ èìåíîâàííûìè ïàðàìåòðàìè:
stories/{id:\d+}
archive/{year:\d\d\d\d}/{month:\d\d?}/{day:\d\d?}
Çíà÷åíèÿ ïàðàìåòðîâ àâòîìàòè÷åñêè ïîäñòàâëÿþòñÿ ïðèâûçîâå ìåòîäîâ.
public function story($id);
public function archive_daily($year, $month, $day);
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 14
Университет Текарт
Îïèñàíèå ìåòîäîâ ðåñóðñà
Ìåòîä ðåñóðñà âûïîëíÿåò îáðàáîòêó çàïðîñîâ ðàçëè÷íîãî âèäàè ôîðìèðóåò îòêëèê.
method($name, $http_mask, $path, $formats = 'html');
$name èìÿ ìåòîäà;
$http_mask GET | POST | PUT | DELETE;
$path URL-øàáëîí äëÿ ìåòîäà;
$formats ñïèñîê ôîðìàòîâ, ïîääåðæèâàåìûõ ìåòîäîì.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 15
Университет Текарт
Ïàðàìåòðû âûçîâà ìåòîäîâ
Ìåòîä ìîæåò èìåòü ëþáîé íàáîð ïàðàìåòðîâ;
Åñëè èìÿ ïàðàìåòðà ñîâïàäàåò ñ èìåíåì ïàðàìåòðàøàáëîíà � ïîäñòàâëÿåòñÿ çíà÷åíèå ïàðàìåòðà;
Åñëè èìÿ ïàðàìåòðà âõîäèò â íàáîð ïðåäîïðåäåëåííûõèìåí � ïîäñòàâëÿåòñÿ ñîîòâåòñòâóþùåå çíà÷åíèå.
 ïðîòèâíîì ñëó÷àå ïîäñòàâëÿåòñÿ null.
Ïðåäîïðåäåëåííûå çíà÷åíèÿ:
env îêðóæåíèå (çàïðîñ + äàííûå, ñîçäàííûåïðåäûäóùèìè îáðàáîò÷èêàìè);
request http-çàïðîñ;
format ôîðìàò, äëÿ êîòîðîãî âûçâàí ìåòîä.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 16
Университет Текарт
Ôîðìàòû ïðåäñòàâëåíèé
Äëÿ êàæäîãî ìåòîäà ìîæíî óêàçàòü ñïèñîê ôîðìàòîâïðåäñòàâëåíèé.
Îïðåäåëåíèå ôîðìàòà � ïî ðàñøèðåíèþ çàïðàøèâàåìîãîäîêóìåíòà èëè HTTP-çàãîëîâêàì.
Íà êàæäûé ôîðìàò ìîæíî ïðåäóñìîòðåòü îòäåëüíûéìåòîä èëè îáðàáàòûâàòü âñå â îäíîì ìåòîäå, èñïîëüçóÿïàðàìåòð $format.
Ìîæíî îãðàíè÷èâàòü ôîðìàòû äëÿ öåëûõ ðåñóðñîâ.
get_for('most-popular', 'most_popular', 'html,rss');
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 17
Университет Текарт
Ñóáëîêàòîðû
Ñîçäàíèå êëàññà, ñîîòâåòñòâóþùåãî âëîæåííîìó ðåñóðñó,ìîæåò áûòü âûïîëíåíî äèíàìè÷åñêè â ïðîöåññå îáðàáîòêè.
Ñóáëîêàòîð íå îáðàáàòûâàåò çàïðîñ;
Âìåñòî ýòîãî îí ïðèíèìàåò ðåøåíèå î ñîçäàíèèýêçåìïëÿðà êëàññà âëîæåííîãî ðåñóðñà;
Äëÿ ïîëó÷åííîãî ðåñóðñà çàíîâî âûïîëíÿåòñÿ ïðîöåññïîèñêà ìåòîäà îáðàáîòêè çàïðîñà.
sublocator($name, $path)
name èìÿ ìåòîäà-ñóáëîêàòîðà;
path øàáîí URL.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 18
Университет Текарт
Êàê âñå ýòî ðàáîòàåò
1 Îïðåäåëÿåì ôîðìàò (ðàñøèðåíèå èëè çàãîëîâîê);
2 Ïðîñìàòðèâàåì îïèñàíèÿ ðåñóðñîâ è ñîïîñòàâëÿåì ïóòü êàæäîãî ñ íà÷àëîì URL;
3 Åñëè íå íàøëè òàêîãî, ÷òî URL ñîîòâåòñòâóåò è ôîðìàò ïîääåðæèâàåòñÿ � îøèáêà;
4 Ðåñóðñ íàøëè, èùåì ìåòîä. Óáèðàåì èç URL ñîâïàâøóþ ñ ïóòåì ðåñóðñà ÷àñòü;
5 Ïðîñìîòðèâàåì âñå îïèñàíèÿ ìåòîäîâ ðåñóðñà, ñîïîñòàâëÿÿ øàáëîí ñ íà÷àëîì îñòàòêàURL;
6 Åñëè íåò íè http-ìåòîäà, íè ñóáëîêàòîðà, ïîäõîäÿùèõ ïî øàáëîíó URL, ôîðìàòó è ò.ä. �îøèáêà, ìåòîä íå ïîääåðæèâàåòñÿ.
7 Åñëè íàéäåí http-ìåòîä c ñîîòâåòñòâóþùèì øàáëîíîì URL, ôîðìàòîì è ìàñêîéHTTP-ìåòîäîâ � ñîçäàåì îáúåêòà êëàññà ðåñóðñà, ïîäñòàâëÿåì ïàðàìåòðû è âûçûâàåììåòîä. Ðåçóëüòàò ðàáîòû è åñòü ðåçóëüòàò âûïîëíåíèÿ. Ðàáîòà çàâåðøåíà.
8 Åñëè íàéäåí ñóáëîêàòîð ñ ñîîòâåòñòâóþùèì øàáëîíîì URL � ïîäñòàâëÿåì ïàðàìåòðû èâûçûâàåì ìåòîä.
9 Ðåçóëüòàò âûïîëíåíÿ ìåòîäà � íîâûé ðåñóðñ. Èùåì â ñïèñêå ðåñóðñîâ îïèñàíèå ðåñóðñàñîîòâåòñòâóþùåãî êëàññà.
10 Åñëè òàêîãî ðåñóðñà íåò � îøèáêà;
11 Óäàëÿåì ñîâïàâøóþ ñòðîêó èç íà÷àëà URL è âîçâðàùàåìñÿ â ïóíêò 4, è òàê äî òåõ ïîð,ïîêà íå íàéäåòñÿ http-ìåòîä.
Êîëè÷åñòâî ïðîõîäîâ îãðàíè÷åíî, /resource/ è /resource/index.html � ýêâèâàëåíòíû.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 19
Университет Текарт
Èíòåãðàöèÿ ñ ñîáñòâåííûì ORM-ðåøåíèåì
WS.REST.ORM � ñòàíäàðòíûé ìîäóëü äëÿ ïîñòðîåíèÿrestful API íà áàçå ORM;
ïðèâÿçûâàåòñÿ ê íåêîòîðîìó ñòàíäàðòíîìó íàáîðó àäðåñîâ,íàïðèìåð api/*;
êîíòðîëü äîñòóïà íà óðîâíå ñëîÿ áèçíåñ-ëîãèêè;
î÷åíü ïðîñòàÿ ðåàëèçàöèÿ, âñå äâà âèäà ðåñóðñîâ � äëÿìàïïåðîâ è ñóùíîñòåé;
ðåàëèçàöèÿ íå çàâèñèò îò ñïåöèôèêè ñëîÿ áèçíåñ-ëîãèêè.
Áèçíåñ-ëîãèêà + ñòàíäàðòíûé REST API + Rich Client (Dojo) =áûñòðàÿ ðåàëèçàöèÿ ñëîæíûõ ïðèëîæåíèé.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 20
Университет Текарт
Èíòåãðàöèÿ ORM-ðåøåíèåì � ïðîäîëæåíèå
$db->news->stories->most_popular->select()
GET /api/news/stories/most_popular/
$db->news->stories->insert($story);
POST /api/news/stories/
$story = $db->news->stories[15];
$db->news->stories->update($story);
$db->news->stories->delete($story);
PUT /api/news/stories/15/
DELETE /api/news/stories/15/
$db->news->categories[5]->stories;GET /api/news/categories/5/stories/
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 21
Университет Текарт
×òî ìû âûèãðûâàåì îò èñïîëüçîâàíèÿ REST-ìîäåëè
óíèâåðñàëüíîñòü: êëàññè÷åñêèå âåá-ïðèëîæåíèÿ,âåá-ñåðâèñû, áýêåíä AJAX-ïðèëîæåíèé;
îäíîðîäíàÿ ñòðóêòóðà êîäà êëàññîâ êîíòðîëëåðîâ;
ïðîñòàÿ ðàáîòà ñ âëîæåííûìè ðåñóðñàìè: ñòðàíè÷íûéêîíòåíò, êàòàëîãè òîâàðîâ è ò.ä.;
âîçìîæíîñòü èñïîëüçîâàíèÿ îòäåëüíûõ êëàññîâ ðåñóðñîâ ââèäå áèáëèîòå÷íûõ êîìïîíåíò;
ðàñøèðÿåìîñòü.
Ïîñòðîåíèå ñëîÿ êîíòðîëëåðà âåá-ïðèëîæåíèÿ íà îñíîâå àðõèòåêòóðû REST 22
Âîïðîñû?