cutelyst教程_03 _更多Cutelyst基础

Tutorial_03_MoreCutelystBasics

教程_03 _更多Cutelyst基础

Adriaan de Groot edited this page on Jan 1, 2021 · 17 revisions

​Adriaan de Groot在2021年1月1日编辑了这一页,修改了17页。

OVERVIEW

总览

  1. Introduction
  2. Cutelyst Basics
  3. More Cutelyst Basics
  4. Basic CRUD
  5. Authentication

DESCRIPTION

说明

This chapter of the tutorial builds on the work done in Chapter 2 to explore some features that are more typical of "real world" web applications. From this chapter of the tutorial onward, we will be building a simple book database application. Although the application will be too limited to be of use to anyone, it should provide a basic environment where we can explore a variety of features used in virtually all web applications.

本教程的这一章以第2章中所做的工作为基础,探索一些更典型的“真实世界”web应用程序的功能。从本教程的这一章开始,我们将构建一个简单的图书数据库应用程序。虽然该应用程序将受到太多限制,无法供任何人使用,但它应该提供一个基本环境,让我们可以探索几乎所有web应用程序中使用的各种功能。

CREATE A NEW APPLICATION

创建一个新的应用

The remainder of the tutorial will build an application called MyApp. First use the Cutelyst cutelyst command to initialize the framework for the MyApp application (make sure you aren't still inside the directory of the Hello application from the previous chapter of the tutorial or in a directory that already has a "MyApp" subdirectory):

本教程的其余部分将构建一个名为MyApp的应用。首先使用Cutelyst-Cutelyst命令初始化MyApp应用程序的框架(确保您不在本教程上一章的Hello应用程序目录中,也不在已经有“MyApp”子目录的目录中):

$ cutelyst2 --create-app MyApp
 created "MyApp"
 created "MyApp/CMakeLists.txt"
 created "MyApp/build"
 created "MyApp/root"
 created "MyApp/src"
 ...
Change to application directory, then build directory and Run "cmake .." to make sure your install is complete

And change the "MyApp" directory the helper created:

并更改助手创建的“MyApp”目录:

$ cd MyApp

This creates a similar skeletal structure to what we saw in Chapter 2 of the tutorial, except with MyApp and myapp substituted for Hello and hello.

这创建了一个类似于我们在本教程第2章中看到的骨架结构,除了用MyApp和myapp代替Hello和hello之外。

Cutelyst used to create an application with Cutelyst::StaticSimple plugin enabled by default, StaticSimple provides an easy way to serve static content, such as images and CSS files, from within your application.

Cutelyst用于创建默认启用Cutelyst::StaticSimple插件的应用程序,StaticSimple提供了一种从应用程序中提供静态内容(如图像和CSS文件)的简单方法。

However it's much better to use cutelyst (or uwsgi) --static-map or serve them with your front web server. Which is why this plugin doesn't come enabled by default anymore.

不过,最好使用cutelyst(或uwsgi)——静态映射或通过前端web服务器提供它们。这就是为什么这个插件不再默认启用的原因。

When adding new plugins make sure you pass the Cutelyst::Application as their parent so they get automatically registered, as well as adding it's new dependency within the CMakeLists.txt file.

添加新插件时,请确保将Cutelyst::Application作为其父级传递,以便它们自动注册,并在CMakeLists.txt文件中添加新的依赖项。

CREATE A CUTELYST CONTROLLER

创建CUTELYST控制器

As discussed earlier, controllers are where you write methods that interact with user input. Typically, controller methods respond to GET and POST requests from the user's web browser.

如前所述,控制器是编写与用户输入交互的方法的地方。通常,控制器方法会响应来自用户web浏览器的GET和POST请求。

Use the Cutelyst cutelyst command to add a controller for book-related actions:

使用Cutelyst Cutelyst命令为与书本相关的操作添加控制器:

$ cutelyst2 --controller Books
 created "/home/daniel/code/MyApp/src/books.h"
 created "/home/daniel/code/MyApp/src/books.cpp"
Now, on your application class include and instantiate the controller.

To register and instantiate the controller, add to your src/myapp.cpp:

要注册并实例化控制器,请添加到src/myapp.cpp:

...
#include "books.h"
...
bool MyApp::init()
{
    ....
    new Books(this);
    ....
}

Then edit src/books.h and add the method "list" to the controller:

然后编辑src/books.h并将方法“列表”添加到控制器:

class Books : public Controller
{
...
/**
 * Fetch all book objects and pass to books/list.html in stash to be displayed
 */
C_ATTR(list, :Local)
void list(Context *c);
};

And in src/books.cpp the implementation:

在src/books.cpp中实现:

void Books::list(Context *c)
{
    // c is the Cutelyst::Context that's used to 'glue together'
    // the various components that make up the application

    // Retrieve all of the book records as book model objects and store in the
    // stash where they can be accessed by the Grantlee template
    // c->setStash("books", sql result);
    // But, for now, use this code until we create the model later
    c->setStash("books", "");

    // Set the Grantlee template to use. You will almost always want to do this
    // in your action methods (action methods respond to user input in
    // your controllers).
    c->setStash("template", "books/list.html");
}

Here we see "Cutelyst Context object", which is automatically passed as the first argument to all Cutelyst action methods. It is used to pass information between components and provide access to Cutelyst and plugin functionality.

这里我们看到“Cutelyst上下文对象”,它会自动作为第一个参数传递给所有Cutelyst动作方法。它用于在组件之间传递信息,并提供对Cutelyst和插件功能的访问。

Cutelyst Controller actions are regular C++ class methods, but they make use of attributes (the ":Local" inside the C_ATTR macro which also makes the method invokable) to provide additional information to the Cutelyst dispatcher logic (note that there can be an optional space between the colon and the attribute name; you will see attributes written both ways). Most Cutelyst Controllers use one of five action types:

Cutelyst控制器动作是常规C++类方法,但是它们使用属性(“C_ATTR宏”中的“:Local”也使方法可调用)向Cutelyst调用器逻辑提供附加信息(注意,在冒号和属性名之间可以有一个可选的空间;您将看到双向写入的属性)。大多数Cutelyst控制器使用以下五种动作类型之一:

:Private -- Use :Private (or place your method under private section) for methods that you want to make into an action, but you do not want Cutelyst to directly expose the method to your users. Cutelyst will not map :Private methods to a URI. Use them for various sorts of "special" methods (the Begin, Auto, etc. discussed below) or for methods you want to be able to forward or detach to. (If the method is a "plain old method" that you don't want to be an action at all, then just define the method without any attribute -- you can call it in your code, but the Cutelyst dispatcher will ignore it. There are five types of "special" built-in :Private actions: Begin, End, Default, Index, and Auto.

:Private——对于要使其成为动作的方法,使用:Private(或将方法放在Private部分下),但不希望Cutelyst直接向用户公开该方法。Cutelyst不会将:Private方法映射到URI。将它们用于各种“特殊”方法(下面讨论的Begin、Auto等)或您希望能够转发或分离到的方法。(如果这个方法是一个“普通的旧方法”,你根本不想成为一个动作,那么只需定义一个没有任何属性的方法——你可以在代码中调用它,但是Cutelyst调用器会忽略它。有五种类型的“特殊”内置动作:Begin, End, Default, Index, and Auto。

With Begin, End, Default, Index private actions, only the most specific action of each type will be called. For example, if you define a Begin action in your controller it will override a Begin action in your application/root controller -- only the action in your controller will be called. Unlike the other actions where only a single method is called for each request, every Auto action along the chain of namespaces will be called. Each Auto action will be called from the application/root controller down through the most specific class.

对于Begin、End、Default和Index私有操作,只会调用每种类型中最具体的操作。例如,如果在控制器中定义Begin操作,它将覆盖应用程序/根控制器中的开始操作——只调用控制器中的操作。与其他每个请求只调用一个方法的操作不同,命名空间上的每个Auto操作都将被调用。每个Auto操作都将从应用程序/根控制器向下通过最特定的类调用。

:Path -- :Path actions let you map a method to an explicit URI path. For example, ":Path('list')" in src/books.h would match on the URL http://localhost:3000/books/list, but ":Path('/list')" would match on http://localhost:3000/list (because of the leading slash). You can use :Args() to specify how many arguments an action should accept.

:Path--:Path操作允许将方法映射到显式URI路径。例如,src/books.h中的“:Path('list')”,将在URL上匹配http://localhost:3000/books/list,但“:Path('/list')”将匹配http://localhost:3000/list(因为前面的斜杠)。可以使用:Args() 指定一个操作应该接受多少个参数。

:Local -- :Local is merely a shorthand for ":Path('name_of_method')". For example, these are equivalent: "C_ATTR(create_book, :Local) ... (Context*) {...}" and "C_ATTR(create_book, :Path("create_book")) ... {...}".

:Local--:Local只是“:Path('name_of_method')”的简写。例如,它们是等价的:“C_ATTR(create_book, :Local) ... (Context*) {...}”和“C_ATTR(create_book, :Path("create_book")) ... {...}”。

:Global -- :Global is merely a shorthand for ":Path('/name_of_method')". For example, these are equivalent: "C_ATTR(create_book, :Global) ... {...}" and "C_ATTR(create_book, :Path("/create_book")) ... {...}".

:Global--:Global只是“:Path('/name_of_method')”的缩写。例如,它们是等价的:“C_ATTR(create_book, :Global) ... {...}”和“C_ATTR(create_book, :Path("/create_book")) ... {...}”。

:Chained -- Newer Cutelyst applications tend to use the Chained dispatch form of action types because of its power and flexibility. It allows a series of controller methods to be automatically dispatched when servicing a single user request. See Cutelyst::Manual::Tutorial::04_BasicCRUD and Cutelyst::DispatchType::Chained for more information on chained actions.

:Chained——较新的Cutelyst应用程序倾向于使用动作类型的链式分派形式,因为它具有强大的功能和灵活性。它允许在为单个用户请求提供服务时自动调度一系列控制器方法。有关链接操作的更多信息,请参见Cutelyst::Manual::Tutorial::04_BasicCRUD and Cutelyst::DispatchType::Chained。

CUTELYST VIEWS

CUTELYST视图

As mentioned in Chapter 2 of the tutorial, views are where you render output, typically for display in the user's web browser (but can generate other types of output such as PDF or JSON). The code in src/myapp.cpp selects the type of view to use, with the actual rendering template found in the root directory. As with virtually every aspect of Cutelyst, options abound when it comes to the specific view technology you adopt inside your application. However, most Cutelyst applications use the Grantlee (for more information on TT, see http://www.grantlee.org). At the moment other somewhat popular view technology is ClearSilver (http://www.clearsilver.net/).

​如本教程第2章所述,视图是渲染输出的地方,通常用于在用户的web浏览器中显示(但可以生成其他类型的输出,如PDF或JSON)。src/myapp.cpp中的代码,使用根目录中的实际渲染模板选择要使用的视图类型。与Cutelyst的几乎每个方面一样,在应用程序中采用的特定视图技术也有很多选择。然而,大多数Cutelyst应用程序都使用Grantle(有关TT的更多信息,请参阅http://www.grantlee.org)目前,另一种颇受欢迎的视图技术是ClearSilver(http://www.clearsilver.net/).

Register a Cutelyst View

注册Cutelyst视图

It is now up to you to decide how you want to structure your view layout. For the tutorial, we will start with a very simple Grantlee template to initially demonstrate the concepts, but quickly migrate to a more typical "wrapper page" type of configuration (where the "wrapper" controls the overall "look and feel" of your site from a single file or set of files). Edit src/myapp.cpp:

现在由您决定如何构造视图布局。在本教程中,我们将从一个非常简单的Grantle模板开始,首先演示这些概念,然后快速迁移到更典型的“包装页”配置类型(其中“包装页”通过单个文件或一组文件控制站点的整体“外观”)。编辑src/myapp.cpp:

....
#include <Cutelyst/Plugins/View/Grantlee/grantleeview.h>
....
bool MyApp::init()
{
    ....
    auto view = new GrantleeView(this);
    view->setIncludePaths({ pathTo("root/src") });
    ....
}

And link to Grantlee, adding to src/CMakeLists.txt:

并链接到Grantlee,添加到src/CMakeLists.txt:

target_link_libraries(MyApp
    ...
    Cutelyst::View::Grantlee # Add this line
    ...
}

This changes the base directory for your template files from root to root/src.

这会将模板文件的基本目录从root更改为root/src。

Please stick with the settings above for the duration of the tutorial, but feel free to use whatever options you desire in your applications.

在本教程期间,请坚持以上设置,但可以在应用程序中随意使用您想要的任何选项。

Note: We will use root/src as the base directory for our template files, with a full naming convention of root/src/controller_name/action_name.html. Another popular option is to use root/ as the base (with a full filename pattern of root/controller_name/action_name.html).

注意:我们将使用root/src作为模板文件的基本目录,其完整命名约定为root/src/controller_name/action_name.html。另一个流行的选项是使用root/作为基础(完整的文件名模式为root/controller_name/action_name.html)。

Create a Grantlee Template Page

创建Grantlee模板页面

First create a directory for book-related templates:

首先为图书相关模板创建一个目录:

$ mkdir -p root/src/books

Then create root/src/books/list.html in your editor and enter:

然后创建root/src/books/list.html ,然后输入:

{% comment %}This is a Grantlee comment.{% endcomment %}

{% comment %}Some basic HTML with a loop to display books{% endcomment %}
<table>
<tr><th>Title</th><th>Rating</th><th>Author(s)</th></tr>
{% comment %}Display each book in a table row{% endcomment %}
{% for book in books %}
  <tr>
    <td>{{ book.title }}</td>
    <td>{{ book.rating }}</td>
    <td></td>
  </tr>
{% endfor %}
</table>

As indicated by the inline comments above, the for loop iterates through each book model object and prints the title and rating fields.

如上面的内联注释所示,for循环遍历每个图书模型对象,并打印标题和评级字段。

The {% and %} tags are used to delimit Grantlee code. Grantlee supports a wide variety of directives for "calling" other files, looping, conditional logic, etc. In general, Grantlee simplifies the usual range of Qt introspection properties down to the single dot (".") operator. This applies to properties, hash lookups, and list index values.

{%和%}标记用于分隔Grantle代码。Grantle支持多种指令,用于“调用”其他文件、循环、条件逻辑等。一般来说,Grantle将Qt内省属性的通常范围简化为单点(“.”)操作符。这适用于属性、哈希查找和列表索引值。

TIP: While you can build all sorts of complex logic into your Grantlee templates, you should in general keep the "code" part of your templates as simple as possible.

提示:虽然你可以在Grantlee模板中构建各种复杂的逻辑,但一般来说,你应该让模板中的“代码”部分尽可能简单。

Test Run The Application

测试运行应用程序

To test your work so far, compile then start the development server:

要测试到目前为止的工作,请编译并启动开发服务器:

$ make && cutelyst2 -r --server --app-file src/libMyApp -- --chdir ..

Then point your browser to http://localhost:3000 and you should still get the Cutelyst welcome page. Next, change the URL in your browser to http://localhost:3000/books/list. If you have everything working so far, you should see a web page that displays nothing other than our column headers for "Title", "Rating", and "Author(s)" -- we will not see any books until we get the database and model working below.

​然后将浏览器指向http://localhost:3000你还是应该进入Cutelyst欢迎页面。接下来,将浏览器中的URL更改为http://localhost:3000/books/list.如果到目前为止一切正常,你应该会看到一个网页,除了“标题”、“评级”和“作者”的列标题之外,什么也不显示——在下面的数据库和模型正常工作之前,我们不会看到任何书籍。

If you run into problems getting your application to run correctly, it might be helpful to refer to some of the debugging techniques covered in the Debugging chapter of the tutorial.

如果在正确运行应用程序时遇到问题,参考本教程调试一章中介绍的一些调试技术可能会有所帮助。

CREATE A SQLITE DATABASE

创建SQLITE数据库

In this step, we make a text file with the required SQL commands to create a database table and load some sample data. We will use SQLite (http://www.sqlite.org), a popular database that is lightweight and easy to use. Be sure to get at least version 3. Open myapp01.sql in your editor and enter:

​在这一步中,我们使用所需的SQL命令创建一个文本文件,以创建一个数据库表并加载一些示例数据。我们将使用SQLite(http://www.sqlite.org),一个轻量级且易于使用的流行数据库。确保至少获得版本3。在编辑器中打开myapp01.sql,然后输入:

--
-- Create a very simple database to hold book and author information
--
PRAGMA foreign_keys = ON;
CREATE TABLE book (
        id          INTEGER PRIMARY KEY,
        title       TEXT ,
        rating      INTEGER
);
-- 'book_author' is a many-to-many join table between books & authors
CREATE TABLE book_author (
        book_id     INTEGER REFERENCES book(id) ON DELETE CASCADE ON UPDATE CASCADE,
        author_id   INTEGER REFERENCES author(id) ON DELETE CASCADE ON UPDATE CASCADE,
        PRIMARY KEY (book_id, author_id)
);
CREATE TABLE author (
        id          INTEGER PRIMARY KEY,
        first_name  TEXT,
        last_name   TEXT
);
---
--- Load some sample data
---
INSERT INTO book VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
INSERT INTO book VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
INSERT INTO book VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
INSERT INTO book VALUES (4, 'Perl Cookbook', 5);
INSERT INTO book VALUES (5, 'Designing with Web Standards', 5);
INSERT INTO author VALUES (1, 'Greg', 'Bastien');
INSERT INTO author VALUES (2, 'Sara', 'Nasseh');
INSERT INTO author VALUES (3, 'Christian', 'Degu');
INSERT INTO author VALUES (4, 'Richard', 'Stevens');
INSERT INTO author VALUES (5, 'Douglas', 'Comer');
INSERT INTO author VALUES (6, 'Tom', 'Christiansen');
INSERT INTO author VALUES (7, 'Nathan', 'Torkington');
INSERT INTO author VALUES (8, 'Jeffrey', 'Zeldman');
INSERT INTO book_author VALUES (1, 1);
INSERT INTO book_author VALUES (1, 2);
INSERT INTO book_author VALUES (1, 3);
INSERT INTO book_author VALUES (2, 4);
INSERT INTO book_author VALUES (3, 5);
INSERT INTO book_author VALUES (4, 6);
INSERT INTO book_author VALUES (4, 7);
INSERT INTO book_author VALUES (5, 8);

Then use the following command to build a myapp.db SQLite database:

然后使用以下命令构建myapp.db SQLite数据库:

$ sqlite3 myapp.db < myapp01.sql

If you need to create the database more than once, you probably want to issue the rm myapp.db command to delete the database before you use the sqlite3 myapp.db < myapp01.sql command.

如果需要多次创建数据库,可能需要在使用命令sqlite3 myapp.db < myapp01.sql前,使用rm myapp.db命令删除数据库。

Once the myapp.db database file has been created and initialized, you can use the SQLite command line environment to do a quick dump of the database contents:

一旦打开myapp.db数据库文件已创建并初始化,您可以使用SQLite命令行环境快速转储数据库内容:

$ sqlite3 myapp.db
SQLite version 3.7.3
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from book;
1|CCSP SNRS Exam Certification Guide|5
2|TCP/IP Illustrated, Volume 1|5
3|Internetworking with TCP/IP Vol.1|4
4|Perl Cookbook|5
5|Designing with Web Standards|5
sqlite> .q
$

Or:

$ sqlite3 myapp.db "select * from book"
1|CCSP SNRS Exam Certification Guide|5
2|TCP/IP Illustrated, Volume 1|5
3|Internetworking with TCP/IP Vol.1|4
4|Perl Cookbook|5
5|Designing with Web Standards|5

As with most other SQL tools, if you are using the full "interactive" environment you need to terminate your SQL commands with a ";" (it's not required if you do a single SQL statement on the command line). Use ".q" to exit from SQLite from the SQLite interactive mode and return to your OS command prompt.

与大多数其他SQL工具一样,如果使用完整的“交互式”环境,则需要使用“;”终止SQL命令(如果在命令行上执行一条SQL语句,则不需要该命令)。使用“.q”从SQLite交互模式退出SQLite,并返回操作系统命令提示符。

For using other databases, such as PostgreSQL or MySQL, see Appendix 2.

有关使用其他数据库(如PostgreSQL或MySQL)的信息,请参见附录2。

DATABASE ACCESS WITH QtSql

用QtSql访问数据库

Cutelyst can be used in conjunction with ORMs such as QxORM (http://www.qxorm.com/) or ODB (ODB - C++ Object-Relational Mapping (ORM)), but for the time being we are going to use QtSql and write our own SQL statements. In future we might write appendixes for them, so for now make sure you have QtSql development packages as well as Qt Sqlite3 driver installed. Cutelyst::Sql::Utils will also be used to simplify Sql code.

​Cutelyst可以与QxORM等ORM结合使用(http://www.qxorm.com/)或ODB(ODB-C++对象关系映射(ORM)),但目前我们将使用QtSQL并编写自己的SQL语句。将来我们可能会为它们编写附录,所以现在请确保您已经安装了QtSql开发包和qtsqlite3驱动程序。Cutelyst::Sql::Utils还将用于简化Sql代码。

Before you continue, make sure your myapp.db database file is in the application's topmost directory.

在继续之前,请确保您的myapp.db数据库文件位于应用程序的最顶层目录中。

Now we are going to add the required code open the database connection:

现在我们将添加打开数据库连接所需的代码:

First change CMakeLists.txt to find QtSql:

首先,修改CMakeLists.txt,以查找QtSql:

- find_package(Qt5 COMPONENTS Core Network)
+ find_package(Qt5 COMPONENTS Core Network Sql)

Then src/CMakeLists.txt to include and link to QtSql:

然后是src/CMakeLists.txt,要包含并链接到QtSql:

target_link_libraries(MyApp
    ...
    Qt5::Sql             # Add this line
    Cutelyst::Utils::Sql # Add this line
    ...
}

With QtSql we can open a connection to the database once and reuse it for application's lifetime, but it's important to notice that you must not open it before forking a new process, otherwise all child process will share the same connection and the behavior is undefined.

使用QtSql,我们可以一次打开到数据库的连接,并在应用程序的生命周期内重复使用它,但需要注意的是,在派生新进程之前,不能打开它,否则所有子进程将共享相同的连接,并且行为未定义。

Add the virtual postFork() method to your main application class, there you can return false if your database fails to open:

将virtual postFork()方法添加到主应用程序类中,如果数据库无法打开,则可以在主应用程序类中返回false:

// myapp.h
public:
   ...
   virtual bool postFork() override;
// myapp.cpp
...
#include <QtSql>
#include <Cutelyst/Plugins/Utils/Sql>
...
bool MyApp::postFork()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", Sql::databaseNameThread("MyDB"));
    db.setDatabaseName("myapp.db");
    db.setConnectOptions("foreign_keys = ON");
    if (!db.open()) {
        qCritical() << "Failed to open database:" << db.lastError().text();
        return false;
    }
    return true;
}

ENABLE THE MODEL IN THE CONTROLLER

在控制器中启用模型

For the time being QtSql or more specifically QSqlQuery doesn't provide any form of introspection, which makes it unusable for QML and Grantlee which require it, because of that we need to read all result and put it into introspectable objects (such as QVariantHash), for that we will use Cutelyst::Utils::Sql

目前,QtSql或更具体地说,QSqlQuery不提供任何形式的内省,这使得它无法用于需要它的QML和grantle,因为我们需要读取所有结果并将其放入可内省的对象(例如QVariantHash),为此,我们将使用Cutelyst::Utils::Sql

Open src/books.cpp and add the code to fetch the list of books:

打开src/books.cpp并添加获取图书列表的代码:

+ #include <QtSql>
+ #include <Cutelyst/Plugins/Utils/Sql>
void Books::list(Context *c)
{
+     QSqlQuery query = CPreparedSqlQueryThreadForDB("SELECT * FROM book", "MyDB");
+     if (query.exec()) {
+         c->setStash("books", Sql::queryToHashList(query));
+     }
-     c->setStash("books", "");
}

Test Run The Application

测试运行应用程序

Rebuild the application and and see the server restarting.

重建应用程序并查看服务器是否重新启动。

Some things you should note in the server output:

在服务器输出中应注意的一些事项:

To view the book list, change the URL in your browser to http://localhost:3000/books/list. You should get a list of the five books loaded by the myapp01.sql script above without any formatting. The rating for each book should appear on each row, but the "Author(s)" column will still be blank (we will fill that in later).

​要查看图书列表,请将浏览器中的URL更改为http://localhost:3000/books/list.你应该得到myapp01加载的五本书的列表。上面的sql脚本没有任何格式。每本书的评分应该出现在每一行,但“Author(s)”列仍然是空的(我们将在后面填写)。

You now have the beginnings of a simple but workable web application. Continue on to future sections and we will develop the application more fully.

现在,您已经开始创建一个简单但可行的web应用程序。继续下一节,我们将开发更全面地应用程序。

CREATE A WRAPPER FOR THE VIEW

为视图创建一个包装器

When using Grantlee, you can (and should) create a wrapper that will literally wrap content around each of your templates. This is certainly useful as you have one main source for changing things that will appear across your entire site/application instead of having to edit many individual files.

在使用Grantle时,您可以(也应该)创建一个包装器,将每个模板的内容包装起来。这当然很有用,因为你有一个主要的来源来改变整个网站/应用程序中出现的内容,而不必编辑许多单独的文件。

Configure the view in myapp.cpp For The Wrapper

在myapp.cpp中,为包装器配置视图。

In order to create a wrapper, you must first edit your view object and tell it where to find your wrapper file.

为了创建包装器,必须首先编辑视图对象,并告诉它在哪里可以找到包装器文件。

Edit your view in src/myapp.cpp and change it to match the following:

在src/myapp.cpp中编辑视图。并将其更改为与以下内容匹配:

bool MyApp::init()
{
    ...
    auto view = new GrantleeView(this);
    view->setIncludePaths({ pathTo("root/src") });
    ...
    view->setWrapper("wrapper.html"); // Add this line
    ...
}

Create the Wrapper Template File and Stylesheet

创建包装器模板文件和样式表

Next you need to set up your wrapper template. Basically, you'll want to take the overall layout of your site and put it into this file. For the tutorial, open root/src/wrapper.html and input the following:

接下来,您需要设置包装器模板。基本上,你需要把网站的整体布局放到这个文件中。对于本教程,请打开root/src/wrapper.html并输入以下内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" [%#
    %]"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>{{ template }}</title>
<link rel="stylesheet" href="/static/css/main.css" />
</head>

<body>
<div id="outer">
<div id="header">
    {% comment %} Insert the page title {% endcomment %}
    <h1>{{ site.title }}</h1>
</div>
    
<div id="bodyblock">
<div id="menu">
    Navigation:
    <ul>
        <li><a href="/books/list">Home</a></li>
        <li><a href="/" title="Cutelyst Welcome Page">Welcome</a></li>
        </ul>
</div><!-- end menu -->

<div id="content">
    {% comment %} Status and error messages {% endcomment %}
        <span class="message">{{ status_msg }}</span>
        <span class="error">{{ error_msg }}</span>
    {% comment %} This is where Grantlee will stick all of your template's contents.{% endcomment %}
    {{ content }}
</div><!-- end content -->
</div><!-- end bodyblock -->

<div id="footer">Copyright (c) your name goes here</div>
</div><!-- end outer -->

</body>
</html>

Notice the status and error message sections in the code above:

请注意上面代码中的状态和错误消息部分:

    <span class="status">{{ status_msg }}</span>
    <span class="error">{{ error_msg }}</span>

If we set either message in the Cutelyst stash (e.g., c->setStash("status_msg", "Request was successful!") it will be displayed whenever any view used by that request is rendered. The message and error CSS styles can be customized to suit your needs in the root/static/css/main.css file we create below.

如果我们在Cutelyst stash中设置了其中一条消息(例如,c->setStash("status_msg", "Request was successful!")每当呈现该请求使用的任何视图时,都会显示该视图。可以在root/static/css/main.css中定制消息和错误CSS样式,以满足您的需要。我们在下面创建css文件。

Notes:

笔记:

The Cutelyst stash only lasts for a single HTTP request. If you need to retain information across requests you can use Cutelyst::Plugin::Session (we will use Cutelyst sessions in the Authentication chapter of the tutorial). Although it is beyond the scope of this tutorial, you may wish to use a JavaScript or AJAX tool such as jQuery (http://www.jquery.com) or Dojo (http://www.dojotoolkit.org).

​Cutelyst stash只适用于一个HTTP请求。如果需要跨请求保留信息,可以使用Cutelyst::Plugin::Session(我们将在本教程的“身份验证”一章中使用Cutelyst会话)。虽然这超出了本教程的范围,但您可能希望使用JavaScript或AJAX工具,如jQuery(http://www.jquery.com)或者Dojo(http://www.dojotoolkit.org).

Create A Basic Stylesheet

创建基本样式表

First create a central location for stylesheets under the static directory:

首先在静态目录下为样式表创建一个中心位置:

$ mkdir -p root/static/css

Then open the file root/static/css/main.css (the file referenced in the stylesheet href link of our wrapper above) and add the following content:

然后打开文件root/static/css/main.css(上面包装器的样式表href链接中引用的文件)并添加以下内容:

#header {
    text-align: center;
}
#header h1 {
    margin: 0;
}
#header img {
    float: right;
}
#footer {
    text-align: center;
    font-style: italic;
    padding-top: 20px;
}
#menu {
    font-weight: bold;
    background-color: #ddd;
}
#menu ul {
    list-style: none;
    float: left;
    margin: 0;
    padding: 0 0 50% 5px;
    font-weight: normal;
    background-color: #ddd;
    width: 100px;
}
#content {
    margin-left: 120px;
}
.message {
    color: #390;
}
.error {
    color: #f00;
}

You may wish to check out a "CSS Framework" like Emastic (http://code.google.com/p/emastic/) as a way to quickly provide lots of high-quality CSS functionality.

​您可能希望查看像Emastic这样的“CSS框架”(http://code.google.com/p/emastic/)作为一种快速提供大量高质量CSS功能的方法。

Test Run The Application

测试运行应用程序

Rebuild and hit "Reload" in your web browser and you should now see a formatted version of our basic book list. (Again, the development server should have automatically restarted when you run make. If you are not using the "-r" option, you will need to hit Ctrl-C and manually restart it. Also note that the development server does NOT need to restart for changes to the Grantlee and static files we created and edited in the root directory -- those updates are handled on a per-request basis.)

重新构建并在web浏览器中点击“重新加载”,您现在应该可以看到基本图书列表的格式化版本。(同样,当您运行make时,开发服务器应该已经自动重新启动。如果您没有使用“-r”选项,您需要点击Ctrl-C并手动重新启动它。还要注意的是,对于我们在根目录中创建和编辑的Grantle和静态文件的更改,开发服务器不需要重新启动——这些更新是按请求处理的。)

Although our wrapper and stylesheet are obviously very simple, you should see how it allows us to control the overall look of an entire website from two central files. To add new pages to the site, just provide a template that fills in the content section of our wrapper template -- the wrapper will provide the overall feel of the page.

虽然我们的包装器和样式表显然非常简单,但您应该看到它如何允许我们从两个中心文件控制整个网站的整体外观。要向站点添加新页面,只需提供一个模板来填充包装器模板的内容部分——包装器将提供页面的整体感觉。

OPTIONAL INFORMATION

可选信息

NOTE: The rest of this chapter of the tutorial is optional. You can skip to Chapter 4, Basic CRUD, if you wish.

注意:本教程本章的其余部分是可选的。如果你愿意,你可以跳到第四章,基本CRUD。

Using 'RenderView' for the Default View

使用“RenderView”作为默认视图

Once your controller logic has processed the request from a user, it forwards processing to your view in order to generate the appropriate response output. Cutelyst uses Cutelyst::Action::RenderView by default to automatically perform this operation. If you look in src/root.h, you should see the empty definition for the sub end method:

一旦控制器逻辑处理了来自用户的请求,它就会将处理转发到视图,以便生成适当的响应输出。默认情况下,Cutelyst使用Cutelyst::Action::RenderView自动执行此操作。如果查看src/root.h、 您应该看到sub-end方法的空定义:

private:
    C_ATTR(End, :ActionClass("RenderView"))
    void End(Context *c) { Q_UNUSED(c); }

The following bullet points provide a quick overview of the RenderView process:

以下要点简要介绍了RenderView过程:

Root class is designed to hold application-wide logic. At the end of a given user request, Cutelyst will call the most specific End method that's appropriate. For example, if the controller for a request has an End method defined, it will be called. However, if the controller does not define a controller-specific End method, the "global" End method in Root.h will be called. Because the definition includes an ActionClass attribute, the Cutelyst::Action::RenderView logic will be executed after any code inside the definition of End is run. See Cutelyst::Manual::Actions for more information on ActionClass. Because End is empty, this effectively just runs the default logic in RenderView. However, you can easily extend the RenderView logic by adding your own code inside the empty method body ({}) created by the Cutelyst Helpers when we first ran the cutelst command to initialize our application.

根类用于保存应用程序范围的逻辑。在给定的用户请求结束时,Cutelyst将调用最具体的适当End方法。例如,如果请求的控制器定义了End方法,则将调用它。但是,如果控制器未定义特定于控制器的结束方法,则Root.h中的“全局”End方法将被调用。因为定义包含ActionClass属性,所以在运行End定义中的任何代码后,都将执行Cutelyst::Action::RenderView逻辑。有关ActionClass的更多信息,请参见Cutelyst::Manual::Actions。因为End是空的,所以它实际上只是运行RenderView中的默认逻辑。然而,当我们第一次运行cutelst命令初始化应用程序时,您可以通过在Cutelyst助手创建的空方法体({})中添加自己的代码来轻松扩展RenderView逻辑。

Calling the View Renderer directly

直接调用视图渲染器

When using the End action there is no easy way to call different view renderers. For instance you want one action to be rendered through grantlee and another in the same class through the JSON renderer. In this case you need to register both views in the init() method but you need to give them unique names passed as the second parameter:

当使用End动作时,没有简单的方法来调用不同的视图渲染器。例如,您希望通过Grantle呈现一个动作,并通过JSON呈现器呈现同一类中的另一个动作。在这种情况下,需要在init() 方法中注册这两个视图,但需要为它们指定作为第二个参数传递的唯一名称:

bool MyApp::init() {
  new GrantleeView(this, "grantlee_view");
  new ViewJson(this, "json_view");

  return true;
}

In the header file you can now decide which renderer to use by setting the :View C_ATTR parameter:

在头文件中,您现在可以通过设置:View C_ATTR 参数来决定使用哪个渲染器:

C_ATTR(index, :Path :Args(0) :ActionClass("RenderView") :View("grantlee_view"))
void index(Context *c);

C_ATTR(entries, :Local :Args(0) :ActionClass("RenderView") :View("json_view"))
void entries(Context *c);

Using The Default Template Name

使用默认模板名称

By default, Cutelyst::View::Grantlee will look for a template that uses the same name as your controller action, allowing you to save the step of manually specifying the template name in each action. For example, this would allow us to remove the c->setStash("template", "books/list.html"); line of our list action in the Books controller. Open src/books.cpp in your editor and comment out this line to match the following:

默认情况下,Cutelyst::View::Grantlee将查找与控制器操作使用相同名称的模板,允许您保存在每个操作中手动指定模板名称的步骤。例如,这将允许我们删除c->setStash("template", "books/list.html");Books控制器中的列表操作行。打开src/books.cpp,在编辑器中输入,并注释掉这一行,以匹配以下内容:

-    c->setStash("template", "books/list.html");
+    // c->setStash("template", "books/list.html");

You should now be able to access the http://localhost:3000/books/list URL as before.

​您现在应该可以访问http://localhost:3000/books/listURL和以前一样。

NOTE: If you use the default template technique, you will not be able to use either the c->forward or the c->detach mechanisms (these are discussed in Chapter 2 and Chapter 9 of the Tutorial).

注意:如果使用默认模板技术,将无法使用c->forward或c->detach机制(本教程第2章和第9章讨论了这些机制)。

IMPORTANT: Make sure that you do not skip the following section before continuing to the next chapter 4 Basic CRUD.

重要提示:在继续下一章4基本CRUD之前,请确保不要跳过以下部分。

Return To A Manually Specified Template

返回到手动指定的模板

In order to be able to use c->forward() and c->detach() later in the tutorial, you should remove the comment from the statement in the method list in MyApp/src/books.cpp:

为了能够在本教程后面的部分中使用c->forward()和c->detach() ,您应该从MyApp/src/books.cpp的方法列表中的语句中删除注释:

-    // c->setStash("template", "books/list.html");
+    c->setStash("template", "books/list.html");

Check the http://localhost:3000/books/list URL in your browser. It should look the same manner as with earlier sections.

​检查http://localhost:3000/books/list浏览器中的URL。它的外观应该与前面的部分相同。

You can jump to the next chapter of the tutorial here: Basic CRUD

​你可以在这里跳到教程的下一章:基本CRUD

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值