CETAN Documentation
This documentation covers the CETAN Web Application Server, logging, TLS, authentication, IP filtering, Safe secrets, Safe utilities, development environment setup, and building C++ REST web services using the CETAN REST API.
Contents
Building Advanced REST Applications: cetan-rest-201
cetan-rest-201 extends the introductory concepts from
cetan-rest-101 and demonstrates how to build more advanced REST
applications using the CETAN REST C++ API. This example introduces dynamic
parameters, URL‑encoded values, file uploads, JSON responses, logging using
SLog, and deploy multiple applications on a single CETAN server.
Overview
In this example, you will implement a User web service with
multiple REST resources, including a simple greeting endpoint and a more
advanced user‑creation endpoint that accepts an uploaded avatar image and
returns a JSON response.
Project Structure
The project follows the same layout as cetan-rest-101, with
include, src, and lib directories,
and produces a CETAN REST application file named
cetan-rest-201.ctn.
User Service
The User service defines the resources below:
-
GET /user/say-hello/{fname}/{lname}— returns an HTML greeting using dynamic URI parameters. -
POST /user/create— creates a new user, processes an uploaded avatar file, logs activity, and returns a JSON response. -
GET /user/hello— returns an HTML greeting to the authenticated User using dynamic AuthenticatedUser parameters. -
GET /user/authn— returns an JSON greeting to the authenticated User using dynamic Request parameters.
User.h
#ifndef USER_H
#define USER_H
#include "SLog.h"
#include "Param.h"
#include "Request.h"
#include "Resource.h"
#include "Response.h"
#include "FileUpload.h"
#include "WebService.h"
#include "AuthenticatedUser.h"
#include <vector>
#include <string>
using namespace std;
using namespace ctn;
class User : public WebService {
public:
const string tag = "User";
User();
virtual ~User();
private:
SLog slog;
Response get_request(Request req) noexcept;
Response hello(AuthenticatedUser user) noexcept;
Response say_hello(Param fname, Param lname) noexcept;
Response create_user(Param fname, Param lname, FileUpload avatar) noexcept;
vector register_resources() const override;
};
User.cpp
#include "User.h"
#include "JSON.h"
#include "RestAPI201.h"
#include "RouteBuilder.h"
#include "ResourceBuilder.h"
#include <string_view>
User::User() : WebService("/user") {
slog = SLog("RestAPI");
}
Response User::get_request(Request req) noexcept {
JSON json;
Response resp;
try {
resp.status(200);
if(req.authenticated()) {
AuthenticatedUser user = req.get_authenticated_user();
json.push_back("authenticated", true);
json.push_back("user", user.get_display());
}
else {
json.push_back("authenticated", false);
}
resp.header(Response::Content_Type, "application/json");
resp.body(move(json.to_string()));
}
catch(const exception& e) {
slog.error(tag, "exception occurred: ", e.what());
return Response(500);
}
catch(...) {
slog.error(tag, "unknow exception occurred");
return Response(500);
}
return resp;
}
Response User::hello(AuthenticatedUser user) noexcept {
string msg;
Response resp;
msg.append("<html><head>")
.append("<meta charset=\"utf-8\">")
.append("<title>RestAPI User Management</title></head>")
.append("<body><center><font size=\"25\">Hello, ");
if(!user.get_display().empty())
msg.append(user.get_display());
else
msg.append(user.get_id());
msg.append("!</font></center></body></html>");
resp.status(200);
resp.header(Response::Content_Type, "text/html; charset=utf-8");
resp.body(move(msg));
return resp;
}
Response User::say_hello(Param<string> fname, Param<string> lname) noexcept {
string msg;
Response resp;
msg.append("<html><head>")
.append("<meta charset=\"utf-8\">")
.append("<title>RestAPI User Management</title></head>")
.append("<body><center><font size=\"25\">Hello, ")
.append(fname.value())
.append(" ")
.append(lname.value()).append("!</font></center></body></html>");
resp.status(200);
resp.header(Response::Content_Type, "text/html; charset=utf-8");
resp.body(move(msg));
return resp;
}
Response User::create_user(Param<string> fname, Param<string> lname, FileUpload avatar) noexcept {
JSON json;
Response resp;
int user_id = 0;
string_view firstname, lastname;
try {
slog.info(tag, "create user");
slog.info(tag, "fname: ", fname.value());
slog.info(tag, "lname: ", lname.value());
//get user first and last name from request
firstname = string_view(fname.value());
lastname = string_view(lname.value());
//call create usr implemented logic
RestAPI201* app = static_cast(get_app());
if((user_id = app->make_user(firstname, lastname)) != -1) {
json.push_back(fname.name(), firstname);
json.push_back(lname.name(), lastname);
json.push_back("id", user_id);
slog.info(tag, "avartar file: ", avatar.filename());
avatar.move_file(this, "images");
json.push_back(avatar.name_id(), avatar.filename());
}
else
json.push_back("error", "user already exists");
resp.status(200);
resp.body(move(json.to_string()));
}
catch(...) {
resp.status(500);
}
return resp;
}
vector User::register_resources() const {
ResourceBuilder builder;
vector res;
auto* r0 = builder.GET("/hello", &User::hello)
.build(AuthenticatedUser());
res.emplace_back(r0);
auto* r1 = builder.GET("/say-hello/{fname}/{lname}", &User::say_hello)
.build(Param<string>("fname"), Param<string>("lname"));
res.emplace_back(r1);
auto r2 = builder.POST("/create-user", &User::create_user)
.produce("application/json")
.build(Param<string>("fname"), Param<string>("lname"), FileUpload("avatar"));
res.emplace_back(r2);
auto r3 = builder.GET("/authn", &User::get_request)
.produce("application/json")
.build(Request());
res.emplace_back(r3);
return res;
}
User::~User() {
}
REST Application
The RestAPI201 class derives from REST and
registers the User service. The resulting application is
deployed under a configurable root URL, such as
/rest-201.
RestAPI201.h
#ifndef RESTAPI201_H
#define RESTAPI201_H
#include "SLog.h"
#include "REST.h"
#include "WebService.h"
#include <set>
using namespace std;
using namespace ctn;
class RestAPI201 : public REST {
public:
RestAPI201();
int make_user(string_view fname, string_view lname);
virtual ~RestAPI201();
private:
SLog slog;
static int id;
REST* create_cetan_app() const override final;
set<WebService*> register_services() const override final;
};
#endif /* RESTAPI201_H */
RestAPI201.cpp
#include "User.h"
#include "REST.h"
#include "RestAPI201.h"
int RestAPI201::id = 1000;
RestAPI201::RestAPI201() {
slog = SLog("RestAPI");
}
REST* RestAPI201::create_cetan_app() const {
return new RestAPI201();
}
int RestAPI201::make_user(string_view fname, string_view lname) {
int usr_id;
static vector<pair<int, pair<string, string>>> users;
for(auto it = users.begin(); it != users.end(); it++) {
if((it->second.first == fname) && (it->second.second == lname)) {
slog.debug(tag, "user already exists");
return -1;
}
}
usr_id = ++id;
users.emplace_back(usr_id, make_pair(fname, lname));
return usr_id;
}
set<WebService*> RestAPI201::register_services() const {
set<WebService*> services;
services.emplace(new User());
return services;
}
RestAPI201::~RestAPI201() {
}
Deployment
After building the project, place cetan-rest-201.ctn in the
server’s ext directory and add an application entry to
cetan_config.xml.
With both cetan-rest-101 and cetan-rest-201 deploy, the application
configuration look like below:
<applications>
<app name="cetan-rest-101">
<root_url>/rest-101</root_url>
<number_thread>1</number_thread>
<app_file>cetan-rest-101.ctn</app_file>
</app>
<app name="cetan-rest-201">
<root_url>/rest-201</root_url>
<number_thread>2</number_thread>
<app_file>cetan-rest-201.ctn</app_file>
<protect_resources>
<uris>/rest-201</uris>
<authn>cetan_basic_file_authn</authn>
</protect_resources>
</app>
</applications>
Restart the CETAN Web Application Server to
activate the new REST application.
Config SLog
Add the below in slog_config.xml file to enable logging.
<target name="restapi_file">
<type>file</type>
<file_name>restapi.log</file_name>
<max_number_file>10</max_number_file>
</target>
<logger name="RestAPI">
<target>restapi_file</target>
<accept_level>DEBUG</accept_level>
</logger>
Testing
Access the greeting endpoint in a browser:
https://your-server.com/rest-201/user/say-hello/John/Smith
Use a REST client to test the user‑creation endpoint:
POST https://your-server.com/rest-201/user/create
Below is a screenshot of a post request and result using Postman
Advanced Features Demonstrated
- Dynamic URI parameters using
Param<T> - File uploads using
FileUpload - Get entire Request using
Requestparameter - Get AuthenticatedUser using as parameter
- JSON responses using
JSONobject - Logging using
SLog - Resource registration using
ResourceBuilder
Supported Resource Parameter Types
The following parameter types may be used in REST resource handlers:
Param<T>HeaderFileUploadQueryStringRequestStreamUriAuthenticatedUser
These parameter types allow handlers to access request metadata, headers, cookies, query strings, uploaded files, and typed values extracted from the URI or request body.