fix #27 : handling routes with trailing slash

This commit is contained in:
Jaeseung Ha 2015-01-19 19:03:06 +09:00
parent b5942c4dda
commit 07042b55fd
4 changed files with 897 additions and 768 deletions

File diff suppressed because it is too large Load Diff

View File

@ -56,6 +56,12 @@ int main()
return "About Crow example."; return "About Crow example.";
}); });
// a request to /path should be forwarded to /path/
CROW_ROUTE(app, "/path/")
([](){
return "Trailing slash test case..";
});
// simple json response // simple json response
CROW_ROUTE(app, "/json") CROW_ROUTE(app, "/json")
([]{ ([]{

View File

@ -276,6 +276,8 @@ namespace crow
friend class Router; friend class Router;
}; };
const int RULE_SPECIAL_REDIRECT_SLASH = 1;
class Trie class Trie
{ {
public: public:
@ -607,14 +609,28 @@ public:
class Router class Router
{ {
public: public:
Router() : rules_(1) {} Router() : rules_(2)
{
}
template <uint64_t N> template <uint64_t N>
typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule) typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
{ {
using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>; using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
auto ruleObject = new RuleT(rule); auto ruleObject = new RuleT(rule);
rules_.emplace_back(ruleObject); rules_.emplace_back(ruleObject);
trie_.add(rule, rules_.size() - 1); trie_.add(rule, rules_.size() - 1);
// directory case:
// request to `/about' url matches `/about/' rule
if (rule.size() > 1 && rule.back() == '/')
{
std::string rule_without_trailing_slash = rule;
rule_without_trailing_slash.pop_back();
trie_.add(rule_without_trailing_slash, RULE_SPECIAL_REDIRECT_SLASH);
}
return *ruleObject; return *ruleObject;
} }
@ -633,6 +649,7 @@ public:
auto found = trie_.find(req.url); auto found = trie_.find(req.url);
unsigned rule_index = found.first; unsigned rule_index = found.first;
CROW_LOG_DEBUG << "???" << rule_index;
if (!rule_index) if (!rule_index)
{ {
@ -645,6 +662,24 @@ public:
if (rule_index >= rules_.size()) if (rule_index >= rules_.size())
throw std::runtime_error("Trie internal structure corrupted!"); throw std::runtime_error("Trie internal structure corrupted!");
if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
{
CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
res = response(301);
// TODO absolute url building
if (req.get_header_value("Host").empty())
{
res.add_header("Location", req.url + "/");
}
else
{
res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/");
}
res.end();
return;
}
if ((rules_[rule_index]->methods() & (1<<(uint32_t)req.method)) == 0) if ((rules_[rule_index]->methods() & (1<<(uint32_t)req.method)) == 0)
{ {
CROW_LOG_DEBUG << "Rule found but method mismatch: " << req.url << " with " << method_name(req.method) << "(" << (uint32_t)req.method << ") / " << rules_[rule_index]->methods(); CROW_LOG_DEBUG << "Rule found but method mismatch: " << req.url << " with " << method_name(req.method) << "(" << (uint32_t)req.method << ") / " << rules_[rule_index]->methods();

View File

@ -116,6 +116,59 @@ TEST(ParameterTagging)
static_assert(std::is_same<black_magic::S<uint64_t, double, int64_t>, black_magic::arguments<6*6+6*3+2>::type>::value, "tag to type container"); static_assert(std::is_same<black_magic::S<uint64_t, double, int64_t>, black_magic::arguments<6*6+6*3+2>::type>::value, "tag to type container");
} }
TEST(PathRouting)
{
SimpleApp app;
CROW_ROUTE(app, "/file")
([]{
return "file";
});
CROW_ROUTE(app, "/path/")
([]{
return "path";
});
{
request req;
response res;
req.url = "/file";
app.handle(req, res);
ASSERT_EQUAL(200, res.code);
}
{
request req;
response res;
req.url = "/file/";
app.handle(req, res);
ASSERT_EQUAL(404, res.code);
}
{
request req;
response res;
req.url = "/path";
app.handle(req, res);
ASSERT_NOTEQUAL(404, res.code);
}
{
request req;
response res;
req.url = "/path/";
app.handle(req, res);
ASSERT_EQUAL(200, res.code);
}
}
TEST(RoutingTest) TEST(RoutingTest)
{ {
SimpleApp app; SimpleApp app;