Mercurial > pub > Impl
comparison Lib/IMPL/Web/Handler/RestController.pm @ 229:47f77e6409f7
heavily reworked the resource model of the web application:
*some ResourcesContraact functionality moved to Resource
+Added CustomResource
*Corrected action handlers
author | sergey |
---|---|
date | Sat, 29 Sep 2012 02:34:47 +0400 |
parents | d6e2ea24af08 |
children | 6d8092d8ce1b |
comparison
equal
deleted
inserted
replaced
228:431db7034a88 | 229:47f77e6409f7 |
---|---|
1 package IMPL::Web::Handler::RestController; | 1 package IMPL::Web::Handler::RestController; |
2 use strict; | 2 use strict; |
3 | 3 |
4 use IMPL::lang qw(:declare :constants); | 4 use IMPL::lang qw(:declare :constants); |
5 | |
6 | |
7 use IMPL::declare { | 5 use IMPL::declare { |
8 require => { | 6 require => { |
7 ResourceInterface => 'IMPL::Web::Application::ResourceInterface', | |
9 Exception => 'IMPL::Exception', | 8 Exception => 'IMPL::Exception', |
10 ArgumentExecption => '-IMPL::InvalidArgumentException', | 9 ArgumentExecption => '-IMPL::InvalidArgumentException', |
11 HttpException => 'IMPL::Web::Exception', | 10 NotFoundException => 'IMPL::Web::NotFoundException' |
12 NotFoundException => 'IMPL::Web::NotFoundException' | |
13 }, | 11 }, |
14 base => { | 12 base => { |
15 'IMPL::Object' => undef, | 13 'IMPL::Object' => undef, |
16 'IMPL::Object::Autofill' => '@_', | 14 'IMPL::Object::Autofill' => '@_', |
17 'IMPL::Object::Serializable' => undef | 15 'IMPL::Object::Serializable' => undef |
18 } | 16 }, |
17 props => [ | |
18 rootResource => PROP_GET | PROP_OWNERSET, | |
19 trailingSlash => PROP_GET | PROP_OWNERSET | |
20 ] | |
19 }; | 21 }; |
20 | |
21 BEGIN { | |
22 public property root => PROP_GET | PROP_OWNERSET; | |
23 public property contract => PROP_GET | PROP_OWNERSET; | |
24 } | |
25 | 22 |
26 sub CTOR { | 23 sub CTOR { |
27 my ($this) = @_; | 24 my ($this) = @_; |
28 | 25 |
29 die ArgumentException->new("root") unless $this->root; | 26 die ArgumentException->new(rootResource => "A web-resource is required") |
30 die ArgumentException->new("contract") unless $this->contract; | 27 unless eval { $this->rootResource->isa(ResourceInterface) }; |
28 | |
31 } | 29 } |
30 | |
31 sub GetResourcePath { | |
32 my ($this,$action) = @_; | |
33 | |
34 my $pathInfo = $action->pathInfo; | |
35 my @segments; | |
36 | |
37 if (length $pathInfo) { | |
38 | |
39 @segments = split(/\//, $pathInfo, $this->trailingSlash ? -1 : 0); | |
40 | |
41 # remove first segment since it's always empty | |
42 shift @segments; | |
43 | |
44 my ($obj,$view) = (pop(@segments) =~ m/(.*?)(?:\.(\w+))?$/); | |
45 push @segments, $obj; | |
46 | |
47 } | |
48 | |
49 return @segments; | |
50 } | |
51 | |
32 | 52 |
33 sub Invoke { | 53 sub Invoke { |
34 my ($this,$action) = @_; | 54 my ($this,$action) = @_; |
35 | 55 |
36 my $query = $action->query; | 56 my $method = $action->requestMethod; |
37 | 57 |
38 my $method = $query->request_method; | 58 my @segments = $this->GetResourcePath($action); |
39 | 59 |
40 #TODO: path_info is broken for IIS | 60 my $res = $this->rootResource; |
41 my $pathInfo = $query->path_info; | |
42 my @segments; | |
43 | |
44 if (length $pathInfo) { | |
45 | |
46 @segments = split /\//, $pathInfo, -1; # keep trailing empty string if present | |
47 | |
48 # remove first segment since it's always empty | |
49 shift @segments; | |
50 | |
51 my ($obj,$view) = (pop(@segments) =~ m/(.*?)(?:\.(\w+))?$/); | |
52 push @segments, $obj; | |
53 | |
54 } | |
55 | |
56 | |
57 my $res = $this->contract->Transform($this->root, { id => '' } ); | |
58 | 61 |
59 while(@segments) { | 62 while(@segments) { |
60 my $id = shift @segments; | 63 my $id = shift @segments; |
61 | 64 |
62 $res = $res->FetchChildResource($id,$action); | 65 $res = $res->FetchChildResource($id); |
63 | 66 |
64 die NotFoundException->new($pathInfo,$id) unless $res; | 67 die NotFoundException->new($pathInfo,$id) unless $res; |
65 } | 68 } |
66 | 69 |
67 $res = $res->InvokeHttpMethod($method,$action); | 70 $res = $res->InvokeHttpVerb($method,$action); |
68 } | 71 } |
69 | 72 |
70 1; | 73 1; |
71 | 74 |
72 __END__ | 75 __END__ |
73 | 76 |
74 =pod | 77 =pod |
75 | 78 |
76 =head1 NAME | 79 =head1 NAME |
77 | 80 |
78 C<IMPL::Web::Handler::RestController> - Транслирует запросы к ресурсам в вызовы методов. | 81 C<IMPL::Web::Handler::RestController> - Обрабатывает C<HTTP> запрос передавая |
82 его соответствующему ресурсу. | |
79 | 83 |
80 =head1 SYNOPSIS | 84 =head1 SYNOPSIS |
81 | 85 |
82 Использует контракты для преобразования стандартных C<REST> запросов в вызовы методов объектов. | 86 Используется в конфигурации приложения как элемент цепочки обработчиков. |
83 C<$ENV{PATH_INFO}> используется как путь к нужному ресурсу у которого будет вызван метод указанный в запросе. | 87 Как правило располагается на самом верхнем уровне. |
88 | |
89 =begin code xml | |
90 | |
91 <handlers type="ARRAY"> | |
92 <item type="IMPL::Web::Handler::RestController"> | |
93 <rootResource type="My::App::Web::RootResource"/> | |
94 </item> | |
95 <item type="IMPL::Web::Handler::JSONView" /> | |
96 <item type="IMPL::Web::Handler::SecureCookie" /> | |
97 <item type="IMPL::Web::Handler::ErrorHandler" /> | |
98 </handlers> | |
99 | |
100 =end code xml | |
101 | |
84 | 102 |
85 =head1 DESCRIPTION | 103 =head1 DESCRIPTION |
86 | 104 |
87 =head2 Resource model | 105 Использует C<PATH_INFO> для определения нужного ресурса, затем предает |
106 найденному ресурсу управление для обработки запроса. | |
88 | 107 |
89 Ресурсы имеют иерархическую структуру, аналогичную файлам и каталогам, которая описывается контрактом, также | 108 Если ресурс не найден, то возникает исключение C<IMPL::Web::NotFoundException>. |
90 контрак описывает то, как должны обрабатываться методы C<HTTP> запроса, такие как C<GET> и C<POST>. | |
91 | 109 |
92 За корректность реализации данных методов отвечает разработчик. | 110 Для определения нужного ресурса контроллер разбивает C<PATH_INFO> на фрагменты |
111 и использует каждый фрагмент для получения дочернего ресурса начиная с корневого. | |
112 Для чего используется метод | |
113 C<< IMPL::Web::Application::ResourceInterface->FetchChildResource($childId) >>. | |
93 | 114 |
94 Каждый ресурс представляет собой коллкецию вложенных ресурсов, путь указанный в C<HTTP> запросе разбивается на | 115 =head1 MEMEBERS |
95 части, затем каждый сегмент последовательно используется для поиска дочернего ресурса. При обработки | |
96 первого сегмента используется корневой ресурс. Корневой ресурс должен существовать всегда. | |
97 | 116 |
98 =head2 Contract | 117 =head2 C<[get]rootResource> |
99 | 118 |
100 Контрактом может быть любое преобразование которое определяет соответсвие между объектами приложения и | 119 Корневой ресурс приложения, должен быть всегда и реализовывать интерфес ресурса |
101 ресурсами, доступными через протокол C<HTTP>. | 120 C<IMPL::Web::Application::ResourceInterface>. |
102 | 121 |
122 =head2 C<[get]trailingSlash> | |
103 | 123 |
104 | 124 Если данная переменная имеет значение C<true>, то слеш в конце пути к ресурсу |
125 будет интерпретироваться, как дочерний ресурс с пустым идентификатором. | |
105 | 126 |
106 =cut | 127 =cut |