Add RestListService and RestDetailService; update README.
This commit is contained in:
@@ -2,11 +2,95 @@
|
|||||||
|
|
||||||
A lightweight C++ REST and SOAP client and server library based on *Boost.Asio*.
|
A lightweight C++ REST and SOAP client and server library based on *Boost.Asio*.
|
||||||
|
|
||||||
Please see the `doc` folder for tutorials and `example` folder for examples.
|
## A Quick Look
|
||||||
|
|
||||||
|
### REST Server
|
||||||
|
|
||||||
|
Suppose you want to create a book server, and provide the following operations with RESTful API:
|
||||||
|
|
||||||
|
- Query books based on some criterias.
|
||||||
|
- Add a new book.
|
||||||
|
- Get the detailed information of a book.
|
||||||
|
- Update the information of a book.
|
||||||
|
- Delete a book.
|
||||||
|
|
||||||
|
The first two operations can be implemented by deriving from `webcc::RestListService`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class BookListService : public webcc::RestListService {
|
||||||
|
protected:
|
||||||
|
// Query books based on some criterias.
|
||||||
|
// GET /books?<query>
|
||||||
|
bool Get(const webcc::UrlQuery& query,
|
||||||
|
std::string* response_content) final;
|
||||||
|
|
||||||
|
// Add a new book.
|
||||||
|
// POST /books
|
||||||
|
// The new book's data is attached as request content in JSON format.
|
||||||
|
bool Post(const std::string& request_content,
|
||||||
|
std::string* response_content) final;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The others, derive from `webcc::RestDetailService`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// The URL is like '/book/{BookID}', and the 'url_sub_matches' parameter
|
||||||
|
// contains the matched book ID.
|
||||||
|
class BookDetailService : public webcc::RestDetailService {
|
||||||
|
protected:
|
||||||
|
// Get the detailed information of a book.
|
||||||
|
bool Get(const std::vector<std::string>& url_sub_matches,
|
||||||
|
std::string* response_content) final;
|
||||||
|
|
||||||
|
// Update the information of a book.
|
||||||
|
bool Patch(const std::vector<std::string>& url_sub_matches,
|
||||||
|
const std::string& request_content,
|
||||||
|
std::string* response_content) final;
|
||||||
|
|
||||||
|
// Delete a book.
|
||||||
|
bool Delete(const std::vector<std::string>& url_sub_matches) final;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, all you have to do is to override the proper virtual functions which are named after HTTP methods.
|
||||||
|
|
||||||
|
The detailed implementation is out of the scope of this document, but here is an example:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool BookDetailService::Get(const std::vector<std::string>& url_sub_matches,
|
||||||
|
std::string* response_content) {
|
||||||
|
if (url_sub_matches.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const std::string& book_id = url_sub_matches[0];
|
||||||
|
|
||||||
|
// Get the book by ID from database.
|
||||||
|
// Convert the book details to JSON string.
|
||||||
|
// Assign JSON string to response_content.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Last step, register the services and run the server:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
webcc::RestServer server(8080, 2);
|
||||||
|
|
||||||
|
server.RegisterService(std::make_shared<BookListService>(),
|
||||||
|
"/books");
|
||||||
|
|
||||||
|
server.RegisterService(std::make_shared<BookDetailService>(),
|
||||||
|
"/book/(\\d+)");
|
||||||
|
|
||||||
|
server.Run();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Please see the `example` folder for the complete examples (including the client).**
|
||||||
|
|
||||||
## Build Instructions
|
## Build Instructions
|
||||||
|
|
||||||
A lot of C++11 features are used, e.g., `std::move`. But C++14 is not required. It means that you can still build `webcc` using VS2013.
|
A lot of C++11 features are used, e.g., `std::move`. But C++14 is not required.
|
||||||
|
(It means that you can still build `webcc` using VS2013 on Windows.)
|
||||||
|
|
||||||
[CMake 3.1.0+](https://cmake.org/) is required as the build system. But if you don't use CMake, you can just copy the `src/webcc` folder to your own project then manage it by yourself.
|
[CMake 3.1.0+](https://cmake.org/) is required as the build system. But if you don't use CMake, you can just copy the `src/webcc` folder to your own project then manage it by yourself.
|
||||||
|
|
||||||
@@ -25,3 +109,26 @@ option(WEBCC_BUILD_SOAP_EXAMPLE "Build SOAP example?" ON)
|
|||||||
```
|
```
|
||||||
|
|
||||||
If `WEBCC_ENABLE_SOAP` is `ON`, **pugixml** (already included) is used to parse and compose XML strings.
|
If `WEBCC_ENABLE_SOAP` is `ON`, **pugixml** (already included) is used to parse and compose XML strings.
|
||||||
|
|
||||||
|
### Build On Linux
|
||||||
|
|
||||||
|
Create a build folder under the root (or any other) directory, and `cd` to it:
|
||||||
|
```bash
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
```
|
||||||
|
Generate Makefiles with the following command:
|
||||||
|
```bash
|
||||||
|
cmake -G"Unix Makefiles" \
|
||||||
|
-DWEBCC_ENABLE_LOG=ON \
|
||||||
|
-DWEBCC_ENABLE_SOAP=ON \
|
||||||
|
-DWEBCC_BUILD_UNITTEST=OFF \
|
||||||
|
-DWEBCC_BUILD_REST_EXAMPLE=ON \
|
||||||
|
-DWEBCC_BUILD_SOAP_EXAMPLE=OFF \
|
||||||
|
..
|
||||||
|
```
|
||||||
|
Feel free to change the build options (`ON` or `OFF`).
|
||||||
|
Then make:
|
||||||
|
```bash
|
||||||
|
$ make -j4
|
||||||
|
```
|
||||||
|
|||||||
@@ -69,6 +69,11 @@
|
|||||||
"command": "/usr/bin/c++ -DBOOST_ASIO_NO_DEPRECATED -DWEBCC_ENABLE_LOG -DWEBCC_ENABLE_SOAP -I/home/adam/github/webcc/src -I/home/adam/github/webcc/third_party -std=c++11 -o CMakeFiles/webcc.dir/rest_server.cc.o -c /home/adam/github/webcc/src/webcc/rest_server.cc",
|
"command": "/usr/bin/c++ -DBOOST_ASIO_NO_DEPRECATED -DWEBCC_ENABLE_LOG -DWEBCC_ENABLE_SOAP -I/home/adam/github/webcc/src -I/home/adam/github/webcc/third_party -std=c++11 -o CMakeFiles/webcc.dir/rest_server.cc.o -c /home/adam/github/webcc/src/webcc/rest_server.cc",
|
||||||
"file": "/home/adam/github/webcc/src/webcc/rest_server.cc"
|
"file": "/home/adam/github/webcc/src/webcc/rest_server.cc"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"directory": "/home/adam/github/webcc/build/src/webcc",
|
||||||
|
"command": "/usr/bin/c++ -DBOOST_ASIO_NO_DEPRECATED -DWEBCC_ENABLE_LOG -DWEBCC_ENABLE_SOAP -I/home/adam/github/webcc/src -I/home/adam/github/webcc/third_party -std=c++11 -o CMakeFiles/webcc.dir/rest_service.cc.o -c /home/adam/github/webcc/src/webcc/rest_service.cc",
|
||||||
|
"file": "/home/adam/github/webcc/src/webcc/rest_service.cc"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"directory": "/home/adam/github/webcc/build/src/webcc",
|
"directory": "/home/adam/github/webcc/build/src/webcc",
|
||||||
"command": "/usr/bin/c++ -DBOOST_ASIO_NO_DEPRECATED -DWEBCC_ENABLE_LOG -DWEBCC_ENABLE_SOAP -I/home/adam/github/webcc/src -I/home/adam/github/webcc/third_party -std=c++11 -o CMakeFiles/webcc.dir/url.cc.o -c /home/adam/github/webcc/src/webcc/url.cc",
|
"command": "/usr/bin/c++ -DBOOST_ASIO_NO_DEPRECATED -DWEBCC_ENABLE_LOG -DWEBCC_ENABLE_SOAP -I/home/adam/github/webcc/src -I/home/adam/github/webcc/third_party -std=c++11 -o CMakeFiles/webcc.dir/url.cc.o -c /home/adam/github/webcc/src/webcc/url.cc",
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public:
|
|||||||
|
|
||||||
bool GetBook(const std::string& id) {
|
bool GetBook(const std::string& id) {
|
||||||
webcc::HttpResponse http_response;
|
webcc::HttpResponse http_response;
|
||||||
if (!Request(webcc::kHttpGet, "/books/" + id, "", &http_response)) {
|
if (!Request(webcc::kHttpGet, "/book/" + id, "", &http_response)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ public:
|
|||||||
std::string book_json = Json::writeString(builder, root);
|
std::string book_json = Json::writeString(builder, root);
|
||||||
|
|
||||||
webcc::HttpResponse http_response;
|
webcc::HttpResponse http_response;
|
||||||
if (!Request(webcc::kHttpPost, "/books/" + id, book_json, &http_response)) {
|
if (!Request(webcc::kHttpPost, "/book/" + id, book_json, &http_response)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ public:
|
|||||||
|
|
||||||
bool DeleteBook(const std::string& id) {
|
bool DeleteBook(const std::string& id) {
|
||||||
webcc::HttpResponse http_response;
|
webcc::HttpResponse http_response;
|
||||||
if (!Request(webcc::kHttpDelete, "/books/" + id, "", &http_response)) {
|
if (!Request(webcc::kHttpDelete, "/book/" + id, "", &http_response)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,71 +115,77 @@ static bool BookFromJson(const std::string& json, Book* book) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BookListService::Handle(const std::string& http_method,
|
// Return all books as a JSON array.
|
||||||
const std::vector<std::string>& url_sub_matches,
|
// TODO: Support query parameters.
|
||||||
const webcc::UrlQuery& query,
|
bool BookListService::Get(const webcc::UrlQuery& /* query */,
|
||||||
const std::string& request_content,
|
std::string* response_content) {
|
||||||
std::string* response_content) {
|
Json::Value root(Json::arrayValue);
|
||||||
if (http_method == webcc::kHttpGet) {
|
for (const Book& book : g_book_store.books()) {
|
||||||
// Return all books as a JSON array.
|
root.append(book.ToJson());
|
||||||
|
|
||||||
Json::Value root(Json::arrayValue);
|
|
||||||
for (const Book& book : g_book_store.books()) {
|
|
||||||
root.append(book.ToJson());
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::StreamWriterBuilder builder;
|
|
||||||
*response_content = Json::writeString(builder, root);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (http_method == webcc::kHttpPost) {
|
Json::StreamWriterBuilder builder;
|
||||||
// Add a new book.
|
*response_content = Json::writeString(builder, root);
|
||||||
Book book;
|
|
||||||
if (BookFromJson(request_content, &book)) {
|
|
||||||
return g_book_store.AddBook(book);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new book.
|
||||||
|
// No response content.
|
||||||
|
bool BookListService::Post(const std::string& request_content,
|
||||||
|
std::string* /* response_content */) {
|
||||||
|
Book book;
|
||||||
|
if (BookFromJson(request_content, &book)) {
|
||||||
|
return g_book_store.AddBook(book);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
bool BookDetailService::Handle(const std::string& http_method,
|
bool BookDetailService::Get(const std::vector<std::string>& url_sub_matches,
|
||||||
const std::vector<std::string>& url_sub_matches,
|
std::string* response_content) {
|
||||||
const webcc::UrlQuery& query,
|
|
||||||
const std::string& request_content,
|
|
||||||
std::string* response_content) {
|
|
||||||
if (url_sub_matches.size() != 1) {
|
if (url_sub_matches.size() != 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& book_id = url_sub_matches[0];
|
const std::string& book_id = url_sub_matches[0];
|
||||||
|
|
||||||
if (http_method == webcc::kHttpGet) {
|
const Book& book = g_book_store.GetBook(book_id);
|
||||||
const Book& book = g_book_store.GetBook(book_id);
|
if (!book.IsNull()) {
|
||||||
if (!book.IsNull()) {
|
Json::StreamWriterBuilder builder;
|
||||||
Json::StreamWriterBuilder builder;
|
*response_content = Json::writeString(builder, book.ToJson());
|
||||||
*response_content = Json::writeString(builder, book.ToJson());
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
} else if (http_method == webcc::kHttpPost) {
|
|
||||||
// Update a book.
|
|
||||||
Book book;
|
|
||||||
if (BookFromJson(request_content, &book)) {
|
|
||||||
book.id = book_id;
|
|
||||||
return g_book_store.UpdateBook(book);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
} else if (http_method == webcc::kHttpDelete) {
|
|
||||||
return g_book_store.DeleteBook(book_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update a book partially.
|
||||||
|
bool BookDetailService::Patch(const std::vector<std::string>& url_sub_matches,
|
||||||
|
const std::string& request_content,
|
||||||
|
std::string* response_content) {
|
||||||
|
if (url_sub_matches.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& book_id = url_sub_matches[0];
|
||||||
|
|
||||||
|
Book book;
|
||||||
|
if (BookFromJson(request_content, &book)) {
|
||||||
|
book.id = book_id;
|
||||||
|
return g_book_store.UpdateBook(book);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BookDetailService::Delete(const std::vector<std::string>& url_sub_matches) {
|
||||||
|
if (url_sub_matches.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& book_id = url_sub_matches[0];
|
||||||
|
|
||||||
|
return g_book_store.DeleteBook(book_id);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,48 +3,42 @@
|
|||||||
|
|
||||||
#include "webcc/rest_service.h"
|
#include "webcc/rest_service.h"
|
||||||
|
|
||||||
// NOTE:
|
|
||||||
// XxxListService and XxxDetailService are similar to the XxxListView
|
|
||||||
// and XxxDetailView in Django (a Python web framework).
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// List Service handles the HTTP GET and returns the book list based on
|
// BookListService handles the HTTP GET and returns the book list based on
|
||||||
// query parameters specified in the URL.
|
// query parameters specified in the URL.
|
||||||
// The URL should be like:
|
// The URL should be like:
|
||||||
// - /books
|
// - /books
|
||||||
// - /books?name={BookName}
|
// - /books?name={BookName}
|
||||||
// The query parameters could be regular expressions.
|
// The query parameters could be regular expressions.
|
||||||
class BookListService : public webcc::RestService {
|
class BookListService : public webcc::RestListService {
|
||||||
public:
|
protected:
|
||||||
BookListService() = default;
|
// Return a list of books based on query parameters.
|
||||||
~BookListService() override = default;
|
// URL examples:
|
||||||
|
// - /books
|
||||||
|
// - /books?name={BookName}
|
||||||
|
bool Get(const webcc::UrlQuery& query,
|
||||||
|
std::string* response_content) final;
|
||||||
|
|
||||||
bool Handle(const std::string& http_method,
|
// Create a new book.
|
||||||
const std::vector<std::string>& url_sub_matches,
|
bool Post(const std::string& request_content,
|
||||||
const webcc::UrlQuery& query,
|
std::string* response_content) final;
|
||||||
const std::string& request_content,
|
|
||||||
std::string* response_content) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Detail Service handles the following HTTP methods:
|
// The URL is like '/books/{BookID}', and the 'url_sub_matches' parameter
|
||||||
// - GET
|
// contains the matched book ID.
|
||||||
// - PUT
|
class BookDetailService : public webcc::RestDetailService {
|
||||||
// - PATCH
|
protected:
|
||||||
// - DELETE
|
bool Get(const std::vector<std::string>& url_sub_matches,
|
||||||
// The URL should be like: /books/{BookID}.
|
std::string* response_content) final;
|
||||||
class BookDetailService : public webcc::RestService {
|
|
||||||
public:
|
|
||||||
BookDetailService() = default;
|
|
||||||
~BookDetailService() override = default;
|
|
||||||
|
|
||||||
bool Handle(const std::string& http_method,
|
bool Patch(const std::vector<std::string>& url_sub_matches,
|
||||||
const std::vector<std::string>& url_sub_matches,
|
const std::string& request_content,
|
||||||
const webcc::UrlQuery& query,
|
std::string* response_content) final;
|
||||||
const std::string& request_content,
|
|
||||||
std::string* response_content) override;
|
bool Delete(const std::vector<std::string>& url_sub_matches) final;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BOOK_SERVICE_H_
|
#endif // BOOK_SERVICE_H_
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ int main(int argc, char* argv[]) {
|
|||||||
"/books");
|
"/books");
|
||||||
|
|
||||||
server.RegisterService(std::make_shared<BookDetailService>(),
|
server.RegisterService(std::make_shared<BookDetailService>(),
|
||||||
"/books/(\\d+)");
|
"/book/(\\d+)");
|
||||||
|
|
||||||
server.Run();
|
server.Run();
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ set(SRCS
|
|||||||
queue.h
|
queue.h
|
||||||
rest_server.cc
|
rest_server.cc
|
||||||
rest_server.h
|
rest_server.h
|
||||||
|
rest_service.cc
|
||||||
rest_service.h
|
rest_service.h
|
||||||
url.cc
|
url.cc
|
||||||
url.h
|
url.h
|
||||||
|
|||||||
+4
-2
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#if WEBCC_ENABLE_LOG
|
#if WEBCC_ENABLE_LOG
|
||||||
|
|
||||||
|
#include <cstring> // for strrchr()
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
enum LogLevel {
|
enum LogLevel {
|
||||||
@@ -32,7 +34,7 @@ void LogWrite(int level, const char* file, int line, const char* format, ...);
|
|||||||
#if (defined(WIN32) || defined(_WIN64))
|
#if (defined(WIN32) || defined(_WIN64))
|
||||||
|
|
||||||
// See: https://stackoverflow.com/a/8488201
|
// See: https://stackoverflow.com/a/8488201
|
||||||
#define __FILENAME__ strrchr("\\" __FILE__, '\\') + 1
|
#define __FILENAME__ std::strrchr("\\" __FILE__, '\\') + 1
|
||||||
|
|
||||||
#define LOG_VERB(format, ...) \
|
#define LOG_VERB(format, ...) \
|
||||||
webcc::LogWrite(webcc::VERB, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
webcc::LogWrite(webcc::VERB, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||||
@@ -52,7 +54,7 @@ void LogWrite(int level, const char* file, int line, const char* format, ...);
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
// See: https://stackoverflow.com/a/8488201
|
// See: https://stackoverflow.com/a/8488201
|
||||||
#define __FILENAME__ strrchr("/" __FILE__, '/') + 1
|
#define __FILENAME__ std::strrchr("/" __FILE__, '/') + 1
|
||||||
|
|
||||||
#define LOG_VERB(format, args...) \
|
#define LOG_VERB(format, args...) \
|
||||||
webcc::LogWrite(webcc::VERB, __FILENAME__, __LINE__, format, ##args);
|
webcc::LogWrite(webcc::VERB, __FILENAME__, __LINE__, format, ##args);
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#include "webcc/rest_service.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
bool RestListService::Handle(const std::string& http_method,
|
||||||
|
const std::vector<std::string>& url_sub_matches,
|
||||||
|
const UrlQuery& query,
|
||||||
|
const std::string& request_content,
|
||||||
|
std::string* response_content) {
|
||||||
|
if (http_method == kHttpGet) {
|
||||||
|
return Get(query, response_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http_method == kHttpPost) {
|
||||||
|
return Post(request_content, response_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERRO("RestListService doesn't support '%s' method.", http_method.c_str());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool RestDetailService::Handle(const std::string& http_method,
|
||||||
|
const std::vector<std::string>& url_sub_matches,
|
||||||
|
const UrlQuery& query,
|
||||||
|
const std::string& request_content,
|
||||||
|
std::string* response_content) {
|
||||||
|
if (http_method == kHttpGet) {
|
||||||
|
return Get(url_sub_matches, response_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http_method == kHttpPut) {
|
||||||
|
return Put(url_sub_matches, request_content, response_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http_method == kHttpPatch) {
|
||||||
|
return Patch(url_sub_matches, request_content, response_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http_method == kHttpDelete) {
|
||||||
|
return Delete(url_sub_matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERRO("RestDetailService doesn't support '%s' method.",
|
||||||
|
http_method.c_str());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
#ifndef WEBCC_REST_SERVICE_H_
|
#ifndef WEBCC_REST_SERVICE_H_
|
||||||
#define WEBCC_REST_SERVICE_H_
|
#define WEBCC_REST_SERVICE_H_
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// The design of RestListService and RestDetailService is very similar to
|
||||||
|
// XxxListView and XxxDetailView in Django Rest Framework.
|
||||||
|
// Deriving from them instead of RestService can simplify your own REST services
|
||||||
|
// a lot. But if you find the filtered parameters cannot meet your needs, feel
|
||||||
|
// free to derive from RestService directly.
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -12,6 +19,8 @@ namespace webcc {
|
|||||||
|
|
||||||
class UrlQuery;
|
class UrlQuery;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Base class for your REST service.
|
// Base class for your REST service.
|
||||||
class RestService {
|
class RestService {
|
||||||
public:
|
public:
|
||||||
@@ -34,6 +43,63 @@ public:
|
|||||||
|
|
||||||
typedef std::shared_ptr<RestService> RestServicePtr;
|
typedef std::shared_ptr<RestService> RestServicePtr;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class RestListService : public RestService {
|
||||||
|
public:
|
||||||
|
bool Handle(const std::string& http_method,
|
||||||
|
const std::vector<std::string>& url_sub_matches,
|
||||||
|
const UrlQuery& query,
|
||||||
|
const std::string& request_content,
|
||||||
|
std::string* response_content) final;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RestListService() = default;
|
||||||
|
|
||||||
|
virtual bool Get(const UrlQuery& query,
|
||||||
|
std::string* response_content) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool Post(const std::string& request_content,
|
||||||
|
std::string* response_content) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class RestDetailService : public RestService {
|
||||||
|
public:
|
||||||
|
bool Handle(const std::string& http_method,
|
||||||
|
const std::vector<std::string>& url_sub_matches,
|
||||||
|
const UrlQuery& query,
|
||||||
|
const std::string& request_content,
|
||||||
|
std::string* response_content) final;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool Get(const std::vector<std::string>& url_sub_matches,
|
||||||
|
std::string* response_content) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool Put(const std::vector<std::string>& url_sub_matches,
|
||||||
|
const std::string& request_content,
|
||||||
|
std::string* response_content) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool Patch(const std::vector<std::string>& url_sub_matches,
|
||||||
|
const std::string& request_content,
|
||||||
|
std::string* response_content) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool Delete(const std::vector<std::string>& url_sub_matches) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|
||||||
#endif // WEBCC_REST_SERVICE_H_
|
#endif // WEBCC_REST_SERVICE_H_
|
||||||
|
|||||||
Reference in New Issue
Block a user