asyncgi

2025-12-07 0 786

asyncgi是C ++ 17异步微框架,用于创建Web应用程序与任何支持FastCGI协议的HTTP服务器接口。它旨在提供一种现代化的CGI方式,并在C ++,多线程支持和干净而简单的API中使用自定义性能FastCGI实现:

asyncgi .h>

namespace http = asyncgi ::http;

int main()
{
auto io = asyncgi ::IO{};
auto router = asyncgi ::Router{io};
router.route(\”/\”, http::RequestMethod::Get).process(
[](const asyncgi ::Request&)
{
return http::Response{\”Hello world\”};
});

auto server = asyncgi ::Server{io, router};
server.listen(\”/tmp/fcgi.sock\”);
io.run();
}\”>

# include < asyncgi / asyncgi .h >

namespace http = asyncgi ::http;

int main ()
{
    auto io = asyncgi ::IO{};
    auto router = asyncgi ::Router{io};
    router. route ( \" / \" , http::RequestMethod::Get). process (
        []( const asyncgi ::Request&)
        {
            return http::Response{ \" Hello world \" };
        });

    auto server = asyncgi ::Server{io, router};
    server. listen ( \" /tmp/fcgi.sock \" );
    io. run ();
}

目录

  • 用法
    • 联系
    • 请求处理器
    • 路由器
    • 路由参数
    • 路线上下文
    • 路线匹配者
    • 完整的留言簿示例
    • 计时器
    • 客户
    • 执行ASIO任务
  • 展示柜
  • 发展状况
  • 安装
  • 建筑物例子
  • 运行功能测试
  • 执照

用法

联系

使用asyncgi开发的Web应用程序需要使用Web服务器处理HTTP请求建立FASTCGI连接。大多数流行的服务器提供此功能,例如NGINX可以使用以下配置:

 server {
	listen 8088;
	server_name localhost;
	
	location / {
		try_files $uri @fcgi;
	}
	
	location @fcgi {	
		fastcgi_pass  unix:/tmp/fcgi.sock;		
		#or using a TCP socket
		#fastcgi_pass localhost:9000;
		include fastcgi_params;
	}
}

asyncgi支持UNIX domainTCP插座,以打开FastCGI连接。

请求处理器

为了处理请求,有必要提供满足RequestProcessor要求的函数或功能对象。这意味着该函数必须与以下签名之一无关:

  • http::Response (const asyncgi ::Request&)
  • void (const asyncgi ::Request&, asyncgi ::Responder&)
例子

asyncgi .h>

namespace http = asyncgi ::http;

http::Response guestBookPage(const asyncgi ::Request& request)
{
if (request.path() == \”/\”)
return {R\”(
<h1>Guest book</h1>
<p>No messages</p>
)\”};

return http::ResponseStatus::_404_Not_Found;
}

int main()
{
auto io = asyncgi ::IO{};
auto server = asyncgi ::Server{io, guestBookPage};
//Listen for FastCGI connections on UNIX domain socket
server.listen(\”/tmp/fcgi.sock\”);
//or over TCP
//server.listen(\”127.0.0.1\”, 9088);
io.run();
return 0;
}\”>

 // /examples/example_request_processor.cpp
// /
# include < asyncgi / asyncgi .h >

namespace http = asyncgi ::http;

http::Response guestBookPage ( const asyncgi ::Request& request)
{
    if (request. path () == \" / \" )
        return { R\"(
                <h1>Guest book</h1>
                <p>No messages</p>
            )\" };

    return http::ResponseStatus::_404_Not_Found;
}

int main ()
{
    auto io = asyncgi ::IO{};
    auto server = asyncgi ::Server{io, guestBookPage};
    // Listen for FastCGI connections on UNIX domain socket
    server. listen ( \" /tmp/fcgi.sock \" );
    // or over TCP
    // server.listen(\"127.0.0.1\", 9088);
    io. run ();
    return 0 ;
}

在这里, guestBookPage函数用作请求处理器。实施它的另一种方法是接受对asyncgi ::Responder对象的引用,该对象可用于手动发送响应:

asyncgi ::Responder& responder)
{
if (request.path() == \”/\”)
responder.send(R\”(
<h1>Guest book</h1>
<p>No messages</p>
)\”);

return responder.send(http::ResponseStatus::_404_Not_Found);
}\”>

 void guestBookPage ( const asyncgi ::Request& request, asyncgi ::Responder& responder)
{
    if (request. path () == \" / \" )
        responder. send ( R\"(
                <h1>Guest book</h1>
                <p>No messages</p>
            )\" );

    return responder. send (http::ResponseStatus::_404_Not_Found);
}

这种方法倾向于更详细和容易出错,因此,只有在从请求处理器启动asyncgi操作需要启动异步操作的需要时,才能使用它。这些情况在本文档的后期部分涵盖。

路由器

可以在asyncgi ::Router对象中注册多个请求处理器,并将其与请求中指定的路径匹配。 asyncgi ::Router本身满足RequestProcessor要求。

如果请求处理需要多个线程,则可以将所需数量的工人传递给asyncgi ::IO对象的构造函数。在这种情况下,用户必须确保保护请求处理器中的任何共享数据都受到保护,避免并发读/写入访问。

例子

asyncgi .h>
#include <mutex>

namespace http = asyncgi ::http;
using namespace std::string_literals;

class GuestBookState {
public:
std::vector<std::string> messages()
{
auto lock = std::scoped_lock{mutex_};
return messages_;
}

void addMessage(const std::string& msg)
{
auto lock = std::scoped_lock{mutex_};
messages_.emplace_back(msg);
}

private:
std::vector<std::string> messages_;
std::mutex mutex_;
};

class GuestBookPage {
public:
GuestBookPage(GuestBookState& state)
: state_(&state)
{
}

http::Response operator()(const asyncgi ::Request&)
{
auto messages = state_->messages();
auto page = \”<h1>Guest book</h1>\”s;
if (messages.empty())
page += \”<p>No messages</p>\”;
else
for (const auto& msg : messages)
page += \”<p>\” + msg + \”</p>\”;

page += \”<hr>\”;
page += \”<form method=\\\”post\\\” enctype=\\\”multipart/form-data\\\”>\”
\”<label for=\\\”msg\\\”>Message:</label>\”
\”<input id=\\\”msg\\\” name=\\\”msg\\\” value=\\\”\\\”>\”
\”<input value=\\\”Submit\\\” data-popup=\\\”true\\\” type=\\\”submit\\\”>\”
\”</form>\”;
return page;
}

private:
GuestBookState* state_;
};

class GuestBookAddMessage {
public:
GuestBookAddMessage(GuestBookState& state)
: state_(&state)
{
}

http::Response operator()(const asyncgi ::Request& request)
{
state_->addMessage(std::string{request.formField(\”msg\”)});
return http::Redirect{\”/\”};
}

private:
GuestBookState* state_;
};

int main()
{
auto io = asyncgi ::IO{4}; //4 threads processing requests
auto state = GuestBookState{};
auto router = asyncgi ::Router{io};
router.route(\”/\”, http::RequestMethod::Get).process<GuestBookPage>(state);
router.route(\”/\”, http::RequestMethod::Post).process<GuestBookAddMessage>(state);
router.route().set(http::Response{http::ResponseStatus::_404_Not_Found, \”Page not found\”});
//Alternatively, it\’s possible to pass arguments for creation of http::Response object to the set() method.
//router.route().set(http::ResponseStatus::Code_404_Not_Found, \”Page not found\”);

auto server = asyncgi ::Server{io, router};
server.listen(\”/tmp/fcgi.sock\”);
io.run();
}\”>

 // /examples/example_router.cpp
// /
# include < asyncgi / asyncgi .h >
# include < mutex >

namespace http = asyncgi ::http;
using namespace std ::string_literals ;

class GuestBookState {
public:
    std::vector<std::string> messages ()
    {
        auto lock = std::scoped_lock{mutex_};
        return messages_;
    }

    void addMessage ( const std::string& msg)
    {
        auto lock = std::scoped_lock{mutex_};
        messages_. emplace_back (msg);
    }

private:
    std::vector<std::string> messages_;
    std::mutex mutex_;
};

class GuestBookPage {
public:
    GuestBookPage (GuestBookState& state)
        : state_(&state)
    {
    }

    http::Response operator ()( const asyncgi ::Request&)
    {
        auto messages = state_-> messages ();
        auto page = \" <h1>Guest book</h1> \" s;
        if (messages. empty ())
            page += \" <p>No messages</p> \" ;
        else
            for ( const auto & msg : messages)
                page += \" <p> \" + msg + \" </p> \" ;

        page += \" <hr> \" ;
        page += \" <form method= \\\" post \\\" enctype= \\\" multipart/form-data \\\" > \"
                \" <label for= \\\" msg \\\" >Message:</label> \"
                \" <input id= \\\" msg \\\" name= \\\" msg \\\" value= \\\"\\\" > \"
                \" <input value= \\\" Submit \\\" data-popup= \\\" true \\\" type= \\\" submit \\\" > \"
                \" </form> \" ;
        return page;
    }

private:
    GuestBookState* state_;
};

class GuestBookAddMessage {
public:
    GuestBookAddMessage (GuestBookState& state)
        : state_(&state)
    {
    }

    http::Response operator ()( const asyncgi ::Request& request)
    {
        state_-> addMessage (std::string{request. formField ( \" msg \" )});
        return http::Redirect{ \" / \" };
    }

private:
    GuestBookState* state_;
};

int main ()
{
    auto io = asyncgi ::IO{ 4 }; // 4 threads processing requests
    auto state = GuestBookState{};
    auto router = asyncgi ::Router{io};
    router. route ( \" / \" , http::RequestMethod::Get). process <GuestBookPage>(state);
    router. route ( \" / \" , http::RequestMethod::Post). process <GuestBookAddMessage>(state);
    router. route (). set (http::Response{http::ResponseStatus::_404_Not_Found, \" Page not found \" });
    // Alternatively, it\'s possible to pass arguments for creation of http::Response object to the set() method.
    // router.route().set(http::ResponseStatus::Code_404_Not_Found, \"Page not found\");

    auto server = asyncgi ::Server{io, router};
    server. listen ( \" /tmp/fcgi.sock \" );
    io. run ();
}

路由参数

当使用带有正则表达式的asyncgi ::Router时,请求处理器必须满足ParametrizedRequestProcessor要求。这意味着一个函数对象必须与以下签名之一无关:

  • http::Response void(const TRouteParams&..., const asyncgi ::Request&)
  • void (const TRouteParams&..., const asyncgi ::Request&, asyncgi ::Responder&)

TRouteParams表示从正则表达式的捕获组生成的零或更多参数。例如,可以使用http::Response (int age, string name, const asyncgi ::Request&)签名来处理由asyncgi ::rx{\"/person/(\\\\w+)/age/(\\\\d+)\"}匹配的请求。

ParametrizedRequestProcessor下面的示例GuestBookRemoveMessage

例子

asyncgi .h>
#include <mutex>

using namespace asyncgi ;
using namespace std::string_literals;

class GuestBookState {
public:
std::vector<std::string> messages()
{
auto lock = std::scoped_lock{mutex_};
return messages_;
}

void addMessage(const std::string& msg)
{
auto lock = std::scoped_lock{mutex_};
messages_.emplace_back(msg);
}

void removeMessage(int index)
{
auto lock = std::scoped_lock{mutex_};
if (index < 0 || index >= static_cast<int>(messages_.size()))
return;
messages_.erase(std::next(messages_.begin(), index));
}

private:
std::vector<std::string> messages_;
std::mutex mutex_;
};

std::string makeMessage(int index, const std::string& msg)
{
return msg + R\”(<form action=\”/delete/)\” + std::to_string(index) +
R\”(\” method=\”post\”> <input value=\”Delete\” type=\”submit\”> </form></div>)\”;
}

class GuestBookPage {
public:
explicit GuestBookPage(GuestBookState& state)
: state_{&state}
{
}

http::Response operator()(const asyncgi ::Request&)
{
auto messages = state_->messages();
auto page = \”<h1>Guest book</h1>\”s;
if (messages.empty())
page += \”<p>No messages</p>\”;
else
for (auto i = 0; i < static_cast<int>(messages.size()); ++i)
page += \”<p>\” + makeMessage(i, messages.at(i)) + \”</p>\”;

page += \”<hr>\”;
page += \”<form method=\\\”post\\\” enctype=\\\”multipart/form-data\\\”>\”
\”<label for=\\\”msg\\\”>Message:</label>\”
\”<input id=\\\”msg\\\” name=\\\”msg\\\” value=\\\”\\\”>\”
\”<input value=\\\”Submit\\\” data-popup=\\\”true\\\” type=\\\”submit\\\”>\”
\”</form>\”;
return page;
}

private:
GuestBookState* state_;
};

class GuestBookAddMessage {
public:
explicit GuestBookAddMessage(GuestBookState& state)
: state_{&state}
{
}

http::Response operator()(const asyncgi ::Request& request)
{
state_->addMessage(std::string{request.formField(\”msg\”)});
return http::Redirect{\”/\”};
}

private:
GuestBookState* state_;
};

class GuestBookRemoveMessage {
public:
explicit GuestBookRemoveMessage(GuestBookState& state)
: state_{&state}
{
}

http::Response operator()(int index, const asyncgi ::Request&)
{
state_->removeMessage(index);
return http::Redirect{\”/\”};
}

private:
GuestBookState* state_;
};

int main()
{
auto io = asyncgi ::IO{4};
auto state = GuestBookState{};
auto router = asyncgi ::Router{io};
router.route(\”/\”, http::RequestMethod::Get).process<GuestBookPage>(state);
router.route(\”/\”, http::RequestMethod::Post).process<GuestBookAddMessage>(state);
router.route( asyncgi ::rx{\”/delete/(.+)\”}, http::RequestMethod::Post).process<GuestBookRemoveMessage>(state);
router.route().set(http::ResponseStatus::_404_Not_Found, \”Page not found\”);

auto server = asyncgi ::Server{io, router};
server.listen(\”/tmp/fcgi.sock\”);
io.run();
}\”>

 // /examples/example_route_params.cpp
// /
# include < asyncgi / asyncgi .h >
# include < mutex >

using namespace asyncgi ;
using namespace std ::string_literals ;

class GuestBookState {
public:
    std::vector<std::string> messages ()
    {
        auto lock = std::scoped_lock{mutex_};
        return messages_;
    }

    void addMessage ( const std::string& msg)
    {
        auto lock = std::scoped_lock{mutex_};
        messages_. emplace_back (msg);
    }

    void removeMessage ( int index)
    {
        auto lock = std::scoped_lock{mutex_};
        if (index < 0 || index >= static_cast < int >(messages_. size ()))
            return ;
        messages_. erase ( std::next (messages_. begin (), index));
    }

private:
    std::vector<std::string> messages_;
    std::mutex mutex_;
};

std::string makeMessage ( int index, const std::string& msg)
{
    return msg + R\"( <form action=\"/delete/ )\" + std::to_string (index) +
            R\"( \" method=\"post\"> <input value=\"Delete\" type=\"submit\"> </form></div> )\" ;
}

class GuestBookPage {
public:
    explicit GuestBookPage (GuestBookState& state)
        : state_{&state}
    {
    }

    http::Response operator ()( const asyncgi ::Request&)
    {
        auto messages = state_-> messages ();
        auto page = \" <h1>Guest book</h1> \" s;
        if (messages. empty ())
            page += \" <p>No messages</p> \" ;
        else
            for ( auto i = 0 ; i < static_cast < int >(messages. size ()); ++i)
                page += \" <p> \" + makeMessage (i, messages. at (i)) + \" </p> \" ;

        page += \" <hr> \" ;
        page += \" <form method= \\\" post \\\" enctype= \\\" multipart/form-data \\\" > \"
                \" <label for= \\\" msg \\\" >Message:</label> \"
                \" <input id= \\\" msg \\\" name= \\\" msg \\\" value= \\\"\\\" > \"
                \" <input value= \\\" Submit \\\" data-popup= \\\" true \\\" type= \\\" submit \\\" > \"
                \" </form> \" ;
        return page;
    }

private:
    GuestBookState* state_;
};

class GuestBookAddMessage {
public:
    explicit GuestBookAddMessage (GuestBookState& state)
        : state_{&state}
    {
    }

    http::Response operator ()( const asyncgi ::Request& request)
    {
        state_-> addMessage (std::string{request. formField ( \" msg \" )});
        return http::Redirect{ \" / \" };
    }

private:
    GuestBookState* state_;
};

class GuestBookRemoveMessage {
public:
    explicit GuestBookRemoveMessage (GuestBookState& state)
        : state_{&state}
    {
    }

    http::Response operator ()( int index, const asyncgi ::Request&)
    {
        state_-> removeMessage (index);
        return http::Redirect{ \" / \" };
    }

private:
    GuestBookState* state_;
};

int main ()
{
    auto io = asyncgi ::IO{ 4 };
    auto state = GuestBookState{};
    auto router = asyncgi ::Router{io};
    router. route ( \" / \" , http::RequestMethod::Get). process <GuestBookPage>(state);
    router. route ( \" / \" , http::RequestMethod::Post). process <GuestBookAddMessage>(state);
    router. route ( asyncgi ::rx{ \" /delete/(.+) \" }, http::RequestMethod::Post). process <GuestBookRemoveMessage>(state);
    router. route (). set (http::ResponseStatus::_404_Not_Found, \" Page not found \" );

    auto server = asyncgi ::Server{io, router};
    server. listen ( \" /tmp/fcgi.sock \" );
    io. run ();
}

使用std::stringstream将正则表达捕获组转化为请求处理器参数。为了支持使用用户定义的参数类型的请求处理器,有必要提供asyncgi ::config::StringConverter类模板的专业化。已修改了上一个示例以重新格式化GuestBookRemoveMessage请求处理器,以将MessageNumber结构用作请求处理器参数:

例子

asyncgi .h>
#include <mutex>

using namespace asyncgi ;
using namespace std::string_literals;

struct MessageNumber {
int value;
};

template<>
struct asyncgi ::config::StringConverter<MessageNumber> {
static std::optional<MessageNumber> fromString(const std::string& data)
{
return MessageNumber{std::stoi(data)};
}
};

class GuestBookState {
public:
std::vector<std::string> messages()
{
auto lock = std::scoped_lock{mutex_};
return messages_;
}

void addMessage(const std::string& msg)
{
auto lock = std::scoped_lock{mutex_};
messages_.emplace_back(msg);
}

void removeMessage(int index)
{
auto lock = std::scoped_lock{mutex_};
if (index < 0 || index >= static_cast<int>(messages_.size()))
return;
messages_.erase(std::next(messages_.begin(), index));
}

private:
std::vector<std::string> messages_;
std::mutex mutex_;
};

std::string makeMessage(int index, const std::string& msg)
{
return msg + R\”(<form action=\”/delete/)\” + std::to_string(index) +
R\”(\” method=\”post\”> <input value=\”Delete\” type=\”submit\”> </form></div>)\”;
}

class GuestBookPage {
public:
explicit GuestBookPage(GuestBookState& state)
: state_{&state}
{
}

http::Response operator()(const asyncgi ::Request&)
{
auto messages = state_->messages();
auto page = \”<h1>Guest book</h1>\”s;
if (messages.empty())
page += \”<p>No messages</p>\”;
else
for (auto i = 0; i < static_cast<int>(messages.size()); ++i)
page += \”<p>\” + makeMessage(i, messages.at(i)) + \”</p>\”;

page += \”<hr>\”;
page += \”<form method=\\\”post\\\” enctype=\\\”multipart/form-data\\\”>\”
\”<label for=\\\”msg\\\”>Message:</label>\”
\”<input id=\\\”msg\\\” name=\\\”msg\\\” value=\\\”\\\”>\”
\”<input value=\\\”Submit\\\” data-popup=\\\”true\\\” type=\\\”submit\\\”>\”
\”</form>\”;
return page;
}

private:
GuestBookState* state_;
};

class GuestBookAddMessage {
public:
explicit GuestBookAddMessage(GuestBookState& state)
: state_{&state}
{
}

http::Response operator()(const asyncgi ::Request& request)
{
state_->addMessage(std::string{request.formField(\”msg\”)});
return http::Redirect{\”/\”};
}

private:
GuestBookState* state_;
};

class GuestBookRemoveMessage {
public:
explicit GuestBookRemoveMessage(GuestBookState& state)
: state_{&state}
{
}

http::Response operator()(MessageNumber msgNumber, const asyncgi ::Request&)
{
state_->removeMessage(msgNumber.value);
return http::Redirect{\”/\”};
}

private:
GuestBookState* state_;
};

int main()
{
auto io = asyncgi ::IO{4};
auto state = GuestBookState{};
auto router = asyncgi ::Router{io};
router.route(\”/\”, http::RequestMethod::Get).process<GuestBookPage>(state);
router.route(\”/\”, http::RequestMethod::Post).process<GuestBookAddMessage>(state);
router.route( asyncgi ::rx{\”/delete/(.+)\”}, http::RequestMethod::Post).process<GuestBookRemoveMessage>(state);
router.route().set(http::ResponseStatus::_404_Not_Found, \”Page not found\”);

auto server = asyncgi ::Server{io, router};
server.listen(\”/tmp/fcgi.sock\”);
io.run();
}\”>

 // /examples/example_route_params_user_defined_types.cpp
// /
# include < asyncgi / asyncgi .h >
# include < mutex >

using namespace asyncgi ;
using namespace std ::string_literals ;

struct MessageNumber {
    int value;
};

template <>
struct asyncgi ::config::StringConverter<MessageNumber> {
    static std::optional<MessageNumber> fromString ( const std::string& data)
    {
        return MessageNumber{ std::stoi (data)};
    }
};

class GuestBookState {
public:
    std::vector<std::string> messages ()
    {
        auto lock = std::scoped_lock{mutex_};
        return messages_;
    }

    void addMessage ( const std::string& msg)
    {
        auto lock = std::scoped_lock{mutex_};
        messages_. emplace_back (msg);
    }

    void removeMessage ( int index)
    {
        auto lock = std::scoped_lock{mutex_};
        if (index < 0 || index >= static_cast < int >(messages_. size ()))
            return ;
        messages_. erase ( std::next (messages_. begin (), index));
    }

private:
    std::vector<std::string> messages_;
    std::mutex mutex_;
};

std::string makeMessage ( int index, const std::string& msg)
{
    return msg + R\"( <form action=\"/delete/ )\" + std::to_string (index) +
            R\"( \" method=\"post\"> <input value=\"Delete\" type=\"submit\"> </form></div> )\" ;
}

class GuestBookPage {
public:
    explicit GuestBookPage (GuestBookState& state)
        : state_{&state}
    {
    }

    http::Response operator ()( const asyncgi ::Request&)
    {
        auto messages = state_-> messages ();
        auto page = \" <h1>Guest book</h1> \" s;
        if (messages. empty ())
            page += \" <p>No messages</p> \" ;
        else
            for ( auto i = 0 ; i < static_cast < int >(messages. size ()); ++i)
                page += \" <p> \" + makeMessage (i, messages. at (i)) + \" </p> \" ;

        page += \" <hr> \" ;
        page += \" <form method= \\\" post \\\" enctype= \\\" multipart/form-data \\\" > \"
                \" <label for= \\\" msg \\\" >Message:</label> \"
                \" <input id= \\\" msg \\\" name= \\\" msg \\\" value= \\\"\\\" > \"
                \" <input value= \\\" Submit \\\" data-popup= \\\" true \\\" type= \\\" submit \\\" > \"
                \" </form> \" ;
        return page;
    }

private:
    GuestBookState* state_;
};

class GuestBookAddMessage {
public:
    explicit GuestBookAddMessage (GuestBookState& state)
        : state_{&state}
    {
    }

    http::Response operator ()( const asyncgi ::Request& request)
    {
        state_-> addMessage (std::string{request. formField ( \" msg \" )});
        return http::Redirect{ \" / \" };
    }

private:
    GuestBookState* state_;
};

class GuestBookRemoveMessage {
public:
    explicit GuestBookRemoveMessage (GuestBookState& state)
        : state_{&state}
    {
    }

    http::Response operator ()(MessageNumber msgNumber, const asyncgi ::Request&)
    {
        state_-> removeMessage (msgNumber. value );
        return http::Redirect{ \" / \" };
    }

private:
    GuestBookState* state_;
};

int main ()
{
    auto io = asyncgi ::IO{ 4 };
    auto state = GuestBookState{};
    auto router = asyncgi ::Router{io};
    router. route ( \" / \" , http::RequestMethod::Get). process <GuestBookPage>(state);
    router. route ( \" / \" , http::RequestMethod::Post). process <GuestBookAddMessage>(state);
    router. route ( asyncgi ::rx{ \" /delete/(.+) \" }, http::RequestMethod::Post). process <GuestBookRemoveMessage>(state);
    router. route (). set (http::ResponseStatus::_404_Not_Found, \" Page not found \" );

    auto server = asyncgi ::Server{io, router};
    server. listen ( \" /tmp/fcgi.sock \" );
    io. run ();
}

路线上下文

使用asyncgi ::Router时,可以为上下文结构类型指定模板参数。然后,该结构将传递给ContextualRequestProcessor函数,可以在多个路由的整个请求处理中访问和修改。 ContextualRequestProcessor是一个RequestProcessor ,它采用一个参考上下文对象的其他参数。
只要所有先前的请求处理器都不发送任何响应,就可以匹配多个路由。为了避免发送响应,请求处理器可以使用std::optional<http::Response>签名并返回空值。这允许使用asyncgi ::Router注册类似中间件的处理器,该处理器主要修改后续处理器的路由上下文。

下一个示例演示了如何将路由上下文用于存储授权信息:

例子

asyncgi .h>
#include <mutex>
#include <optional>

namespace http = asyncgi ::http;
using namespace std::string_literals;

enum class AccessRole {
Admin,
Guest
};

struct RouteContext {
AccessRole role = AccessRole::Guest;
};

struct AdminAuthorizer {
std::optional<http::Response> operator()(const asyncgi ::Request& request, RouteContext& context)
{
if (request.cookie(\”admin_id\”) == \”ADMIN_SECRET\”)
context.role = AccessRole::Admin;

return std::nullopt;
}
};

struct LoginPage {
http::Response operator()(const asyncgi ::Request&, RouteContext& context)
{
if (context.role == AccessRole::Guest)
return {R\”(
<html>
<form method=\”post\” enctype=\”multipart/form-data\”>
<label for=\”msg\”>Login:</label>
<input id=\”login\” name=\”login\” value=\”\”>
<label for=\”msg\”>Password:</label>
<input id=\”passwd\” name=\”passwd\” value=\”\”>
<input value=\”Submit\” data-popup=\”true\” type=\”submit\”>
</form></html>)\”};
else //We are already logged in as the administrator
return http::Redirect{\”/\”};
}
};

struct LoginPageAuthorize {
http::Response operator()(const asyncgi ::Request& request, RouteContext& context)
{
if (context.role == AccessRole::Guest) {
if (request.formField(\”login\”) == \”admin\” && request.formField(\”passwd\”) == \”12345\”)
return {http::Redirect{\”/\”}, { asyncgi ::http::Cookie(\”admin_id\”, \”ADMIN_SECRET\”)}};
else
return http::Redirect{\”/login\”};
}
else //We are already logged in as the administrator
return http::Redirect{\”/\”};
}
};

int main()
{
auto io = asyncgi ::IO{4}; //4 threads processing requests
auto router = asyncgi ::Router<RouteContext>{io};
router.route( asyncgi ::rx{\”.*\”}).process<AdminAuthorizer>();
router.route(\”/\”).process(
[](const asyncgi ::Request&, asyncgi ::Responder& response, RouteContext& context)
{
if (context.role == AccessRole::Admin)
response.send(\”<p>Hello admin</p>\”);
else
response.send(R\”(<p>Hello guest</p><p><a href=\”/login\”>login</a>)\”);
});

router.route(\”/login\”, http::RequestMethod::Get).process<LoginPage>();
router.route(\”/login\”, http::RequestMethod::Post).process<LoginPageAuthorize>();
router.route().set(http::ResponseStatus::_404_Not_Found, \”Page not found\”);

auto server = asyncgi ::Server{io, router};
server.listen(\”/tmp/fcgi.sock\”);
io.run();
}\”>

 // /examples/example_route_context.cpp
// /
# include < asyncgi / asyncgi .h >
# include < mutex >
# include < optional >

namespace http = asyncgi ::http;
using namespace std ::string_literals ;

enum class AccessRole {
    Admin,
    Guest
};

struct RouteContext {
    AccessRole role = AccessRole::Guest;
};

struct AdminAuthorizer {
    std::optional<http::Response> operator ()( const asyncgi ::Request& request, RouteContext& context)
    {
        if (request. cookie ( \" admin_id \" ) == \" ADMIN_SECRET \" )
            context. role = AccessRole::Admin;

        return std::nullopt;
    }
};

struct LoginPage {
    http::Response operator ()( const asyncgi ::Request&, RouteContext& context)
    {
        if (context. role == AccessRole::Guest)
            return { R\"(
                    <html>
                    <form method=\"post\" enctype=\"multipart/form-data\">
                    <label for=\"msg\">Login:</label>
                    <input id=\"login\" name=\"login\" value=\"\">
                    <label for=\"msg\">Password:</label>
                    <input id=\"passwd\" name=\"passwd\" value=\"\">
                    <input value=\"Submit\" data-popup=\"true\" type=\"submit\">
                    </form></html> )\" };
        else // We are already logged in as the administrator
            return http::Redirect{ \" / \" };
    }
};

struct LoginPageAuthorize {
    http::Response operator ()( const asyncgi ::Request& request, RouteContext& context)
    {
        if (context. role == AccessRole::Guest) {
            if (request. formField ( \" login \" ) == \" admin \" && request. formField ( \" passwd \" ) == \" 12345 \" )
                return {http::Redirect{ \" / \" }, { asyncgi ::http::Cookie ( \" admin_id \" , \" ADMIN_SECRET \" )}};
            else
                return http::Redirect{ \" /login \" };
        }
        else // We are already logged in as the administrator
            return http::Redirect{ \" / \" };
    }
};

int main ()
{
    auto io = asyncgi ::IO{ 4 }; // 4 threads processing requests
    auto router = asyncgi ::Router<RouteContext>{io};
    router. route ( asyncgi ::rx{ \" .* \" }). process <AdminAuthorizer>();
    router. route ( \" / \" ). process (
            []( const asyncgi ::Request&, asyncgi ::Responder& response, RouteContext& context)
            {
                if (context. role == AccessRole::Admin)
                    response. send ( \" <p>Hello admin</p> \" );
                else
                    response. send ( R\"( <p>Hello guest</p><p><a href=\"/login\">login</a> )\" );
            });

    router. route ( \" /login \" , http::RequestMethod::Get). process <LoginPage>();
    router. route ( \" /login \" , http::RequestMethod::Post). process <LoginPageAuthorize>();
    router. route (). set (http::ResponseStatus::_404_Not_Found, \" Page not found \" );

    auto server = asyncgi ::Server{io, router};
    server. listen ( \" /tmp/fcgi.sock \" );
    io. run ();
}

路线匹配者

请求或上下文对象的任何参数都可以在asyncgi ::Router::route()方法中注册以进行路由匹配。为了实现这一目标,需要提供asyncgi ::config::RouteMatcher类模板的专业化,并在其中实现比较器Bool Operator()。让我们看看如何从上一个示例中注册枚举类Access作为路由匹配器:

例子

asyncgi .h>
#include <mutex>
#include <optional>

namespace http = asyncgi ::http;
using namespace std::string_literals;

enum class AccessRole {
Admin,
Guest
};

struct RouteContext {
AccessRole role = AccessRole::Guest;
};

struct AdminAuthorizer {
std::optional<http::Response> operator()(const asyncgi ::Request& request, RouteContext& context)
{
if (request.cookie(\”admin_id\”) == \”ADMIN_SECRET\”)
context.role = AccessRole::Admin;

return std::nullopt;
}
};

struct LoginPage {
http::Response operator()(const asyncgi ::Request&)
{
return {R\”(
<html>
<form method=\”post\” enctype=\”multipart/form-data\”>
<label for=\”msg\”>Login:</label>
<input id=\”login\” name=\”login\” value=\”\”>
<label for=\”msg\”>Password:</label>
<input id=\”passwd\” name=\”passwd\” value=\”\”>
<input value=\”Submit\” data-popup=\”true\” type=\”submit\”>
</form></html>)\”};
}
};

struct LoginPageAuthorize {
http::Response operator()(const asyncgi ::Request& request)
{
if (request.formField(\”login\”) == \”admin\” && request.formField(\”passwd\”) == \”12345\”)
return {http::Redirect{\”/\”}, { asyncgi ::http::Cookie(\”admin_id\”, \”ADMIN_SECRET\”)}};

return http::Redirect{\”/login\”};
}
};

template<>
struct asyncgi ::config::RouteMatcher<AccessRole, RouteContext> {
bool operator()(AccessRole value, const asyncgi ::Request&, const RouteContext& context) const
{
return value == context.role;
}
};

int main()
{
auto io = asyncgi ::IO{4};
auto router = asyncgi ::Router<RouteContext>{io};
router.route( asyncgi ::rx{\”.*\”}).process<AdminAuthorizer>();
router.route(\”/\”).process(
[](const asyncgi ::Request&, RouteContext& context) -> http::Response
{
if (context.role == AccessRole::Admin)
return {\”<p>Hello admin</p>\”};
else
return {R\”(<p>Hello guest</p><p><a href=\”/login\”>login</a>)\”};
});

router.route(\”/login\”, http::RequestMethod::Get, AccessRole::Guest).process<LoginPage>();
router.route(\”/login\”, http::RequestMethod::Post, AccessRole::Guest).process<LoginPageAuthorize>();
router.route(\”/login\”, http::RequestMethod::Get, AccessRole::Admin).set(\”/\”, http::RedirectType::Found);
router.route(\”/login\”, http::RequestMethod::Post, AccessRole::Admin).set(\”/\”, http::RedirectType::Found);
router.route().set(http::ResponseStatus::_404_Not_Found, \”Page not found\”);

auto server = asyncgi ::Server{io, router};
server.listen(\”/tmp/fcgi.sock\”);
io.run();
}\”>

 // /examples/example_route_matcher.cpp
// /
# include < asyncgi / asyncgi .h >
# include < mutex >
# include < optional >

namespace http = asyncgi ::http;
using namespace std ::string_literals ;

enum class AccessRole {
    Admin,
    Guest
};

struct RouteContext {
    AccessRole role = AccessRole::Guest;
};

struct AdminAuthorizer {
    std::optional<http::Response> operator ()( const asyncgi ::Request& request, RouteContext& context)
    {
        if (request. cookie ( \" admin_id \" ) == \" ADMIN_SECRET \" )
            context. role = AccessRole::Admin;

        return std::nullopt;
    }
};

struct LoginPage {
    http::Response operator ()( const asyncgi ::Request&)
    {
        return { R\"(
                <html>
                <form method=\"post\" enctype=\"multipart/form-data\">
                <label for=\"msg\">Login:</label>
                <input id=\"login\" name=\"login\" value=\"\">
                <label for=\"msg\">Password:</label>
                <input id=\"passwd\" name=\"passwd\" value=\"\">
                <input value=\"Submit\" data-popup=\"true\" type=\"submit\">
                </form></html> )\" };
    }
};

struct LoginPageAuthorize {
    http::Response operator ()( const asyncgi ::Request& request)
    {
        if (request. formField ( \" login \"

下载源码

通过命令行克隆项目:

git clone https://github.com/kamchatka-volcano/asyncgi.git

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 开发教程 asyncgi https://www.zuozi.net/31933.html

Demos
下一篇: Demos
常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务