drogonframework / drogon

Drogon: A C++14/17/20 based HTTP web application framework running on Linux/macOS/Unix/Windows
MIT License
11.45k stars 1.1k forks source link

How to render view after creating restful API controllers and models created using Drogon's ORM? #944

Closed quantum-whiz closed 4 months ago

quantum-whiz commented 3 years ago

I created a database in mySQL and using drogon's ORM, drogon_ctl auto-generated the models and restful API Base controllers. When I went through the documentation, I'm unable to find any reference for how to read the data from the database using the controllers inherited from auto-generated base Restfulcontrollers and render a view and vice versa. How should it be done?

As an example, let's say I did the following things: Step 1: Created a table called login in database using the following

CREATE TABLE `login` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `login_name` varchar(25) NOT NULL,
  `password` varchar(15) NOT NULL,
  `first_name` varchar(25) NOT NULL,
  `last_name` varchar(25) DEFAULT NULL,
  `birth_date` date DEFAULT NULL,
  `email` varchar(25) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `User_name` (`login_name`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

And: using the drogon_ctl create model command with "enabled": true and "generate_base_only": true in restful API controllers in model.json. This created the models and restful base controllers automatically.

Step 2: Then, I inherited a controller from the base controller and as I was satisfied with the functions in the Base restful Controllers, I just made the inherited controller version of all the methods and called the corresponding base controller method inside it.

Step 3: Registered the handler under /login.

Can you help me complete the remaining steps in this example such that I can take data from the Database and render a view and vice versa?

PS: I am completely new to web development and I have just started learning using this framework.

an-tao commented 3 years ago

Please set the generate_base_only option to false and change the derived controller as you want. if you don't change it, it provide default restful API on table Login.

the generate_base_only is used to re-generate base controllers when you change tables after customizing derived controllers.

quantum-whiz commented 3 years ago

Please set the generate_base_only option to false and change the derived controller as you want. if you don't change it, it provide default restful API on table Login.

the generate_base_only is used to re-generate base controllers when you change tables after customizing derived controllers.

This created the derived controller also. Now, how can I use this controller to access the data in front end? What should be the body of registerHandler and ListUsers.csp?

drogon::HttpAppFramework::instance().registerHandler(
        "/login",
        [=](const HttpRequestPtr &req,
            std::function<void(const HttpResponsePtr &)> &&callback) 
{
            auto user= new LoginController();
            user->get(req, std::move(callback));
            LOG_INFO << "Read ALL";
            try
            {
                HttpViewData data;
                LOG_INFO << "view data";
                data.insert("title", "List of all users");
                data.insert("users", user);
                LOG_INFO << "data inserted";
                auto resp =
                    HttpResponse::newHttpViewResponse("ListUsers.csp", 
                                                      data);
                LOG_INFO << "response";
            }
            catch (const std::exception &e)
            {
                LOG_INFO << "view ex";
                LOG_ERROR << e.what();

                return;
            } 
        },
        {Get});
}

ListUsers.csp

<!DOCTYPE html>
<html>
<%c++
    auto user=ListUsers_view_data.get<std::unordered_map<std::string,std::string>>("users");
%>
<head>
    <meta charset="UTF-8">
    <title>[[ title ]]</title>
</head>
<body>
   <%c++ if(user.size()>0){%>
    <H1>Users</H1>
    <table border="1">
      <tr>
        <th>id</th>
        <th>username</th>
      </tr>
      <%c++ for(auto iter:user){%>
      <tr>
        <td>{%iter.first%}</td>
        <td><%c++ $$<<iter.second;%></td>
      </tr>
      <%c++}%>
    </table>
    <%c++ }else{%>
    <H1>no users</H1>
    <%c++}%>
</body>
</html>

And what changes should I make in the .csp files in view. I tried to follow the documentation but when I did so, I am not able to properly the get the view I wanted (I intended to print the id and usernames of all the users in the login table). I'm only able to able to see the JSON Objects coming to front-end and I'm getting this error ERROR Bad type - HttpViewData.h:50 .

an-tao commented 3 years ago

The Restful APIs are not reponsebility on rendering web page on back-end, only data (with json format) is returned from the Restful APIs. And you don't need to register handlers manully, it is registered automatically by drogon. please refer to the controler's header file to get the routing seting (see the ADD_METHOD_TO macros).

quantum-whiz commented 3 years ago

The Restful APIs are not reponsebility on rendering web page on back-end, only data (with json format) is returned from the Restful APIs. And you don't need to register handlers manully, it is registered automatically by drogon. please refer to the controler's header file to get the routing seting (see the ADD_METHOD_TO macros).

So, I can't do server side rendering of the web pages using Drogon if I use this Restful controller feature? Should I use someother UI Framework and call these controllers so that I would be able to render a view of the web pages or is there a way I could render a view using features available in Drogon? Because I'm not able to find a way to convert json type object into unordered map as required in the ListUsers.csp file. Is there a way to do it?

rbugajewski commented 3 years ago

Just create a regular controller if you want to render classic web pages. Only use the RESTful controller if you need to output JSON as an API contract to other clients.

an-tao commented 3 years ago

The Restful APIs are not reponsebility on rendering web page on back-end, only data (with json format) is returned from the Restful APIs. And you don't need to register handlers manully, it is registered automatically by drogon. please refer to the controler's header file to get the routing seting (see the ADD_METHOD_TO macros).

So, I can't do server side rendering of the web pages using Drogon if I use this Restful controller feature? Should I use someother UI Framework and call these controllers so that I would be able to render a view of the web pages or is there a way I could render a view using features available in Drogon? Because I'm not able to find a way to convert json type object into unordered map as required in the ListUsers.csp file. Is there a way to do it?

Actually, u could insert the Json object to the view data, such as data["json"]=jsonobject;, and use it in the csp view, that's ok.

rbugajewski commented 3 years ago

Actually, u could insert the Json object to the view data, such as data["json"]=jsonobject;, and use it in the csp view, that's ok.

Wouldn’t this do a whole JSON-conversion round-trip and (unnecessarily) degrade performance?

quantum-whiz commented 3 years ago

The Restful APIs are not reponsebility on rendering web page on back-end, only data (with json format) is returned from the Restful APIs. And you don't need to register handlers manully, it is registered automatically by drogon. please refer to the controler's header file to get the routing seting (see the ADD_METHOD_TO macros).

So, I can't do server side rendering of the web pages using Drogon if I use this Restful controller feature? Should I use someother UI Framework and call these controllers so that I would be able to render a view of the web pages or is there a way I could render a view using features available in Drogon? Because I'm not able to find a way to convert json type object into unordered map as required in the ListUsers.csp file. Is there a way to do it?

Actually, u could insert the Json object to the view data, such as data["json"]=jsonobject;, and use it in the csp view, that's ok.

Actually, I don't have the json object directly. Is there a way to retrieve the json object from callback function?

an-tao commented 3 years ago

Actually, u could insert the Json object to the view data, such as data["json"]=jsonobject;, and use it in the csp view, that's ok.

Wouldn’t this do a whole JSON-conversion round-trip and (unnecessarily) degrade performance?

You are right, I mean if he do use RestAPI controllers to render web pages.

an-tao commented 3 years ago

The Restful APIs are not reponsebility on rendering web page on back-end, only data (with json format) is returned from the Restful APIs. And you don't need to register handlers manully, it is registered automatically by drogon. please refer to the controler's header file to get the routing seting (see the ADD_METHOD_TO macros).

So, I can't do server side rendering of the web pages using Drogon if I use this Restful controller feature? Should I use someother UI Framework and call these controllers so that I would be able to render a view of the web pages or is there a way I could render a view using features available in Drogon? Because I'm not able to find a way to convert json type object into unordered map as required in the ListUsers.csp file. Is there a way to do it?

Actually, u could insert the Json object to the view data, such as data["json"]=jsonobject;, and use it in the csp view, that's ok.

Actually, I don't have the json object directly. Is there a way to retrieve the json object from callback function?

For example:

void RestfulLbUserCtrl::get(
    const HttpRequestPtr &req,
    std::function<void(const HttpResponsePtr &)> &&callback)
{
    RestfulLbUserCtrlBase::get(req,
                               [callback = std::move(callback)](
                                   const HttpResponsePtr &resp) {
                                   auto jsonObj = resp->getJsonObject();
                                   LOG_DEBUG << jsonObj->toStyledString();
                                   HttpViewData data;
                                   data["json"]=jsonObj;
                                   callback(drogon::newHttpViewResponse("user_page.csp",data));
                               });
}

And just as @rbugajewski said, this conversion to/from json object occurs internally and is not needed, it degrade performance. you'd better use a regular HttpController.