view lib/IMPL/Web/Handler/RestController.pm @ 422:b0481c071bea ref20150831

IMPL::Config::Container tests, YAMLConfiguration now works and tested
author cin
date Sun, 20 Aug 2017 00:20:41 +0300
parents c6e90e02dd17
children
line wrap: on
line source

package IMPL::Web::Handler::RestController;
use strict;

use IMPL::Const qw(:prop);
use IMPL::declare {
	require => {
	    Locator => 'IMPL::Web::AutoLocator',
	    ResourceInterface => 'IMPL::Web::Application::ResourceInterface', 
		Exception => 'IMPL::Exception',
		ArgumentExecption => '-IMPL::InvalidArgumentException',
		NotFoundException => 'IMPL::Web::NotFoundException',
		Loader => 'IMPL::Code::Loader'
	},
	base => {
		'IMPL::Object' => undef,
		'IMPL::Object::Autofill' => '@_',
		'IMPL::Object::Serializable' => undef
	},
	props => [
	   resourceFactory => PROP_RO,
	   trailingSlash => PROP_RO
	]	
};

sub CTOR {
	my ($this) = @_;
	
	die ArgumentException->new(resourceFactory => "A web-resource is required")
	   unless $this->resourceFactory;
	   #unless eval { $this->resourceFacotry->isa(ResourceInterface) };
	 
}

sub GetResourcePath {
    my ($this,$action) = @_;
    
    my $pathInfo = $action->pathInfo;
    my @segments;
    
    if (length $pathInfo) {
    
        @segments = split(/\//, $pathInfo, $this->trailingSlash ? -1 : 0);
        
        # remove first segment if it is empty
        shift @segments if @segments && length($segments[0]) == 0;
    }
    
    return @segments;    
}


sub Invoke {
	my ($this,$request) = @_;
	
	my $method = $request->requestMethod;
	
	my @segments = $this->GetResourcePath($request);
	
	my $factory = $this->resourceFactory;
	
	$factory = Loader->default->Require($factory)
		unless ref($factory) || eval { $factory->can('new') };
	
	my $res = $factory->new(
	   id => 'root',
	   request => $request,
	   location => Locator->new(base => $request->application->baseUrl),
	);
	
	while(@segments) {
		my $id = shift @segments;
		$res = $res->FetchChildResource($id);
	}
	
	$res = $res->InvokeHttpVerb($method);
}

1;

__END__

=pod

=head1 NAME

C<IMPL::Web::Handler::RestController> - Обрабатывает C<HTTP> запрос передавая
его соответствующему ресурсу.

=head1 SYNOPSIS

Используется в конфигурации приложения как элемент цепочки обработчиков.
Как правило располагается на самом верхнем уровне.

=begin code xml

    <handlers type="ARRAY">
        <item type="IMPL::Web::Handler::RestController">
            <resourceFactory>My::App::Web::RootResource</resourceFactory>
        </item>
        <item type="IMPL::Web::Handler::JSONView" />
        <item type="IMPL::Web::Handler::SecureCookie" />
        <item type="IMPL::Web::Handler::ErrorHandler" />
    </handlers>

=end code xml


=head1 DESCRIPTION

Использует C<PATH_INFO> для определения нужного ресурса, затем предает
найденному ресурсу управление для обработки запроса.

Если ресурс не найден, то возникает исключение C<IMPL::Web::NotFoundException>.

Для определения нужного ресурса контроллер разбивает C<PATH_INFO> на фрагменты
и использует каждый фрагмент для получения дочернего ресурса начиная с корневого.
Для чего используется метод
C<< IMPL::Web::Application::ResourceInterface->FetchChildResource($childId) >>.

Дерево ресурсов сущестувет независимо от обрабатываемого запроса, однако оно
может полностью или частично загружаться в начале обработки запроса и
освобождаться по окончании обработки запроса. Поэтому при получении дочерних
ресурсов не участвует C<HTTP> запрос, он адресуется только последнему ресурсу.

=begin text

/music/audio.mp3 -> ['music','audio.mp3']

=end text

=head1 MEMEBERS

=head2 C<[get]resourceFactory>

Фабрика для создания корневого ресурса приложения, полученный ресурс должен
реализовывать интерфейс C<IMPL::Web::Application::ResourceInterface>.

Фабрика может сохранять ссылку на корневой ресурс и каждый раз не создавать
его, а возвращать уже существующий. Это вполне оправдано, если хранение
дерева ресурсов требует меньше ресурсов, чем его создание и при этом приложение
остается в памяти между C<HTTP> запросами.

=head2 C<[get]trailingSlash>

Если данная переменная имеет значение C<true>, то слеш в конце пути к ресурсу
будет интерпретироваться, как дочерний ресурс с пустым идентификатором.

=cut