创建RESTful应用程序

Creating RESTful Applications

创建RESTful应用程序

Anıl Özbek edited this page on Jan 12, 2019 · 2 revisions

AnılÖzbek于2019年1月12日编辑了该页面

This tutorial was written by Daniel Nicoletti and was first published on cutelyst.org and his personal blog.

​本教程由Daniel Nicoletti编写,首次发表在cutelyst.org和他的个人博客上。

Creating RESTful Applications

创建RESTful应用程序

This mini tutorial aims to show the fundamentals of creating a RESTful application with Qt, as a client and as a server with the help of Cutelyst.

本迷你教程旨在展示在Cutelyst的帮助下,使用Qt作为客户端和服务器创建RESTful应用程序的基本原理。

REST

Services with REST APIs have become very popular in recent years, and interacting with them may be necessary to integrate with services and keep your application relevant, as well as it may be interesting to replace your own protocol with a REST implementation.

近年来,带有REST API的服务变得非常流行,与它们交互可能是与服务集成并保持应用程序相关性所必需的,用REST实现替换您自己的协议可能也很有趣。

REST is very associated with JSON, however, JSON is not required for a service to become RESTful, the way data is exchanged is chosen by the one who defines the API, ie it's possible to have REST exchanging messages in XML or another format. We'll use JSON for its popularity, simplicity and due to the QJsonDocument class being present in the Qt Core module.

​REST与JSON有着密切的联系,然而,服务不需要JSON,数据交换的方式由定义API的人选择,也就是说,REST可以以XML或其他格式交换消息。我们将使用JSON,因为它的流行性、简单性以及QJsonDocument类存在于Qt核心模块中。

A REST service is mainly characterized by making use of the little-used HTTP headers and methods, browsers basically use GET to get data and POST to send form and file data, however REST clients will use other methods like DELETEPUT, and HEAD, concerning headers many APIs define headers for authentication, for example X-Application-Token can contain a key generated only for the application of a user X, so that if this header doesn't contain the correct data it'll not have access to the data.

REST服务的主要特点是使用很少使用的HTTP头和方法,浏览器基本上使用GET获取数据和POST发送表单和文件数据,而REST客户端将使用其他方法,如DELETE、PUT和HEAD,关于头,许多API定义了用于身份验证的头,例如,X-Application-Token可以包含仅为用户X的应用程序生成的密钥,因此,如果此标头不包含正确的数据,它将无法访问该数据。

Let's start by defining the server API:

让我们从定义服务器API开始:

  • /api/v1/users
    • GET - Gets the list of users 获取用户列表
      • Answer: ["uuid1", "uuid2"]
    • POST - Register new user 注册新用户
      • Send: {"name": "someone", "age": 32}
      • Answer: {"status": "ok / error", "uuid": "new user uuid", "error": "msg in case of error"}
  • /api/v1/users/ - where UUID should be replaced by the user's UUID 其中UUID应替换为用户的UUID
    • GET - Gets user information 获取用户信息
      • Answer: {"name": "someone", "age": 32}
    • PUT - Update user information 更新用户信息
      • Send: {"name": "someone", "age": 57}
      • Answer: {"status": "ok / error", "error": "msg in case of error"}
    • DELETE - Delete user 删除用户
      • Answer: {"status": "ok / error", "error": "msg in case of error"}

For the sake of simplicity we'll store the data using QSettings, we don't recommend it for real applications, but Sql or something like that escapes from the scope of this tutorial. We also assume that you already have Qt and Cutelyst installed, the code is available at github.com/ceciletti/example-qt-cutelyst-rest

​为了简单起见,我们将使用QSettings存储数据,我们不建议将其用于实际应用程序,但Sql或类似的内容不属于本教程的范围。我们还假设您已经安装了Qt和Cutelyst,代码可以在github上找到。

RESTful Server with C++, Cutelyst and Qt

使用C++、Cutelyst和Qt的REST服务器

First we create the server application:

首先,我们创建服务器应用程序:

$ cutelyst2 --create-app ServerREST

And then we'll create the Controller that will have the API methods:

然后我们将创建具有API方法的控制器:

$ cutelyst2 --controller ApiV1

Once the new class has been instantiated in serverrest.cppinit() method with:

init()中实例化serverrest.cpp中的新类:

#include "apiv1.h"

bool ServerREST::init()
{
    new ApiV1 (this);
    ...

Add the following methods to the file apiv1.h:

将以下方法添加到文件apiv1.h中:

C_ATTR(users, :Local :AutoArgs :ActionClass(REST))
void users(Context *c);

C_ATTR(users_GET, :Private)
void users_GET(Context *c);

C_ATTR(users_POST, :Private)
void users_POST(Context *c);

C_ATTR(users_uuid, :Path('users') :AutoArgs :ActionClass(REST))
void users_uuid(Context *c, const QString &uuid);

C_ATTR(users_uuid_GET, :Private)
void users_uuid_GET(Context *c, const QString &uuid);

C_ATTR(users_uuid_PUT, :Private)
void users_uuid_PUT(Context *c, const QString &uuid);

C_ATTR(users_uuid_DELETE, :Private)
void users_uuid_DELETE(Context *c, const QString &uuid);

The C_ATTR macro is used to add metadata about the class that the MOC will generate, so Cutelyst knows how to map the URLs to those functions.

C_ATTR宏用于添加有关MOC将生成的类的元数据,因此Cutelyst知道如何将URL映射到这些函数。

  • :Local - Map method name to URL by generating /api/v1/users
  • :Local -通过生成/api/v1/users将方法名称映射到URL
  • :AutoArgs - Automatically checks the number of arguments after the Context *, in users_uuid we have only one, so the method will be called if the URL is /api/v1/users/any-thing
  • :AutoArgs -自动检查上下文*后的参数数量,在users_uuid中,我们只有一个参数,因此如果URL为/api/v1/users/anything,将调用该方法
  • :ActionClass(REST) ​​- Will load the REST plugin that will create an Action class to take care of this method, ActionREST will call the other methods depending on the called method
  • :ActionClass(REST) ​​- 将加载REST插件,该插件将创建一个操作类来处理此方法,ActionREST将根据调用的方法调用其他方法
  • :Private - Registers the action as private in Cutelyst, so that it is not directly accessible via URL
  • :Private -在Cutelyst中将操作注册为Private,因此无法通过URL直接访问

This is enough to have an automatic mapping depending on the HTTP method for each function, it's important to note that the first function (without _METHOD) is always executed, for more information see the API of ActionREST.

​这就足够根据每个函数的HTTP方法进行自动映射了,需要注意的是,第一个函数(没有_METHOD)总是执行的,有关更多信息,请参阅ActionREST的API。

For brevity I will show only the GET code of users, the rest can be seen in GitHub:

为了简洁起见,我将只显示用户的GET代码,其余代码可以在GitHub中看到:

void ApiV1::users_GET(Context *c)
{
    QSettings s;
    const QStringList uuids = s.childGroups();

    c->response()->setJsonArrayBody(QJsonArray::fromStringList(uuids));
}

After implemented all methods, start the server:

实现所有方法后,启动服务器:

$ cutelyst2 -r --server --app-file path_to_it

To test the API you can test a POST with curl:

要测试API,可以使用curl测试POST:

$ curl -H "Content-Type: application/json" -X POST -d '{"name": "someone", "age": 32}' http://localhost:3000/api/v1/users

Okay, now you have a REST server application, made with Qt, with one of the fastest answers in the old west :)

好了,现在您有了一个REST服务器应用程序,它是用Qt开发的,它提供了旧西方最快的答案之一:)

Now let's go to second part, which is to create the client application that will consume this API.

现在让我们进入第二部分,即创建将使用此API的客户端应用程序。

REST Client Application

REST客户端应用程序

First create a QWidgets project with a QMainWindow, the goal here is just to see how to create REST requests from Qt code, so we assume that you are already familiar with creating graphical interfaces with it.

首先,使用QMainWindow创建一个QWidgets项目,这里的目标只是看看如何从Qt代码创建REST请求,所以我们假设您已经熟悉如何使用它创建图形界面。

Our interface will be composed of:

我们的界面将由以下部分组成:

  • 1 QComboBox where we will list users' UUIDs
  • 1个QComboBox,我们将在其中列出用户的UUID
  • 1 QLineEdit to enter and display the user name
  • 1.QLineEdit以输入并显示用户名
  • 1 QSpinBox to enter and view user age
  • 1个QSpinBox用于输入和查看用户年龄
  • 2 QPushButton
  • 2个QPushButton
    • To create or update a user's registry 创建或更新用户的注册表
    • To delete the user record 删除用户记录

Once designed the interface, our QMainWindow sub-class needs to have a pointer to QNetworkAccessManager, this is the class responsible for handling communication with network services such as HTTP and FTP. This class works asynchronously, it has the same operation as a browser that will create up to 6 simultaneous connections to the same server, if you have made more requests at the same time it'll put them in a queue (or pipeline them if set).

一旦设计了接口,我们的QMainWindow子类需要有一个指向QNetworkAccessManager的指针,这个类负责处理与HTTP和FTP等网络服务的通信。这个类是异步工作的,它的操作与浏览器相同,浏览器将创建多达6个到同一服务器的同时连接,如果您同时发出了更多请求,它将把它们放入队列中(如果设置了,则将它们放入管道)。

Then create a QNetworkAccessManager *m_nam; as a member of your class so we can reuse it. Our request to obtain the list of users will be quite simple:

然后创建一个QNetworkAccessManager *m_nam;作为你们班的一员,我们可以重用它。我们获取用户名单的要求非常简单:

QNetworkRequest request(QUrl("http://localhost:3000/api/v1/users"));

QNetworkReply *reply = m_nam->get(request);
connect(reply, &QNetworkReply::finished, this, [this, reply] {
    reply->deleteLater();
    const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
    const QJsonArray array = doc.array();

    for (const QJsonValue &value : array) {
        ui->uuidCB->addItem(value.toString());
    }
});

This fills with the data via GET from the server our QComboBox, now we will see the registration code which is a little more complex:

这将通过从服务器获取数据填充我们的QComboBox,现在我们将看到注册码,它稍微复杂一些:

QNetworkRequest request(QUrl("http://localhost:3000/api/v1/users"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

QJsonObject obj {
    {"name", ui->nameLE->text()},
    ("age", ui->ageSP->value()}
};

QNetworkReply *reply = m_nam->post(request, QJsonDocument(obj).toJson());
connect(reply, &QNetworkReply::finished, this, [this, reply] {
    reply->deleteLater();
    const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
    const QJsonObject obj = doc.object();

    if (obj.value("status").toString() == "ok") {
        ui->uuidCB->addItem(obj.value("uuid").toString());
    } else {
        qWarning() << "ERROR" << obj.value("error").toString();
    }
});

With the above code we send an HTTP request using the POST method, like PUT it accepts sending data to the server. It's important to inform the server with what kind of data it will be dealing with, so the "Content-Type" header is set to "application/json", Qt issues a warning on the terminal if the content type has not been defined. As soon as the server responds we add the new UUID in the combobox so that it stays up to date without having to get all UUIDs again.

在上面的代码中,我们使用POST方法发送HTTP请求,比如PUT,它接受向服务器发送数据。重要的是通知服务器它将处理什么样的数据,因此“Content Type”头被设置为“application/json”,如果没有定义内容类型,Qt会在终端上发出警告。一旦服务器响应,我们就在组合框中添加新的UUID,这样它就可以保持最新,而无需再次获取所有UUID。

As demonstrated QNetworkAccessManager already has methods ready for the most common REST actions, however if you wanted to send a request of type OPTIONS for example will have to create a request of type CustomOperation:

正如所演示的那样,QNetworkAccessManager已经为最常见的REST操作准备好了方法,但是如果您想发送一个类型为OPTIONS的请求,例如,必须创建一个类型为CustomOperation的请求:

m_nam->sendCustomRequest("OPTIONS", request);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Spring Boot 中,创建 RESTful API 接口的步骤如下: 1. 在 Spring Boot 应用程序中添加 Spring Web Starter 依赖,以便使用 Spring MVC 框架来创建 RESTful API 接口。可以在 Maven 或 Gradle 中添加如下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` 2. 创建一个 Controller 类,用于处理客户端发送的请求。可以在该类上添加 @RestController 或 @Controller 注解,使其成为一个 RESTful API 的控制器。 3. 在 Controller 类中定义请求处理方法,处理客户端发送的请求,并返回相应的数据。可以在请求处理方法上添加 @RequestMapping、@GetMapping、@PostMapping 等注解,用于定义请求的 URL 映射和请求类型。 4. 使用 @RequestBody 注解获取客户端发送的请求数据,并使用 @ResponseBody 注解将处理结果返回给客户端。在处理方法中,可以调用业务逻辑层的方法来实现具体的业务逻辑。 5. 为了保证数据的安全性,可以在请求时使用 HTTPS 协议进行加密传输,并在客户端和服务器端都进行数据校验,防止数据被篡改或者恶意攻击。 例如,以下是一个处理 GET 请求的 RESTful API 接口示例: ```java @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public User getUserById(@PathVariable("id") Long id) { return userService.getUserById(id); } } ``` 在该示例中,使用 @RestController 注解标记了一个类为 RESTful API 的控制器,使用 @RequestMapping 注解指定了处理请求的 URL 映射为 "/user",并使用 @GetMapping 注解定义了一个处理 GET 请求的请求处理方法。该方法通过 @PathVariable 注解获取客户端发送的请求参数,调用了业务逻辑层的方法获取用户信息,并将结果作为返回值返回给客户端。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值