Refactor RestService interfaces.
This commit is contained in:
@@ -14,47 +14,39 @@ bool RestRequestHandler::Bind(RestServicePtr service, const std::string& url,
|
||||
}
|
||||
|
||||
void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
||||
const HttpRequest& request = connection->request();
|
||||
const HttpRequest& http_request = connection->request();
|
||||
|
||||
Url url(request.url(), true);
|
||||
Url url(http_request.url(), /*decode*/true);
|
||||
|
||||
if (!url.IsValid()) {
|
||||
connection->SendResponse(HttpStatus::kBadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> sub_matches;
|
||||
RestServicePtr service = service_manager_.GetService(url.path(),
|
||||
&sub_matches);
|
||||
RestRequest rest_request{
|
||||
http_request.method(), http_request.content(), url.query()
|
||||
};
|
||||
|
||||
// Get service by URL path.
|
||||
RestServicePtr service = service_manager_.GetService(
|
||||
url.path(), &rest_request.url_sub_matches);
|
||||
|
||||
if (!service) {
|
||||
LOG_WARN("No service matches the URL: %s", url.path().c_str());
|
||||
connection->SendResponse(HttpStatus::kBadRequest);
|
||||
LOG_WARN("No service matches the URL path: %s", url.path().c_str());
|
||||
connection->SendResponse(HttpStatus::kNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
UrlQuery query;
|
||||
if (request.method() == kHttpGet) {
|
||||
// Suppose URL query is only available for HTTP GET.
|
||||
Url::SplitQuery(url.query(), &query);
|
||||
RestResponse rest_response;
|
||||
service->Handle(rest_request, &rest_response);
|
||||
|
||||
if (!rest_response.content.empty()) {
|
||||
connection->SetResponseContent(std::move(rest_response.content),
|
||||
kAppJsonUtf8);
|
||||
}
|
||||
|
||||
std::string content;
|
||||
bool ok = service->Handle(request.method(), sub_matches, query,
|
||||
request.content(), &content);
|
||||
if (!ok) {
|
||||
connection->SendResponse(HttpStatus::kBadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!content.empty()) {
|
||||
connection->SetResponseContent(std::move(content), kAppJsonUtf8);
|
||||
}
|
||||
|
||||
if (request.method() == kHttpPost) {
|
||||
connection->SendResponse(HttpStatus::kCreated);
|
||||
} else {
|
||||
connection->SendResponse(HttpStatus::kOK);
|
||||
}
|
||||
// Send response back to client.
|
||||
connection->SendResponse(rest_response.status);
|
||||
}
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
+22
-39
@@ -6,51 +6,34 @@ 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);
|
||||
void RestListService::Handle(const RestRequest& request,
|
||||
RestResponse* response) {
|
||||
if (request.method == kHttpGet) {
|
||||
Get(UrlQuery(request.url_query_str), response);
|
||||
} else if (request.method == kHttpPost) {
|
||||
Post(request.content, response);
|
||||
} else {
|
||||
LOG_ERRO("RestListService doesn't support '%s' method.",
|
||||
request.method.c_str());
|
||||
}
|
||||
|
||||
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, query, response_content);
|
||||
void RestDetailService::Handle(const RestRequest& request,
|
||||
RestResponse* response) {
|
||||
if (request.method == kHttpGet) {
|
||||
Get(request.url_sub_matches, UrlQuery(request.url_query_str), response);
|
||||
} else if (request.method == kHttpPut) {
|
||||
Put(request.url_sub_matches, request.content, response);
|
||||
} else if (request.method == kHttpPatch) {
|
||||
Patch(request.url_sub_matches, request.content, response);
|
||||
} else if (request.method == kHttpDelete) {
|
||||
Delete(request.url_sub_matches, response);
|
||||
} else {
|
||||
LOG_ERRO("RestDetailService doesn't support '%s' method.",
|
||||
request.method.c_str());
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
+37
-38
@@ -14,28 +14,40 @@
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/url.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class UrlQuery;
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct RestRequest {
|
||||
// HTTP method (GET, POST, etc.).
|
||||
const std::string& method;
|
||||
|
||||
// Request content (JSON string).
|
||||
const std::string& content;
|
||||
|
||||
// Query string of the URL (only for GET).
|
||||
const std::string& url_query_str;
|
||||
|
||||
// Regex sub-matches of the URL (usually resource ID's).
|
||||
std::vector<std::string> url_sub_matches;
|
||||
};
|
||||
|
||||
struct RestResponse {
|
||||
HttpStatus::Enum status;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Base class for your REST service.
|
||||
class RestService {
|
||||
public:
|
||||
virtual ~RestService() {
|
||||
}
|
||||
virtual ~RestService() = default;
|
||||
|
||||
// Handle REST request, output the response.
|
||||
// The regex sub-matches of the URL (usually resource IDs) were stored in
|
||||
// |url_sub_matches|. The |query| part of the URL is normally only for GET
|
||||
// request. Both the request and response contents are JSON strings.
|
||||
virtual 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) = 0;
|
||||
// Handle REST request, output response.
|
||||
virtual void Handle(const RestRequest& request, RestResponse* response) = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<RestService> RestServicePtr;
|
||||
@@ -44,22 +56,16 @@ 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;
|
||||
void Handle(const RestRequest& request, RestResponse* response) final;
|
||||
|
||||
protected:
|
||||
RestListService() = default;
|
||||
|
||||
virtual bool Get(const UrlQuery& query, std::string* response_content) {
|
||||
return false;
|
||||
virtual void Get(const UrlQuery& query, RestResponse* response) {
|
||||
}
|
||||
|
||||
virtual bool Post(const std::string& request_content,
|
||||
std::string* response_content) {
|
||||
return false;
|
||||
virtual void Post(const std::string& request_content,
|
||||
RestResponse* response) {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -67,33 +73,26 @@ class RestListService : public RestService {
|
||||
|
||||
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;
|
||||
void Handle(const RestRequest& request, RestResponse* response) final;
|
||||
|
||||
protected:
|
||||
virtual bool Get(const std::vector<std::string>& url_sub_matches,
|
||||
virtual void Get(const std::vector<std::string>& url_sub_matches,
|
||||
const UrlQuery& query,
|
||||
std::string* response_content) {
|
||||
return false;
|
||||
RestResponse* response) {
|
||||
}
|
||||
|
||||
virtual bool Put(const std::vector<std::string>& url_sub_matches,
|
||||
virtual void Put(const std::vector<std::string>& url_sub_matches,
|
||||
const std::string& request_content,
|
||||
std::string* response_content) {
|
||||
return false;
|
||||
RestResponse* response) {
|
||||
}
|
||||
|
||||
virtual bool Patch(const std::vector<std::string>& url_sub_matches,
|
||||
virtual void Patch(const std::vector<std::string>& url_sub_matches,
|
||||
const std::string& request_content,
|
||||
std::string* response_content) {
|
||||
return false;
|
||||
RestResponse* response) {
|
||||
}
|
||||
|
||||
virtual bool Delete(const std::vector<std::string>& url_sub_matches) {
|
||||
return false;
|
||||
virtual void Delete(const std::vector<std::string>& url_sub_matches,
|
||||
RestResponse* response) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+24
-26
@@ -166,6 +166,30 @@ bool SplitKeyValue(const std::string& kv, std::string* key,
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
UrlQuery::UrlQuery(const std::string& str) {
|
||||
if (!str.empty()) {
|
||||
// Split into key value pairs separated by '&'.
|
||||
for (std::size_t i = 0; i != std::string::npos;) {
|
||||
std::size_t j = str.find_first_of('&', i);
|
||||
|
||||
std::string kv;
|
||||
if (j == std::string::npos) {
|
||||
kv = str.substr(i);
|
||||
i = std::string::npos;
|
||||
} else {
|
||||
kv = str.substr(i, j - i);
|
||||
i = j + 1;
|
||||
}
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
if (SplitKeyValue(kv, &key, &value)) {
|
||||
Add(std::move(key), std::move(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UrlQuery::UrlQuery(const std::map<std::string, std::string>& map) {
|
||||
for (auto& pair : map) {
|
||||
Add(pair.first, pair.second);
|
||||
@@ -256,32 +280,6 @@ std::vector<std::string> Url::SplitPath(const std::string& path) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// static
|
||||
void Url::SplitQuery(const std::string& str, UrlQuery* query) {
|
||||
const std::size_t NPOS = std::string::npos;
|
||||
|
||||
// Split into key value pairs separated by '&'.
|
||||
std::size_t i = 0;
|
||||
while (i != NPOS) {
|
||||
std::size_t j = str.find_first_of('&', i);
|
||||
|
||||
std::string kv;
|
||||
if (j == NPOS) {
|
||||
kv = str.substr(i);
|
||||
i = NPOS;
|
||||
} else {
|
||||
kv = str.substr(i, j - i);
|
||||
i = j + 1;
|
||||
}
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
if (SplitKeyValue(kv, &key, &value)) {
|
||||
query->Add(std::move(key), std::move(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Url::Init(const std::string& str) {
|
||||
std::size_t pos = str.find('?');
|
||||
if (pos == std::string::npos) {
|
||||
|
||||
+3
-3
@@ -24,6 +24,9 @@ class UrlQuery {
|
||||
|
||||
UrlQuery() = default;
|
||||
|
||||
// The query string should be key value pairs separated by '&'.
|
||||
explicit UrlQuery(const std::string& str);
|
||||
|
||||
// Construct from key-value pairs.
|
||||
explicit UrlQuery(const std::map<std::string, std::string>& map);
|
||||
|
||||
@@ -84,9 +87,6 @@ class Url {
|
||||
// Split a path into its hierarchical components.
|
||||
static std::vector<std::string> SplitPath(const std::string& path);
|
||||
|
||||
// Split query string into key-value parameters.
|
||||
static void SplitQuery(const std::string& str, UrlQuery* query);
|
||||
|
||||
private:
|
||||
void Init(const std::string& str);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user