File Coverage

blib/lib/Web/Request/Role/JSON.pm
Criterion Covered Total %
statement 31 31 100.0
branch 9 10 90.0
condition 6 7 85.7
subroutine 7 7 100.0
pod n/a
total 53 55 96.3


line stmt bran cond sub pod time code
1             package Web::Request::Role::JSON;
2              
3             # ABSTRACT: Make handling JSON easier in Web::Request
4              
5             our $VERSION = '1.008'; # VERSION
6              
7 2     2   6948 use 5.010;
  2         17  
8 2     2   1147 use MooseX::Role::Parameterized;
  2         148182  
  2         11  
9 2     2   75436 use JSON::MaybeXS;
  2         4  
  2         165  
10 2     2   15 use Encode;
  2         3  
  2         1076  
11              
12             parameter 'content_type' => (
13             isa => 'Str',
14             required => 0,
15             default => 'application/json; charset=utf-8',
16             );
17              
18             role {
19             my $p = shift;
20             my $content_type = $p->content_type;
21              
22             method json_payload => sub {
23 6     6   53768 my $self = shift;
24              
25 6 100       21 return unless my $raw = $self->content;
26              
27             # Web::Request->content will decode content based on
28             # $req->encoding, which is utf8 for JSON. So $content has UTF8 flag
29             # on, which means we have to tell JSON::MaybeXS to turn
30             # utf8-handling OFF
31              
32 5         7023 return JSON::MaybeXS->new( utf8 => 0 )->decode($raw);
33              
34             # Alternatives:
35             # - reencode the content (stupid because double the work)
36             # decode_json(encode_utf8($self->content))
37             # - set $self->encoding(undef), and set it back after decoding
38             };
39              
40             method json_response => sub {
41 6     6   210775 my ( $self, $data, $header_ref, $status ) = @_;
42              
43 6   100     43 $status ||= 200;
44 6         8 my $headers;
45 6 100       20 if ($header_ref) {
46 2 100       12 if ( ref($header_ref) eq 'ARRAY' ) {
    50          
47 1         10 $headers = HTTP::Headers->new(@$header_ref);
48             }
49             elsif ( ref($header_ref) eq 'HASH' ) {
50 1         9 $headers = HTTP::Headers->new(%$header_ref);
51             }
52             }
53 6   66     238 $headers ||= HTTP::Headers->new;
54 6         95 $headers->header( 'content-type' => $content_type );
55              
56 6         386 return $self->new_response(
57             headers => $headers,
58             status => $status,
59             content => decode_utf8( encode_json($data) ),
60             );
61             };
62              
63             method json_error => sub {
64 3     3   28530 my ( $self, $message, $status ) = @_;
65 3   100     19 $status ||= 400;
66 3         6 my $body;
67 3 100       32 if ( ref($message) ) {
68 1         11 $body = $message;
69             }
70             else {
71 2         10 $body = { status => 'error', message => "$message" };
72             }
73              
74 3         49 return $self->new_response(
75             headers => [ content_type => $content_type ],
76             status => $status,
77             content => decode_utf8( encode_json($body) ),
78             );
79             };
80             };
81              
82             1;
83              
84             __END__
85              
86             =pod
87              
88             =encoding UTF-8
89              
90             =head1 NAME
91              
92             Web::Request::Role::JSON - Make handling JSON easier in Web::Request
93              
94             =head1 VERSION
95              
96             version 1.008
97              
98             =head1 SYNOPSIS
99              
100             # Create a request handler
101             package My::App::Request;
102             use Moose;
103             extends 'Web::Request';
104             with 'Web::Request::Role::JSON';
105              
106             # Make sure your app uses your request handler, e.g. using OX:
107             package My::App::OX;
108             sub request_class {'My::App::Request'}
109              
110             # Finally, in some controller action
111             sub create_POST {
112             my ($self, $req) = @_;
113              
114             my $data = $req->json_payload;
115             my $created = $self->model->create($data);
116             return $self->json_response($created, undef, 201);
117             }
118              
119             =head1 DESCRIPTION
120              
121             C<Web::Request::Role::JSON> provides a few methods that make handling
122             JSON in L<Web::Request> a bit easier.
123              
124             Please note that all methods return a L<Web::Response> object.
125             Depending on the framework you use (or lack thereof), you might have
126             to call C<finalize> on the response object to turn it into a valid
127             PSGI response.
128              
129             =head2 METHODS
130              
131             =head3 json_payload
132              
133             my $perl_hash = $req->json_payload;
134              
135             Extracts and decodes a JSON payload from the request.
136              
137             =head3 json_response
138              
139             $req->json_response( $data );
140             $req->json_response( $data, $header_ref );
141             $req->json_response( $data, $header_ref, $http_status );
142              
143             Convert your data to JSON and generate a new response with correct HTTP headers.
144              
145             You can pass in more headers as the second argument (either hashref or
146             arrayref). These headers will be passed straight on to
147             C<< HTTP::Headers->new() >>.
148              
149             You can also pass a HTTP status code as the third parameter. If none
150             is provided, we default to C<200>.
151              
152             =head3 json_error
153              
154             $req->json_response( 'something is wrong' );
155             $req->json_response( $error_data );
156             $req->json_response( $error, $status );
157              
158             Generate a JSON object out of your error message, if the message is a
159             plain string. But you can also pass in a data structure that will be
160             converted to JSON.
161              
162             Per default, HTTP status is set to C<400>, but you can pass any other
163             status as a second argument. (Yes, there is no checking if you pass a
164             valid status code or not. You're old enough to not do stupid things..)
165              
166             =head2 PARAMETERS
167              
168             An optional C<content_type> parameter can be added on role application to
169             restore previous behaviour. Browsers tend to like the 'charset=utf-8' better,
170             but you might have your reasons.
171              
172             package MyRequest;
173             extends 'OX::Request';
174             with (
175             'Web::Request::Role::JSON' => { content_type => 'application/json' },
176             );
177              
178             =head1 THANKS
179              
180             Thanks to
181              
182             =over
183              
184             =item *
185              
186             L<validad.com|https://www.validad.com/> for supporting Open Source.
187              
188             =back
189              
190             =head1 AUTHORS
191              
192             =over 4
193              
194             =item *
195              
196             Thomas Klausner <domm@plix.at>
197              
198             =item *
199              
200             Klaus Ita <koki@itascraft.com>
201              
202             =back
203              
204             =head1 COPYRIGHT AND LICENSE
205              
206             This software is copyright (c) 2017 - 2021 by Thomas Klausner.
207              
208             This is free software; you can redistribute it and/or modify it under
209             the same terms as the Perl 5 programming language system itself.
210              
211             =cut