REST API

REST接口

To use a web-service, we first need to create one. We will use Flask (https://flask.palletsprojects.com) a simple HTTP app server based on python to create a simple color web-service. You could also use every other web server which accepts and returns JSON data. The idea is to have a list of named colors, which can be managed via the web-service. Managed in this case means CRUD (create-read-update-delete).

​要使用web服务,我们首先需要创建一个。我们将使用Flask(https://flask.palletsprojects.com)一个基于python的简单HTTP应用服务器,用于创建简单的彩色web服务。您还可以使用其他所有接收和返回JSON数据的web服务器。这个想法是要有一个命名颜色的列表,可以通过web服务进行管理。在这种情况下,托管意味着CRUD(创建-读取-更新-删除)。

A simple web-service in Flask can be written in one file. We start with an empty server.py file. Inside this file, we create some boiler-code and load our initial colors from an external JSON file. See also the Flask quickstart documentation.

​Flask中的一个简单web服务可以写在一个文件中。我们从一个空server.py文件开始。在这个文件中,我们创建一些锅炉代码,并从外部JSON文件加载初始颜色。另请参阅Flask quickstart文档。

from flask import Flask, jsonify, request
import json

with open('colors.json', 'r') as file:
    colors = json.load(file)

app = Flask(__name__)
# Services registration & implementation...
if __name__ == '__main__':
    app.run(debug = True)

When you run this script, it will provide a web-server at http://localhost:5000, which does not serve anything useful yet.

​运行此脚本时,它将提供web服务http://localhost:5000,它还没有提供任何有用的东西。

We will now start adding our CRUD (Create,Read,Update,Delete) endpoints to our little web-service.

现在,我们将开始向我们的小web服务添加CRUD(创建、读取、更新、删除)端点。

Read Request

读取请求

To read data from our web-server, we will provide a GET method for all colors.

为了从我们的web服务器读取数据,我们将为所有颜色提供一个GET方法。

@app.route('/colors', methods = ['GET'])
def get_colors():
    return jsonify({ "data" : colors })

This will return the colors under the ‘/colors’ endpoint. To test this we can use curl to create an HTTP request.

这将返回“/colors”端点下的颜色。为了测试这一点,我们可以使用curl创建一个HTTP请求。

curl -i -GET http://localhost:5000/colors

Which will return us the list of colors as JSON data.

它会将颜色列表作为JSON数据返回给我们。

Read Entry

读取条目

To read an individual color by name we provide the details endpoint, which is located under /colors/<name>. The name is a parameter to the endpoint, which identifies an individual color.

要按名称读取单个颜色,我们提供了详细信息端点,它位于/colors/<name>下。名称是端点的一个参数,用于标识单个颜色。

@app.route('/colors/<name>', methods = ['GET'])
def get_color(name):
    for color in colors:
        if color["name"] == name:
            return jsonify(color)
    return jsonify({ 'error' : True })

And we can test it with using curl again. For example to get the red color entry.

我们可以再次使用curl进行测试。例如,获取红色条目。

curl -i -GET http://localhost:5000/colors/red

It will return one color entry as JSON data.

它将返回一个颜色条目作为JSON数据。

Create Entry

创建条目

Till now we have just used HTTP GET methods. To create an entry on the server side, we will use a POST method and pass the new color information with the POST data. The endpoint location is the same as to get all colors. But this time we expect a POST request.

到目前为止,我们只使用了HTTP GET方法。为了在服务器端创建条目,我们将使用POST方法,并将新的颜色信息与POST数据一起传递。端点位置与获取所有颜色的位置相同。但这一次,我们期待一个POST请求。

@app.route('/colors', methods= ['POST'])
def create_color():
    print('create color')
    color = {
        'name': request.json['name'],
        'value': request.json['value']
    }
    colors.append(color)
    return jsonify(color), 201

Curl is flexible enough to allow us to provide JSON data as the new entry inside the POST request.

Curl足够灵活,允许我们在POST请求中提供JSON数据作为新条目。

curl -i -H "Content-Type: application/json" -X POST -d '{"name":"gray1","value":"#333"}' http://localhost:5000/colors

Update Entry

更新条目

To update an individual entry we use the PUT HTTP method. The endpoint is the same as to retrieve an individual color entry. When the color was updated successfully we return the updated color as JSON data.

要更新单个条目,我们使用PUT HTTP方法。端点与检索单个颜色条目相同。成功更新颜色后,我们将更新后的颜色作为JSON数据返回。

@app.route('/colors/<name>', methods= ['PUT'])
def update_color(name):
    for color in colors:
        if color["name"] == name:
            color['value'] = request.json.get('value', color['value'])
            return jsonify(color)
    return jsonify({ 'error' : True })

In the curl request, we only provide the values to be updated as JSON data and then a named endpoint to identify the color to be updated.

在curl请求中,我们只提供要更新为JSON数据的值,然后提供一个命名端点来标识要更新的颜色。

curl -i -H "Content-Type: application/json" -X PUT -d '{"value":"#666"}' http://localhost:5000/colors/red

Delete Entry

Deleting an entry is done using the DELETE HTTP verb. It also uses the same endpoint for an individual color, but this time the DELETE HTTP verb.

删除条目是使用HTTP关键字DELETE完成的。它还对单个颜色使用相同的端点,但这一次是HTTP关键字DELETE。

@app.route('/colors/<name>', methods=['DELETE'])
def delete_color(name):
    for color in colors:
        if color["name"] == name:
            colors.remove(color)
            return jsonify(color)
    return jsonify({ 'error' : True })

This request looks similar to the GET request for an individual color.

此请求看起来类似于对单个颜色的GET请求。

curl -i -X DELETE http://localhost:5000/colors/red

HTTP Verbs

HTTP关键字

Now we can read all colors, read a specific color, create a new color, update a color and delete a color. Also, we know the HTTP endpoints to our API.

现在我们可以读取所有颜色、读取特定颜色、创建新颜色、更新颜色和删除颜色。此外,我们知道API的HTTP端点。

# Read All
GET    http://localhost:5000/colors
# Create Entry
POST   http://localhost:5000/colors
# Read Entry
GET    http://localhost:5000/colors/${name}
# Update Entry
PUT    http://localhost:5000/colors/${name}
# Delete Entry
DELETE http://localhost:5000/colors/${name}

Our little REST server is complete now and we can focus on QML and the client side. To create an easy to use API we need to map each action to an individual HTTP request and provide a simple API to our users.

我们的小小REST服务器现在已经完成,我们可以专注于QML和客户端。为了创建一个易于使用的API,我们需要将每个操作映射到一个单独的HTTP请求,并为用户提供一个简单的API。

Client REST

客户端REST

To demonstrate a REST client we write a small color grid. The color grid displays the colors retrieved from the web-service via HTTP requests. Our user interface provides the following commands:

为了演示REST客户机,我们编写了一个小的颜色网格。颜色网格显示通过HTTP请求从web服务检索到的颜色。我们的用户界面提供以下命令:

  • Get a color list
  • 获取颜色列表
  • Create color
  • 创造色彩
  • Read the last color
  • 读最后一种颜色
  • Update last color
  • 更新最后一种颜色
  • Delete the last color
  • 删除最后一种颜色

We bundle our API into an own JS file called colorservice.js and import it into our UI as Service. Inside the service module (colorservice.js), we create a helper function to make the HTTP requests for us:

我们将API绑定到一个名colorservice.js的JS文件中。并将其作为Service导入我们的UI。在服务模块(colorservice.js)中,我们创建了一个helper函数来为我们发出HTTP请求:

function request(verb, endpoint, obj, cb) {
    print('request: ' + verb + ' ' + BASE + (endpoint ? '/' + endpoint : ''))
    var xhr = new XMLHttpRequest()
    xhr.onreadystatechange = function() {
        print('xhr: on ready state change: ' + xhr.readyState)
        if(xhr.readyState === XMLHttpRequest.DONE) {
            if(cb) {
                var res = JSON.parse(xhr.responseText.toString())
                cb(res)
            }
        }
    }
    xhr.open(verb, BASE + (endpoint ? '/' + endpoint : ''))
    xhr.setRequestHeader('Content-Type', 'application/json')
    xhr.setRequestHeader('Accept', 'application/json')
    var data = obj ? JSON.stringify(obj) : ''
    xhr.send(data)
}

It takes four arguments. The verb, which defines the HTTP verb to be used (GET, POST, PUT, DELETE). The second parameter is the endpoint to be used as a postfix to the BASE address (e.g. ‘http://localhost:5000/colors’). The third parameter is the optional obj, to be sent as JSON data to the service. The last parameter defines a callback to be called when the response returns. The callback receives a response object with the response data. Before we send the request, we indicate that we send and accept JSON data by modifying the request header.

​它需要四个参数。verb,定义要使用的HTTP关键字(GET、POST、PUT、DELETE)。第二个参数是用作基址后缀的端点(例如'http://localhost:5000/colors’). 第三个参数是可选的obj,将作为JSON数据发送到服务。最后一个参数定义了响应返回时要调用的回调。回调函数接收带有响应数据的响应对象。在发送请求之前,我们通过修改请求头指示发送和接受JSON数据。

Using this request helper function we can implement the simple commands we defined earlier (create, read, update, delete). This code resides in the service implementation:

使用这个请求助手函数,我们可以实现前面定义的简单命令(创建、读取、更新、删除)。此代码驻留在服务实现中:

function getColors(cb) {
    // GET http://localhost:5000/colors
    request('GET', null, null, cb)
}

function createColor(entry, cb) {
    // POST http://localhost:5000/colors
    request('POST', null, entry, cb)
}

function getColor(name, cb) {
    // GET http://localhost:5000/colors/${name}
    request('GET', name, null, cb)
}

function updateColor(name, entry, cb) {
    // PUT http://localhost:5000/colors/${name}
    request('PUT', name, entry, cb)
}

function deleteColor(name, cb) {
    // DELETE http://localhost:5000/colors/${name}
    request('DELETE', name, null, cb)
}

In the UI we use the service to implement our commands. We have a ListModel with the id gridModel as a data provider for the GridView. The commands are indicated using a Button UI element.

在UI中,我们使用服务来实现我们的命令。我们有一个id为gridModel的ListModel作为GridView的数据提供者。使用按钮Button UI元素类型发送命令。

Importing our service library is pretty straightforward:

导入我们的服务库非常简单:

import "colorservice.js" as Service

Reading the color list from the server:

正在从服务器读取颜色列表:

Button {
    text: 'Read Colors'
    onClicked: {
        Service.getColors(function(response) {
            print('handle get colors response: ' + JSON.stringify(response))
            gridModel.clear()
            const entries = response.data
            for(let i=0; i<entries.length; i++) {
                gridModel.append(entries[i])
            }
        })
    }
}

Create a new color entry on the server:

在服务器上创建新的颜色条目:

Button {
    text: 'Create New'
    onClicked: {
        const index = gridModel.count - 1
        const entry = {
            name: 'color-' + index,
            value: Qt.hsla(Math.random(), 0.5, 0.5, 1.0).toString()
        }
        Service.createColor(entry, function(response) {
            print('handle create color response: ' + JSON.stringify(response))
            gridModel.append(response)
        })
    }
}

Reading a color based on its name:

根据颜色名称读取颜色:

Button {
    text: 'Read Last Color'
    onClicked: {
        const index = gridModel.count - 1
        const name = gridModel.get(index).name
        Service.getColor(name, function(response) {
            print('handle get color response:' + JSON.stringify(response))
            message.text = response.value
        })
    }
}

Update a color entry on the server based on the color name:

根据颜色名称更新服务器上的颜色条目:

Button {
    text: 'Update Last Color'
    onClicked: {
        const index = gridModel.count - 1
        const name = gridModel.get(index).name
        const entry = {
            value: Qt.hsla(Math.random(), 0.5, 0.5, 1.0).toString()
        }
        Service.updateColor(name, entry, function(response) {
            print('handle update color response: ' + JSON.stringify(response))
            gridModel.setProperty(gridModel.count - 1, 'value', response.value)
        })
    }
}

Delete a color by the color name:

按颜色名称删除颜色:

Button {
    text: 'Delete Last Color'
    onClicked: {
        const index = gridModel.count - 1
        const name = gridModel.get(index).name
        Service.deleteColor(name)
        gridModel.remove(index, 1)
    }
}

This concludes the CRUD (create, read, update, delete) operations using a REST API. There are also other possibilities to generate a Web-Service API. One could be module based and each module would have one endpoint. And the API could be defined using JSON RPC (http://www.jsonrpc.org/). Sure also XML based API is possible and but the JSON approach has great advantages as the parsing is built into the QML/JS as part of JavaScript.

​这就结束了使用REST API的CRUD(创建、读取、更新、删除)操作。还有其他生成Web服务API的可能性。一个可以是基于模块的,每个模块都有一个端点。API可以使用JSON RPC定义(http://www.jsonrpc.org/)。当然,基于XML的API也是可能的,但是JSON方法有很大的优势,因为解析是作为JavaScript的一部分内置到QML/JS中的。

示例源码下载​​​​​​​

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐