Quick start Logging TLS Authentication IPFilter Safe cetan-rest-101 CETAN REST C++ API cetan-rest-201 Running the CETAN Server Inside Docker Download Support Contact

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

CETAN REST 201 - create user

Advanced Features Demonstrated

  • Dynamic URI parameters using Param<T>
  • File uploads using FileUpload
  • Get entire Request using Request parameter
  • Get AuthenticatedUser using as parameter
  • JSON responses using JSON object
  • Logging using SLog
  • Resource registration using ResourceBuilder

Supported Resource Parameter Types

The following parameter types may be used in REST resource handlers:

  • Param<T>
  • Header
  • FileUpload
  • QueryString
  • Request
  • Stream
  • Uri
  • AuthenticatedUser

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.