-
Notifications
You must be signed in to change notification settings - Fork 35
RFC: RFC web respond
Vert.x Web has a new experimental api RoutingContext#respond()
. This Method is a helper for chaining vert.x Future or future like results to a web handler. Unlike other methods, this specific method is very opinionated as it attempts to address common use patterns and reduce boilerplate code.
- When the result of the
Future
isnull
the status code is204
and no content is returned. - When the result of the
Future
is an instanceofBuffer
then the headerContent-Type
is set toapplication/octet-stream
and status code is 200. - When the result of the
Future
is an instanceofString
then the headerContent-Type
is set totext/html
and status code is 200. - Otherwise, the result of the
Future
is passes toRoutingContext#json()
which will add the content typeapplication/json
with status code 200.
It is a common best practice to return 201
as status code for successful POST/PUT/PATCH
responses. This means that the HTTP verb should be taken into consideration when dealing with successful responses.
The question we need to still address is the case of null
. Should we override the default value of 204
no content (for these HTTP verbs too?)
https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.30
Also states: The Location response-header field is used to redirect the recipient to a location other than the Request-URI for completion of the request or identification of a new resource. For 201 (Created) responses, the Location is that of the new resource which was created by the request.
The question to address right now is, should we also add the Location
header? or use the presence of Location
header to assume that the user wants 201 as status code?
Any non String/Buffer
result will be assumed to be JSON
, we should try to reuse the codec functionality used in vertx-web-client
to perform custom content encoding.
The current implementation assumes that all non Buffer
or String
data is a JSON
. A hypothetical API would be:
ctx.respond(Codec.XML, ctx -> db.findProductById(ctx.get("productId")))
In this case, the codec would be responsible to provide the header Content-Type
value as well as perform the encoding from Object
to Buffer
. In this case, the previous rules would still apply however the default content type header values would always be replaced by the codec one.
ReadStream
is not supported ATM, we could easily allow it thanks to a Pipe
. The main problem to address would be how to handle content encoding.
- We either assume that
stream
s are opaque and just pipe without setting any specific content-type - We need to build on top of the previous item (
CODEC
s)
A less intrusive alternative would be to recommend user to use Future.compose()
to address any deviations from the default behavior, for example, a user wishing to have the custom 201 status code would:
ctx.respond(ctx ->
db
.findProductById(ctx.get("productId"))
.compose(prod -> {
ctx.response().setStatusCode(201);
return Future.succeededFuture(prod);
}));
As suggested by @tsegismont a better approach is to use the onSuccess
handler which is executed before the future is returned:
ctx.respond(ctx ->
db
.findProductById(ctx.get("productId"))
.onSuccess(prod -> ctx.response().setStatusCode(201)));
This approach also ensures that the end user doesn't need to remember to return the future again.
The same can be said about codec
support:
ctx.respond(ctx ->
db
.findProductById(ctx.get("productId"))
.map(this::convertToXML));
// ...
public <T> Future<Buffer> convertToXML(T source) {
// ... conversion
return Future.succeededFuture(buffer);
}