1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2017 Alexandre Terrasa <alexandre@moz-code.org>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Introduce useful services for JSON REST API handlers.
19 # Validation and Deserialization of request bodies:
25 # # Validator used do validate the body
26 # redef var validator = new MyFormValidator
28 # # Define the kind of objects expected by the deserialization process
29 # redef type BODY: MyForm
31 # redef fun post(req, res) do
32 # var post = validate_body(req, res)
33 # if post == null then return # Validation error: let popcorn return a HTTP 400
34 # var form = deserialize_body(req, res)
35 # if form == null then return # Deserialization error: let popcorn return a HTTP 400
37 # # TODO do something with the input
48 # class MyFormValidator
49 # super ObjectValidator
52 # add new StringField("name", min_size=1, max_size=255)
65 # Validator used to check body input
67 # Here we use the `pop_validation` module to validate JSON document from the request body.
68 var validator
: nullable DocumentValidator = null
70 # Validate body input with `validator`
72 # Try to validate the request body as a json document using `validator`:
73 # * Returns the validated string input if the result of the validation is ok.
74 # * Answers a json error and returns `null` if something went wrong.
75 # * If no `validator` is set, returns the body without validation.
80 # class ValidatedHandler
83 # redef var validator = new MyObjectValidator
85 # redef fun post(req, res) do
86 # var body = validate_body(req, res)
87 # if body == null then return # Validation error
88 # # At this point popcorn returned a HTTP 400 code with the validation error
89 # # if the validation failed.
91 # # TODO do something with the input
96 # class MyObjectValidator
97 # super ObjectValidator
100 # add new StringField("name", min_size=1, max_size=255)
104 fun validate_body
(req
: HttpRequest, res
: HttpResponse): nullable String do
107 var validator
= self.validator
108 if validator
== null then return body
110 if not validator
.validate
(body
) then
111 res
.json
(validator
.validation
, 400)
117 # Deserialize the request body
119 # Returns the deserialized request body body or `null` if something went wrong.
120 # If the object cannot be deserialized, answers with a HTTP 400.
122 # See `BODY` and `new_body_object`.
126 # class MyDeserializedHandler
129 # redef type BODY: MyObject
131 # redef fun post(req, res) do
132 # var form = deserialize_body(req, res)
133 # if form == null then return # Deserialization error
134 # # At this point popcorn returned a HTTP 400 code if something was wrong with
135 # # the deserialization process
137 # # TODO do something with the input
148 fun deserialize_body
(req
: HttpRequest, res
: HttpResponse): nullable BODY do
150 var deserializer
= new JsonDeserializer(body
)
151 var form
= deserializer
.deserialize
(body_type
)
152 if not form
isa BODY or deserializer
.errors
.not_empty
then
153 res
.json_error
("Bad input", 400)
159 # Kind of objects returned by `deserialize_body`
161 # Define it in each sub handlers depending on the kind of objects sent in request bodies.
162 type BODY: Serializable
164 private var body_type
: String is lazy
do return (new GetName[BODY]).to_s
167 redef class HttpResponse
169 # Write data as JSON and set the right content type header.
170 fun json
(json
: nullable Serializable, status
: nullable Int) do
171 header
["Content-Type"] = media_types
["json"].as(not null)
175 send
(json
.to_json
, status
)
179 # Write error as JSON.
181 # Format: `{"message": message, "status": status}`
182 fun json_error
(message
: String, status
: Int) do
183 var obj
= new JsonObject
184 obj
["status"] = status
185 obj
["message"] = message