拆分核心库和服务
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
namespace drogon {
|
||||
enum HttpStatusCode {
|
||||
k200OK = 200,
|
||||
k400BadRequest = 400,
|
||||
k404NotFound = 404,
|
||||
k422UnprocessableEntity = 422,
|
||||
k500InternalServerError = 500
|
||||
};
|
||||
enum ContentType {
|
||||
CT_APPLICATION_JSON
|
||||
};
|
||||
enum HttpMethod {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Patch,
|
||||
Delete
|
||||
};
|
||||
namespace internal {
|
||||
class HttpConstraint {
|
||||
public:
|
||||
HttpConstraint(HttpMethod method) : method_(method), is_method_(true) {}
|
||||
HttpConstraint(std::string filter) : filter_(std::move(filter)) {}
|
||||
bool is_method() const noexcept {
|
||||
return is_method_;
|
||||
}
|
||||
HttpMethod method() const noexcept {
|
||||
return method_;
|
||||
}
|
||||
const std::string& filter() const noexcept {
|
||||
return filter_;
|
||||
}
|
||||
private:
|
||||
HttpMethod method_{Get};
|
||||
bool is_method_{};
|
||||
std::string filter_;
|
||||
};
|
||||
}
|
||||
class HttpResponse {
|
||||
public:
|
||||
static std::shared_ptr<HttpResponse> newHttpResponse() {
|
||||
return std::make_shared<HttpResponse>();
|
||||
}
|
||||
void setStatusCode(HttpStatusCode value) {
|
||||
status = value;
|
||||
}
|
||||
void setContentTypeCode(ContentType value) {
|
||||
content_type = value;
|
||||
}
|
||||
void setBody(std::string value) {
|
||||
body = std::move(value);
|
||||
}
|
||||
HttpStatusCode status{k200OK};
|
||||
ContentType content_type{CT_APPLICATION_JSON};
|
||||
std::string body;
|
||||
};
|
||||
using HttpResponsePtr = std::shared_ptr<HttpResponse>;
|
||||
class HttpRequest {
|
||||
public:
|
||||
std::string_view getBody() const noexcept {
|
||||
return body;
|
||||
}
|
||||
const std::string& getParameter(const std::string& name) const {
|
||||
const auto iterator = parameters.find(name);
|
||||
return iterator == parameters.end() ? empty_parameter_ : iterator->second;
|
||||
}
|
||||
std::string body;
|
||||
std::string user;
|
||||
std::map<std::string, std::string> parameters;
|
||||
private:
|
||||
inline static const std::string empty_parameter_;
|
||||
};
|
||||
using HttpRequestPtr = std::shared_ptr<HttpRequest>;
|
||||
class HttpAppFramework {
|
||||
public:
|
||||
using Callback = std::function<void(const HttpResponsePtr&)>;
|
||||
using Handler = std::function<void(const HttpRequestPtr&, Callback&&)>;
|
||||
using Parameter_Handler = std::function<void(const HttpRequestPtr&, Callback&&, std::uint64_t)>;
|
||||
template <class Function>
|
||||
void registerHandler(const std::string& path, Function&& function, const std::vector<internal::HttpConstraint>& constraints) {
|
||||
HttpMethod method = Get;
|
||||
std::vector<std::string> filters;
|
||||
for(const auto& constraint : constraints) {
|
||||
if(constraint.is_method()) {
|
||||
method = constraint.method();
|
||||
} else {
|
||||
filters.push_back(constraint.filter());
|
||||
}
|
||||
}
|
||||
Route route;
|
||||
route.path = path;
|
||||
route.method = method;
|
||||
route.filters = std::move(filters);
|
||||
if constexpr(std::is_invocable_v<Function, const HttpRequestPtr&, Callback&&, std::uint64_t>) {
|
||||
route.parameter_handler = Parameter_Handler(std::forward<Function>(function));
|
||||
} else {
|
||||
route.handler = Handler(std::forward<Function>(function));
|
||||
}
|
||||
routes_.push_back(std::move(route));
|
||||
}
|
||||
void handle(const std::string& path, HttpMethod method, const HttpRequestPtr& request, Callback callback) {
|
||||
for(auto& route : routes_) {
|
||||
if(route.method != method) {
|
||||
continue;
|
||||
}
|
||||
if(route.handler && route.path == path) {
|
||||
route.handler(request, std::move(callback));
|
||||
return;
|
||||
}
|
||||
std::uint64_t parameter{};
|
||||
if(route.parameter_handler && match_parameter_route(route.path, path, parameter)) {
|
||||
route.parameter_handler(request, std::move(callback), parameter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("route is not registered");
|
||||
}
|
||||
const std::vector<std::string>& filters(const std::string& path, HttpMethod method) const {
|
||||
for(const auto& route : routes_) {
|
||||
if(route.path == path && route.method == method) {
|
||||
return route.filters;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("route is not registered");
|
||||
}
|
||||
private:
|
||||
struct Route {
|
||||
std::string path;
|
||||
HttpMethod method{Get};
|
||||
Handler handler;
|
||||
Parameter_Handler parameter_handler;
|
||||
std::vector<std::string> filters;
|
||||
};
|
||||
static bool match_parameter_route(const std::string& pattern, const std::string& path, std::uint64_t& parameter) {
|
||||
const auto begin = pattern.find('{');
|
||||
if(begin == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
const auto end = pattern.find('}', begin);
|
||||
if(end == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
const std::string_view prefix(pattern.data(), begin);
|
||||
const std::string_view suffix(pattern.data() + end + 1, pattern.size() - end - 1);
|
||||
if(path.size() < prefix.size() + suffix.size() || !std::string_view(path).starts_with(prefix) || !std::string_view(path).ends_with(suffix)) {
|
||||
return false;
|
||||
}
|
||||
const std::string_view value(path.data() + prefix.size(), path.size() - prefix.size() - suffix.size());
|
||||
const auto [parsed_end, error] = std::from_chars(value.data(), value.data() + value.size(), parameter);
|
||||
return error == std::errc{} && parsed_end == value.data() + value.size();
|
||||
}
|
||||
std::vector<Route> routes_;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user