SlideShare a Scribd company logo
1 of 32
Download to read offline
Web::Machine
Simpl{e,y} HTTP?!
Web::Machine?
➔ Simple HTTP State Machine…
➔ Represented as Resource classes
◆ See API Design talk
➔ Provides hooks for HTTP states
➔ Replacement for MVC style Web
Frameworks
➔ Plack compatible
PLACK
use Plack::Builder;
use Bean::API;
builder {
mount ‘/’ => Bean::API->as_psgi_app
}
sub as_psgi_app {
my ($self) = @_;
$self = ref $self ? $self : $self->new;
$self->router->add_route('/users/:id' =>
validations => {
id => Int,
},
target => sub {
my ($request, $id) = @_;
my $app = Web::Machine->new(
resource => Bean::API::Resources::User',
resource_args => [
user_id => $id,
]
)->to_app;
return $app->($request->env);
}
);
return Plack::App::Path::Router->new(router => $self->router)->to_app;
}
sub as_psgi_app {
my ($self) = @_;
$self = ref $self ? $self : $self->new;
$self->router->add_route('/users/:id' =>
validations => {
id => Int,
},
target => sub {
my ($request, $id) = @_;
my $app = Web::Machine->new(
resource => Bean::API::Resources::User',
resource_args => [
user_id => $id,
]
)->to_app;
return $app->($request->env);
}
);
return Plack::App::Path::Router->new(router => $self->router)->to_app;
}
my $app = Web::Machine->new(
# Resource is a Web::Machine::Resource subclass
resource => Bean::API::Resources::User',
# resource_args are passed to the Resource subclass on initialization
resource_args => [
user_id => $id,
]
)->to_app;
my $app = Web::Machine->new(
# Resource is a Web::Machine::Resource subclass
resource => Bean::API::Resources::User',
# resource_args are passed to the Resource subclass on initialization
resource_args => [
user_id => $id,
]
)->to_app;
my $app = Web::Machine->new(
# Resource is a Web::Machine::Resource subclass
resource => Bean::API::Resources::User',
# resource_args are passed to the Resource subclass on initialization
resource_args => [
user_id => $id,
]
)->to_app;
sub as_psgi_app {
my ($self) = @_;
$self = ref $self ? $self : $self->new;
$self->router->add_route('/users/:id' =>
validations => {
id => Int,
},
target => sub {
my ($request, $id) = @_;
my $app = Web::Machine->new(
resource => Bean::API::Resources::User',
resource_args => [
user_id => $id,
]
)->to_app;
return $app->($request->env);
}
);
return Plack::App::Path::Router->new(router => $self->router)->to_app;
}
Resource Class
➔ @ISA
◆ Web::Machine::Resource
➔ use Moo{se} for fun and profit
➔ Kinda like a controller.
➔ has request, has response.
package Bean::API::Resources::User;
use Moo;
extends ‘Web::Machine::Resource’;
1;
package Bean::API::Resources::User;
use Moo;
extends ‘Web::Machine::Resource’;
use Types::Standard qw/Int/
has user_id => (
is => ‘ro’,
isa => Int,
required => 1
);
1;
sub as_psgi_app {
my ($self) = @_;
$self = ref $self ? $self : $self->new;
$self->router->add_route('/users/:id' =>
validations => {
id => Int,
},
target => sub {
my ($request, $id) = @_;
my $app = Web::Machine->new(
resource => Bean::API::Resources::User',
resource_args => [
user_id => $id,
]
)->to_app;
return $app->($request->env);
}
);
return Plack::App::Path::Router->new(router => $self->router)->to_app;
}
package Bean::API::Resources::User;
use Moo;
extends ‘Web::Machine::Resource’;
use Types::Standard qw/Str/;
has user_id => (
isa => Str,
is => ‘ro’,
required => 1
);
has user => (
is => ‘lazy’,
isa => Maybe[‘Bean::Schema::ResultUser’],
builder => 1,
);
...
sub _build_user {
return $schema->resultset(‘User’)->find($self->user_id);
}
...
sub resource_exists { 1 } sub service_available { 1 }
sub is_authorized { 1 } sub forbidden { 0 }
sub allow_missing_post { 0 } sub malformed_request { 0 }
sub uri_too_long { 0 } sub known_content_type { 1 }
sub valid_content_headers { 1 } sub valid_entity_length { 1 }
sub options { +{} } sub allowed_methods { [ qw[GET HEAD] ] }
sub known_methods { [qw[ GET HEAD POST PUT DELETE TRACE CONNECT OPTIONS ]]}
sub delete_resource { 0 } sub delete_completed { 1 }
sub post_is_create { 0 } sub create_path { undef }
sub base_uri { undef } sub process_post { 0 }
sub content_types_provided { [] } sub content_types_accepted { [] }
sub charsets_provided { [] } sub default_charset {}
sub languages_provided { [] } sub encodings_provided { { 'identity' => sub { $_[1] } } }
sub variances { [] } sub is_conflict { 0 }
sub multiple_choices { 0 } sub previously_existed { 0 }
sub moved_permanently { 0 } sub moved_temporarily { 0 }
sub last_modified { undef } sub expires { undef }
sub generate_etag { undef } sub finish_request {}
sub create_path_after_handler { 0 }
sub resource_exists { 1 } sub service_available { 1 }
sub is_authorized { 1 } sub forbidden { 0 }
sub allow_missing_post { 0 } sub malformed_request { 0 }
sub uri_too_long { 0 } sub known_content_type { 1 }
sub valid_content_headers { 1 } sub valid_entity_length { 1 }
sub options { +{} } sub allowed_methods { [ qw[ GET HEAD ] ] }
sub known_methods { [qw[ GET HEAD POST PUT DELETE TRACE CONNECT OPTIONS ]] }
sub delete_resource { 0 } sub delete_completed { 1 }
sub post_is_create { 0 } sub create_path { undef }
sub base_uri { undef } sub process_post { 0 }
sub content_types_provided { [] } sub content_types_accepted { [] }
sub charsets_provided { [] } sub default_charset {}
sub languages_provided { [] } sub encodings_provided { { 'identity' => sub { $_[1] } } }
sub variances { [] } sub is_conflict { 0 }
sub multiple_choices { 0 } sub previously_existed { 0 }
sub moved_permanently { 0 } sub moved_temporarily { 0 }
sub last_modified { undef } sub expires { undef }
sub generate_etag { undef } sub finish_request {}
sub create_path_after_handler { 0 }
GET
➔ content_types_provided
◆ Takes a list of
● Hashrefs
◆ Key: Content-Type
◆ Value: callback for data output
◆ First item is default content-type
➔ resource_exists
◆ returns 404 if this returns false
➔ Auto encoding if charset
package Bean::API::Resources::User;
…
# TRUE ? continue : return 404 RESOURCE NOT FOUND
sub resource_exists {
return !! $self->user;
}
sub content_types_provided {
return [
{‘application/json’ => ‘user_to_json’},
{‘text/html’ => ‘user_to_html’},
{‘application/x-tar => ‘user_to_tar’}
]
}
…
use CPanel::JSON::XS;
use Template::Toolkit;
sub user_to_json {
# May not handle inflated data! But fine for simple data
return encode_json({$self->user->get_columns});
}
sub user_to_html {
return $mason->run(‘/user’)->output;
}
...
package Bean::API::Resources::User;
…
sub resource_exists {
return !! $self->user;
}
# HashRefs of content type and handler name
# FOR GET REQUESTS
sub content_types_provided {
return [
{‘application/json’ => ‘user_to_json’},
{‘text/html’ => ‘user_to_html’},
{‘application/x-tar => ‘user_to_tar’}
];
}
...
package Bean::API::Resources::User;
…
sub resource_exists {
return !! $self->user;
}
sub content_types_provided {
return [
{‘application/json’ => ‘user_to_json’},
{‘text/html’ => ‘user_to_html’},
{‘application/x-tar => ‘user_to_tar’}
];
}
...
…
use CPanel::JSON::XS;
use Template::Toolkit;
sub user_to_json {
# May not handle inflated data! But fine for simple data
return encode_json({$self->user->get_columns});
}
sub user_to_html {
return $mason->run(‘/user’)->output;
}
...
package Bean::API::Resources::User;
…
sub resource_exists {
return !! $self->user;
}
sub content_types_provided {
return [
{‘application/json’ => ‘user_to_json’},
{‘text/html’ => ‘user_to_html’},
{‘application/x-tar => ‘user_to_tar’}
];
}
...
…
use CPanel::JSON::XS;
use Mason;
sub user_to_json {
# May not handle inflated data! But fine for simple data
return encode_json({$self->user->get_columns});
}
sub user_to_html {
return $mason->run(‘/user’)->output;
}
...
Authentication
➔ Authn/Authz
◆ Authentication (401) -> is_authorized(‘Authorization’)
◆ Authorization (403) -> forbidden()
➔ Can be a role for all resources
sub _basic_authn {
my ($self, $authn_string) = @_;
my ($username, $password) =
split(‘:’, decode_base64($authn_string));
my $is_authenticated =
$self->schema->resultset(‘User’)
->find({username => $username})
->is_authenticated($password);
if ($is_authenticated){
return 1;
} else {
return create_header(WWWAuthenticate => [
‘Basic’ => (realm => ”BB-LDAP”’)
]);
}
}
package Bean::API::Auth;
use Moo::Role;
use Web::Machine::Utils qw/create_header/;
# HTTP is bad at the distinction between Authz and
Authn
sub is_authorized {
my ($self, $authn_header) = @_;
my ($authn_type, $authn_string) =
split(‘ ‘, $authn_header);
if ($authn_type eq ‘Basic’){
return $self->_basic_authn($authn_string);
}
}
sub _basic_authn {
my ($self, $authn_string) = @_;
my ($username, $password) =
split(‘:’, decode_base64($authn_string));
my $is_authenticated =
$self
->schema
->resultset(‘User’)
->find({username => $username})
->is_authenticated($password);
if ($is_authenticated){
return 1;
} else {
return ‘Basic realm=”BB-LDAP”’
}
}
package Bean::API::Auth;
use Moo::Role;
# HTTP is bad at the distinction between Authz and
Authn
sub is_authorized {
my ($self, $authn_header) = @_;
my ($authn_type, $authn_string) =
split(‘ ‘, $authn_header);
if ($authn_type eq ‘Basic’){
return $self->_basic_authn($authn_string);
}
}
sub _basic_authn {
my ($self, $authn_string) = @_;
my ($username, $password) =
split(‘:’, decode_base64($authn_string));
my $is_authenticated =
$self
->schema
->resultset(‘User’)
->find({username => $username})
->is_authenticated($password);
if ($is_authenticated){
return 1;
} else {
return ‘Basic realm=”BB-LDAP”’
}
}
package Bean::API::Auth;
use Moo::Role;
# HTTP is bad at the distinction between Authz and
Authn
sub is_authorized {
my ($self, $authn_header) = @_;
my ($authn_type, $authn_string) =
split(‘ ‘, $authn_header);
if ($authn_type eq ‘Basic’){
return $self->_basic_authn($authn_string);
}
}
package Bean::API::Resources::User;
# HTTP is bad at the distinction between Authz and Authn
sub forbidden {
my ($self) = @_;
my $is_authorized = 0;
if ($self->request->method eq ‘GET’){
$is_authorized = $self->active_user->can_retrieve($self->user);
}
return $is_authorized;
}
PUT
➔ content_types_accepted
◆ Takes a list of
● Hashrefs
◆ Key: Content-Type
◆ Value: callback for data input
◆ First item is default content-type
package Bean::API::Resources::User;
sub content_types_accepted {
return [
{‘application/json’ => ‘user_from_json’},
];
}
# PUT user with 204 response
sub user_from_json {
my ($self) = @_;
$self->user->delete;
$self->user->create(decode_json($self->request->body));
}
POST
➔ post_is_create
◆ create_path / create_path_after_handler
◆ Then the post is treated like a PUT
➔ process_post
◆ handles all other post request
◆ No Support for Content-type based handlers
package Bean::API::Resources::User;
sub process_post {
my ($self) = @_;
# Do whatever you want
# No. really. anything!
# Return a status code
# If you set the response->body then it will be encoded with the correct charset if set
}
package Bean::API::Resources::User;
sub content_types_accepted {
return [
{‘application/json’ => ‘user_from_json’},
{‘application/x-webform-url-encoded’ => ‘user_from_webform’},
];
}
sub user_from_json {
# create/update user (POST/PUT)
}
sub post_is_create {1}
sub create_path_after_handler {return ‘/user/’.$self->user->id)}
DELETE
➔ delete_resource
◆ true if delete was/appears successful
◆ false if delete failed
➔ delete_completed
◆ delete appears successful but isn’t complete
Discussion
Questions, Answers, Understanding

More Related Content

What's hot

The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony TechniquesKris Wallsmith
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patternsSamuel ROZE
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Kris Wallsmith
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICKonstantin Kudryashov
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDAleix Vergés
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mockingKonstantin Kudryashov
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixturesBill Chang
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2Hugo Hamon
 
Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201Fabien Potencier
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patternsSamuel ROZE
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016Kacper Gunia
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of LithiumNate Abele
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 WorldFabien Potencier
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Fabien Potencier
 
WordPress for developers - phpday 2011
WordPress for developers -  phpday 2011WordPress for developers -  phpday 2011
WordPress for developers - phpday 2011Maurizio Pelizzone
 

What's hot (20)

The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2
 
Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of Lithium
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
WordPress for developers - phpday 2011
WordPress for developers -  phpday 2011WordPress for developers -  phpday 2011
WordPress for developers - phpday 2011
 

Similar to Web::Machine - Simpl{e,y} HTTP

WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hackingJeroen van Dijk
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hackingJeroen van Dijk
 
Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Anatoly Sharifulin
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Shinya Ohyanagi
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐいHisateru Tanaka
 
Using WordPress as your application stack
Using WordPress as your application stackUsing WordPress as your application stack
Using WordPress as your application stackPaul Bearne
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsMichael Peacock
 
I Phone On Rails
I Phone On RailsI Phone On Rails
I Phone On RailsJohn Wilker
 
Mashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsMashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsBastian Hofmann
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryTatsuhiko Miyagawa
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda Katz
 
Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworksdiego_k
 
Curscatalyst
CurscatalystCurscatalyst
CurscatalystKar Juan
 
Services Drupalcamp Stockholm 2009
Services Drupalcamp Stockholm 2009Services Drupalcamp Stockholm 2009
Services Drupalcamp Stockholm 2009hugowetterberg
 

Similar to Web::Machine - Simpl{e,y} HTTP (20)

Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
 
Blog Hacks 2011
Blog Hacks 2011Blog Hacks 2011
Blog Hacks 2011
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
RESTful web services
RESTful web servicesRESTful web services
RESTful web services
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
Using WordPress as your application stack
Using WordPress as your application stackUsing WordPress as your application stack
Using WordPress as your application stack
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friends
 
I Phone On Rails
I Phone On RailsI Phone On Rails
I Phone On Rails
 
Mashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsMashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web Apps
 
Add loop shortcode
Add loop shortcodeAdd loop shortcode
Add loop shortcode
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
Mashing up JavaScript
Mashing up JavaScriptMashing up JavaScript
Mashing up JavaScript
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworks
 
Curscatalyst
CurscatalystCurscatalyst
Curscatalyst
 
Services Drupalcamp Stockholm 2009
Services Drupalcamp Stockholm 2009Services Drupalcamp Stockholm 2009
Services Drupalcamp Stockholm 2009
 

Recently uploaded

MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based projectAnoyGreter
 
cpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptcpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptrcbcrtm
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsSafe Software
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commercemanigoyal112
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Developmentvyaparkranti
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Mater
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfFerryKemperman
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfExploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfkalichargn70th171
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 

Recently uploaded (20)

MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based project
 
cpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptcpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.ppt
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data Streams
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commerce
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Development
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdf
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfExploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 

Web::Machine - Simpl{e,y} HTTP

  • 2. Web::Machine? ➔ Simple HTTP State Machine… ➔ Represented as Resource classes ◆ See API Design talk ➔ Provides hooks for HTTP states ➔ Replacement for MVC style Web Frameworks ➔ Plack compatible
  • 3. PLACK use Plack::Builder; use Bean::API; builder { mount ‘/’ => Bean::API->as_psgi_app }
  • 4. sub as_psgi_app { my ($self) = @_; $self = ref $self ? $self : $self->new; $self->router->add_route('/users/:id' => validations => { id => Int, }, target => sub { my ($request, $id) = @_; my $app = Web::Machine->new( resource => Bean::API::Resources::User', resource_args => [ user_id => $id, ] )->to_app; return $app->($request->env); } ); return Plack::App::Path::Router->new(router => $self->router)->to_app; }
  • 5. sub as_psgi_app { my ($self) = @_; $self = ref $self ? $self : $self->new; $self->router->add_route('/users/:id' => validations => { id => Int, }, target => sub { my ($request, $id) = @_; my $app = Web::Machine->new( resource => Bean::API::Resources::User', resource_args => [ user_id => $id, ] )->to_app; return $app->($request->env); } ); return Plack::App::Path::Router->new(router => $self->router)->to_app; }
  • 6. my $app = Web::Machine->new( # Resource is a Web::Machine::Resource subclass resource => Bean::API::Resources::User', # resource_args are passed to the Resource subclass on initialization resource_args => [ user_id => $id, ] )->to_app;
  • 7. my $app = Web::Machine->new( # Resource is a Web::Machine::Resource subclass resource => Bean::API::Resources::User', # resource_args are passed to the Resource subclass on initialization resource_args => [ user_id => $id, ] )->to_app;
  • 8. my $app = Web::Machine->new( # Resource is a Web::Machine::Resource subclass resource => Bean::API::Resources::User', # resource_args are passed to the Resource subclass on initialization resource_args => [ user_id => $id, ] )->to_app;
  • 9. sub as_psgi_app { my ($self) = @_; $self = ref $self ? $self : $self->new; $self->router->add_route('/users/:id' => validations => { id => Int, }, target => sub { my ($request, $id) = @_; my $app = Web::Machine->new( resource => Bean::API::Resources::User', resource_args => [ user_id => $id, ] )->to_app; return $app->($request->env); } ); return Plack::App::Path::Router->new(router => $self->router)->to_app; }
  • 10. Resource Class ➔ @ISA ◆ Web::Machine::Resource ➔ use Moo{se} for fun and profit ➔ Kinda like a controller. ➔ has request, has response.
  • 11. package Bean::API::Resources::User; use Moo; extends ‘Web::Machine::Resource’; 1;
  • 12. package Bean::API::Resources::User; use Moo; extends ‘Web::Machine::Resource’; use Types::Standard qw/Int/ has user_id => ( is => ‘ro’, isa => Int, required => 1 ); 1; sub as_psgi_app { my ($self) = @_; $self = ref $self ? $self : $self->new; $self->router->add_route('/users/:id' => validations => { id => Int, }, target => sub { my ($request, $id) = @_; my $app = Web::Machine->new( resource => Bean::API::Resources::User', resource_args => [ user_id => $id, ] )->to_app; return $app->($request->env); } ); return Plack::App::Path::Router->new(router => $self->router)->to_app; }
  • 13. package Bean::API::Resources::User; use Moo; extends ‘Web::Machine::Resource’; use Types::Standard qw/Str/; has user_id => ( isa => Str, is => ‘ro’, required => 1 ); has user => ( is => ‘lazy’, isa => Maybe[‘Bean::Schema::ResultUser’], builder => 1, ); ... sub _build_user { return $schema->resultset(‘User’)->find($self->user_id); } ...
  • 14. sub resource_exists { 1 } sub service_available { 1 } sub is_authorized { 1 } sub forbidden { 0 } sub allow_missing_post { 0 } sub malformed_request { 0 } sub uri_too_long { 0 } sub known_content_type { 1 } sub valid_content_headers { 1 } sub valid_entity_length { 1 } sub options { +{} } sub allowed_methods { [ qw[GET HEAD] ] } sub known_methods { [qw[ GET HEAD POST PUT DELETE TRACE CONNECT OPTIONS ]]} sub delete_resource { 0 } sub delete_completed { 1 } sub post_is_create { 0 } sub create_path { undef } sub base_uri { undef } sub process_post { 0 } sub content_types_provided { [] } sub content_types_accepted { [] } sub charsets_provided { [] } sub default_charset {} sub languages_provided { [] } sub encodings_provided { { 'identity' => sub { $_[1] } } } sub variances { [] } sub is_conflict { 0 } sub multiple_choices { 0 } sub previously_existed { 0 } sub moved_permanently { 0 } sub moved_temporarily { 0 } sub last_modified { undef } sub expires { undef } sub generate_etag { undef } sub finish_request {} sub create_path_after_handler { 0 }
  • 15. sub resource_exists { 1 } sub service_available { 1 } sub is_authorized { 1 } sub forbidden { 0 } sub allow_missing_post { 0 } sub malformed_request { 0 } sub uri_too_long { 0 } sub known_content_type { 1 } sub valid_content_headers { 1 } sub valid_entity_length { 1 } sub options { +{} } sub allowed_methods { [ qw[ GET HEAD ] ] } sub known_methods { [qw[ GET HEAD POST PUT DELETE TRACE CONNECT OPTIONS ]] } sub delete_resource { 0 } sub delete_completed { 1 } sub post_is_create { 0 } sub create_path { undef } sub base_uri { undef } sub process_post { 0 } sub content_types_provided { [] } sub content_types_accepted { [] } sub charsets_provided { [] } sub default_charset {} sub languages_provided { [] } sub encodings_provided { { 'identity' => sub { $_[1] } } } sub variances { [] } sub is_conflict { 0 } sub multiple_choices { 0 } sub previously_existed { 0 } sub moved_permanently { 0 } sub moved_temporarily { 0 } sub last_modified { undef } sub expires { undef } sub generate_etag { undef } sub finish_request {} sub create_path_after_handler { 0 }
  • 16. GET ➔ content_types_provided ◆ Takes a list of ● Hashrefs ◆ Key: Content-Type ◆ Value: callback for data output ◆ First item is default content-type ➔ resource_exists ◆ returns 404 if this returns false ➔ Auto encoding if charset
  • 17. package Bean::API::Resources::User; … # TRUE ? continue : return 404 RESOURCE NOT FOUND sub resource_exists { return !! $self->user; } sub content_types_provided { return [ {‘application/json’ => ‘user_to_json’}, {‘text/html’ => ‘user_to_html’}, {‘application/x-tar => ‘user_to_tar’} ] }
  • 18. … use CPanel::JSON::XS; use Template::Toolkit; sub user_to_json { # May not handle inflated data! But fine for simple data return encode_json({$self->user->get_columns}); } sub user_to_html { return $mason->run(‘/user’)->output; } ... package Bean::API::Resources::User; … sub resource_exists { return !! $self->user; } # HashRefs of content type and handler name # FOR GET REQUESTS sub content_types_provided { return [ {‘application/json’ => ‘user_to_json’}, {‘text/html’ => ‘user_to_html’}, {‘application/x-tar => ‘user_to_tar’} ]; } ...
  • 19. package Bean::API::Resources::User; … sub resource_exists { return !! $self->user; } sub content_types_provided { return [ {‘application/json’ => ‘user_to_json’}, {‘text/html’ => ‘user_to_html’}, {‘application/x-tar => ‘user_to_tar’} ]; } ... … use CPanel::JSON::XS; use Template::Toolkit; sub user_to_json { # May not handle inflated data! But fine for simple data return encode_json({$self->user->get_columns}); } sub user_to_html { return $mason->run(‘/user’)->output; } ...
  • 20. package Bean::API::Resources::User; … sub resource_exists { return !! $self->user; } sub content_types_provided { return [ {‘application/json’ => ‘user_to_json’}, {‘text/html’ => ‘user_to_html’}, {‘application/x-tar => ‘user_to_tar’} ]; } ... … use CPanel::JSON::XS; use Mason; sub user_to_json { # May not handle inflated data! But fine for simple data return encode_json({$self->user->get_columns}); } sub user_to_html { return $mason->run(‘/user’)->output; } ...
  • 21. Authentication ➔ Authn/Authz ◆ Authentication (401) -> is_authorized(‘Authorization’) ◆ Authorization (403) -> forbidden() ➔ Can be a role for all resources
  • 22. sub _basic_authn { my ($self, $authn_string) = @_; my ($username, $password) = split(‘:’, decode_base64($authn_string)); my $is_authenticated = $self->schema->resultset(‘User’) ->find({username => $username}) ->is_authenticated($password); if ($is_authenticated){ return 1; } else { return create_header(WWWAuthenticate => [ ‘Basic’ => (realm => ”BB-LDAP”’) ]); } } package Bean::API::Auth; use Moo::Role; use Web::Machine::Utils qw/create_header/; # HTTP is bad at the distinction between Authz and Authn sub is_authorized { my ($self, $authn_header) = @_; my ($authn_type, $authn_string) = split(‘ ‘, $authn_header); if ($authn_type eq ‘Basic’){ return $self->_basic_authn($authn_string); } }
  • 23. sub _basic_authn { my ($self, $authn_string) = @_; my ($username, $password) = split(‘:’, decode_base64($authn_string)); my $is_authenticated = $self ->schema ->resultset(‘User’) ->find({username => $username}) ->is_authenticated($password); if ($is_authenticated){ return 1; } else { return ‘Basic realm=”BB-LDAP”’ } } package Bean::API::Auth; use Moo::Role; # HTTP is bad at the distinction between Authz and Authn sub is_authorized { my ($self, $authn_header) = @_; my ($authn_type, $authn_string) = split(‘ ‘, $authn_header); if ($authn_type eq ‘Basic’){ return $self->_basic_authn($authn_string); } }
  • 24. sub _basic_authn { my ($self, $authn_string) = @_; my ($username, $password) = split(‘:’, decode_base64($authn_string)); my $is_authenticated = $self ->schema ->resultset(‘User’) ->find({username => $username}) ->is_authenticated($password); if ($is_authenticated){ return 1; } else { return ‘Basic realm=”BB-LDAP”’ } } package Bean::API::Auth; use Moo::Role; # HTTP is bad at the distinction between Authz and Authn sub is_authorized { my ($self, $authn_header) = @_; my ($authn_type, $authn_string) = split(‘ ‘, $authn_header); if ($authn_type eq ‘Basic’){ return $self->_basic_authn($authn_string); } }
  • 25. package Bean::API::Resources::User; # HTTP is bad at the distinction between Authz and Authn sub forbidden { my ($self) = @_; my $is_authorized = 0; if ($self->request->method eq ‘GET’){ $is_authorized = $self->active_user->can_retrieve($self->user); } return $is_authorized; }
  • 26. PUT ➔ content_types_accepted ◆ Takes a list of ● Hashrefs ◆ Key: Content-Type ◆ Value: callback for data input ◆ First item is default content-type
  • 27. package Bean::API::Resources::User; sub content_types_accepted { return [ {‘application/json’ => ‘user_from_json’}, ]; } # PUT user with 204 response sub user_from_json { my ($self) = @_; $self->user->delete; $self->user->create(decode_json($self->request->body)); }
  • 28. POST ➔ post_is_create ◆ create_path / create_path_after_handler ◆ Then the post is treated like a PUT ➔ process_post ◆ handles all other post request ◆ No Support for Content-type based handlers
  • 29. package Bean::API::Resources::User; sub process_post { my ($self) = @_; # Do whatever you want # No. really. anything! # Return a status code # If you set the response->body then it will be encoded with the correct charset if set }
  • 30. package Bean::API::Resources::User; sub content_types_accepted { return [ {‘application/json’ => ‘user_from_json’}, {‘application/x-webform-url-encoded’ => ‘user_from_webform’}, ]; } sub user_from_json { # create/update user (POST/PUT) } sub post_is_create {1} sub create_path_after_handler {return ‘/user/’.$self->user->id)}
  • 31. DELETE ➔ delete_resource ◆ true if delete was/appears successful ◆ false if delete failed ➔ delete_completed ◆ delete appears successful but isn’t complete