Skip to content

Commit

Permalink
vibe.web.web: Support JSON more natively
Browse files Browse the repository at this point in the history
  • Loading branch information
wilzbach committed Jul 18, 2017
1 parent be6862a commit 820c13e
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 11 deletions.
46 changes: 39 additions & 7 deletions tests/vibe.web.web/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module app;

import vibe.core.core;
import vibe.core.log;
import vibe.data.json;
import vibe.http.client;
import vibe.http.router;
import vibe.http.server;
Expand All @@ -10,20 +11,45 @@ import std.format : format;

// TODO: test the various parameter and return type combinations, as well as all attributes

struct MyStruct
{
int foo = 2;
}

class Service {
@noRoute void getFoo(HTTPServerResponse res) { res.writeBody("oops"); }
void getBar(HTTPServerResponse res) { res.writeBody("ok"); }
// you can work with Json objects directly
auto postJson(Json json) { return json; }
// for POST/PUT requests: incoming objects are automatically serialized to Json
// by default an unknown return type is serialized to Json
auto postStruct(MyStruct st) { return st.foo + 3; }
}

shared static this()
{
auto settings = new HTTPServerSettings;
settings.bindAddresses = ["127.0.0.1"];
settings.port = 9132;

auto router = new URLRouter;
router.registerWebInterface(new Service);

listenHTTP(settings, router);

runTask({
scope (exit) exitEventLoop();

void postJson(V)(string url, V[string] payload, HTTPStatus expected, scope void delegate(scope HTTPClientResponse res) expectedHandler) {
requestHTTP("http://127.0.0.1:9132"~url,
(scope req) {
req.method = HTTPMethod.POST;
req.writeJsonBody(payload);
},
(scope res) {
assert(res.statusCode == expected, format("Unexpected status code for %s: %s", url, res.statusCode));
expectedHandler(res);
}
);
}
void test(string url, HTTPStatus expected) {
requestHTTP("http://127.0.0.1:9132"~url,
(scope req) {
Expand All @@ -36,11 +62,17 @@ shared static this()
}
test("/foo", HTTPStatus.notFound);
test("/bar", HTTPStatus.ok);

postJson("/json", ["foo": "bar"], HTTPStatus.ok, (scope res) {
auto j = res.readJson;
assert(j["foo"].get!string == "bar");
});

postJson("/struct", ["foo": 5], HTTPStatus.ok, (scope res) {
auto j = res.readJson;
assert(j.get!int == 8);
});

logInfo("All web tests succeeded.");
});
}

class Service {
@noRoute void getFoo(HTTPServerResponse res) { res.writeBody("oops"); }
void getBar(HTTPServerResponse res) { res.writeBody("ok"); }
}
23 changes: 19 additions & 4 deletions web/vibe/web/web.d
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ URLRouter registerWebInterface(C : Object, MethodStyle method_style = MethodStyl
alias RT = ReturnType!overload;
enum minfo = extractHTTPMethodAndName!(overload, true)();
enum url = minfo.hadPathUDA ? minfo.url : adjustMethodStyle(minfo.url, method_style);

static if (findFirstUDA!(NoRouteAttribute, overload).found) {
import vibe.core.log : logDebug;
logDebug("Method %s.%s annotated with @noRoute - not generating a route entry.", C.stringof, M);
Expand Down Expand Up @@ -856,6 +855,10 @@ private void handleRequest(string M, alias overload, C, ERROR...)(HTTPServerRequ
else static if (is(PT == InputStream)) params[i] = req.bodyReader;
else static if (is(PT == HTTPServerRequest) || is(PT == HTTPRequest)) params[i] = req;
else static if (is(PT == HTTPServerResponse) || is(PT == HTTPResponse)) params[i] = res;
else static if (is(PT == Json)) {
enforceBadRequest(req.json.type != Json.Type.undefined, "Invalid Json passed.");
params[i] = req.json;
}
else static if (is(PT == WebSocket)) {} // handled below
else static if (param_names[i].startsWith("_")) {
if (auto pv = param_names[i][1 .. $] in req.params) {
Expand All @@ -868,8 +871,16 @@ private void handleRequest(string M, alias overload, C, ERROR...)(HTTPServerRequ
} else static if (is(PT == bool)) {
params[i] = param_names[i] in req.form || param_names[i] in req.query;
} else {
import std.algorithm.comparison : among;

enum has_default = !is(default_values[i] == void);
ParamResult pres = readFormParamRec(req, params[i], param_names[i], !has_default, nested_style, err);
ParamResult pres = void;
if (req.method.among(HTTPMethod.POST, HTTPMethod.PUT) && req.json.type != Json.Type.undefined && i == 0) {
params[i].setVoid(req.json.deserializeJson!PT);
pres = ParamResult.ok;
} else {
pres = readFormParamRec(req, params[i], param_names[i], !has_default, nested_style, err);
}
static if (has_default) {
if (pres == ParamResult.skipped)
params[i].setVoid(default_values[i]);
Expand Down Expand Up @@ -964,8 +975,12 @@ private void handleRequest(string M, alias overload, C, ERROR...)(HTTPServerRequ
} else {
res.writeBody(ret);
}
} else {
static assert(is(RET == void), M~": Only InputStream, Json and void are supported as return types for route methods.");
} else static if (is(RET : string)) {
res.writeBody(ret);
} else {
// Serialize to Json by default
static if (!is(RET : void))
res.writeJsonBody(ret);
}
}
} catch (Exception ex) {
Expand Down

0 comments on commit 820c13e

Please sign in to comment.