|
| 1 | +#include <thinger/http_server.hpp> |
| 2 | +#include <iostream> |
| 3 | + |
| 4 | +using namespace thinger; |
| 5 | + |
| 6 | +// This example demonstrates JSON Schema validation on route handlers. |
| 7 | +// Invalid requests are automatically rejected with 400 Bad Request. |
| 8 | + |
| 9 | +int main(int argc, char* argv[]) { |
| 10 | + http::server server; |
| 11 | + |
| 12 | + // POST /api/users - Create a user with required fields |
| 13 | + server.post("/api/users", [](nlohmann::json& json, http::response& res) { |
| 14 | + // json is already validated — guaranteed to have name and email as strings |
| 15 | + res.json({ |
| 16 | + {"created", true}, |
| 17 | + {"name", json["name"]}, |
| 18 | + {"email", json["email"]} |
| 19 | + }, http::http_response::status::created); |
| 20 | + }).schema({ |
| 21 | + {"type", "object"}, |
| 22 | + {"required", {"name", "email"}}, |
| 23 | + {"properties", { |
| 24 | + {"name", {{"type", "string"}, {"minLength", 1}}}, |
| 25 | + {"email", {{"type", "string"}}}, |
| 26 | + {"age", {{"type", "integer"}, {"minimum", 0}, {"maximum", 150}}} |
| 27 | + }}, |
| 28 | + {"additionalProperties", false} |
| 29 | + }); |
| 30 | + |
| 31 | + // PUT /api/users/:id - Update a user (partial update allowed) |
| 32 | + server.put("/api/users/:id", [](http::request& req, nlohmann::json& json, http::response& res) { |
| 33 | + res.json({ |
| 34 | + {"updated", true}, |
| 35 | + {"id", req["id"]}, |
| 36 | + {"fields", json} |
| 37 | + }); |
| 38 | + }).schema({ |
| 39 | + {"type", "object"}, |
| 40 | + {"properties", { |
| 41 | + {"name", {{"type", "string"}, {"minLength", 1}}}, |
| 42 | + {"email", {{"type", "string"}}}, |
| 43 | + {"age", {{"type", "integer"}, {"minimum", 0}}} |
| 44 | + }} |
| 45 | + }); |
| 46 | + |
| 47 | + // POST /api/orders - Nested object with enum validation |
| 48 | + server.post("/api/orders", [](nlohmann::json& json, http::response& res) { |
| 49 | + res.json({ |
| 50 | + {"order_id", "ORD-001"}, |
| 51 | + {"status", json["status"]}, |
| 52 | + {"items_count", json["items"].size()} |
| 53 | + }, http::http_response::status::created); |
| 54 | + }).schema({ |
| 55 | + {"type", "object"}, |
| 56 | + {"required", {"items", "status", "shipping"}}, |
| 57 | + {"properties", { |
| 58 | + {"status", { |
| 59 | + {"type", "string"}, |
| 60 | + {"enum", {"pending", "confirmed", "shipped"}} |
| 61 | + }}, |
| 62 | + {"items", { |
| 63 | + {"type", "array"}, |
| 64 | + {"minItems", 1}, |
| 65 | + {"items", { |
| 66 | + {"type", "object"}, |
| 67 | + {"required", {"product", "quantity"}}, |
| 68 | + {"properties", { |
| 69 | + {"product", {{"type", "string"}}}, |
| 70 | + {"quantity", {{"type", "integer"}, {"minimum", 1}}} |
| 71 | + }} |
| 72 | + }} |
| 73 | + }}, |
| 74 | + {"shipping", { |
| 75 | + {"type", "object"}, |
| 76 | + {"required", {"city", "country"}}, |
| 77 | + {"properties", { |
| 78 | + {"city", {{"type", "string"}}}, |
| 79 | + {"country", {{"type", "string"}}}, |
| 80 | + {"zip", {{"type", "string"}}} |
| 81 | + }} |
| 82 | + }} |
| 83 | + }} |
| 84 | + }); |
| 85 | + |
| 86 | + // Endpoint without schema — accepts any JSON |
| 87 | + server.post("/api/raw", [](nlohmann::json& json, http::response& res) { |
| 88 | + res.json({{"received", true}, {"data", json}}); |
| 89 | + }); |
| 90 | + |
| 91 | + uint16_t port = 8090; |
| 92 | + if (argc > 1) port = std::stoi(argv[1]); |
| 93 | + |
| 94 | + std::cout << "Schema Validation Server starting on port " << port << std::endl; |
| 95 | + std::cout << "\nTry these requests:\n" << std::endl; |
| 96 | + |
| 97 | + std::cout << "# Valid user creation:" << std::endl; |
| 98 | + std::cout << R"(curl -X POST http://localhost:)" << port |
| 99 | + << R"(/api/users -H "Content-Type: application/json" -d '{"name":"Alice","email":"alice@example.com","age":30}')" << std::endl; |
| 100 | + |
| 101 | + std::cout << "\n# Missing required field (returns 400):" << std::endl; |
| 102 | + std::cout << R"(curl -X POST http://localhost:)" << port |
| 103 | + << R"(/api/users -H "Content-Type: application/json" -d '{"name":"Alice"}')" << std::endl; |
| 104 | + |
| 105 | + std::cout << "\n# Wrong type (returns 400):" << std::endl; |
| 106 | + std::cout << R"(curl -X POST http://localhost:)" << port |
| 107 | + << R"(/api/users -H "Content-Type: application/json" -d '{"name":123,"email":"test@test.com"}')" << std::endl; |
| 108 | + |
| 109 | + std::cout << "\n# Valid order with nested objects:" << std::endl; |
| 110 | + std::cout << R"(curl -X POST http://localhost:)" << port |
| 111 | + << R"(/api/orders -H "Content-Type: application/json" -d '{"status":"pending","items":[{"product":"Widget","quantity":3}],"shipping":{"city":"Madrid","country":"Spain"}}')" << std::endl; |
| 112 | + |
| 113 | + std::cout << std::endl; |
| 114 | + |
| 115 | + server.start("0.0.0.0", port); |
| 116 | + return 0; |
| 117 | +} |
0 commit comments