Python 仅在HTTP客户端发生更改时使用版本化资源为其提供服务-使用Flask

Python 仅在HTTP客户端发生更改时使用版本化资源为其提供服务-使用Flask,python,flask,download,Python,Flask,Download,我正在运行一个基于Flask的Web服务器,它为正在进行版本控制的资源提供服务(例如,某些版本控制的程序的安装文件)。我只想为我的HTTP客户机提供新资源,以防它没有可用的当前版本。如果有新版本,我希望客户端下载资源并安装它 我的烧瓶服务器看起来像这样 import json import redis import math import requests from flask import Flask,render_template,request app=Flask(__name__)

我正在运行一个基于Flask的Web服务器,它为正在进行版本控制的资源提供服务(例如,某些版本控制的程序的安装文件)。我只想为我的HTTP客户机提供新资源,以防它没有可用的当前版本。如果有新版本,我希望客户端下载资源并安装它

我的烧瓶服务器看起来像这样

import json
import redis
import math
import requests
from flask import Flask,render_template,request

app=Flask(__name__)

@app.route('/version', methods=['GET','POST'])
def getversion():

    r_server=redis.Redis("127.0.0.1")

    if request.method == 'POST':
        jsonobj_recieve=request.data
        data=json.loads(jsonobj)
        currentversion=r_server.hget('version')

        if data == currentversion:
            #code to return a 'ok'
        else:
            #code to return 'not ok' also should send the updated file to the client
    else:
        return r_server.hget('version')

if __name__ == '__main__':
    app.run(
        debug=True,
        host="127.0.0.1",
        port=80
    )
我的客户非常基本:

import sys
import json
import requests

url="http://127.0.0.1/version"
jsonobj=json.dumps(str(sys.argv[1]))

print jsonobj

r=requests.post(url,data=jsonobj)

我可能需要重新编码整个客户端,这不是问题,但我真的不知道从哪里开始……

有多种方法可以实现这一点,但由于这是一个Flask应用程序,这里有一种使用HTTP的方法

如果版本正常,只需返回相关的状态代码,如。如果有必要,可以在正文中添加JSON响应。如果您返回一个带有flask的字符串,状态代码将为200 OK,您可以在客户端中检查它

如果版本不同,请返回文件所在的URL。客户将不得不 下载文件。这是非常简单的使用。以下是下载文件的典型示例:


这是非常简单的。如果您的文件不是静态的,并且无法在服务器上运行(就像软件更新补丁可能不应该这样),那么您必须找到一种从数据库获取文件或动态生成文件的方法。

有多种方法可以实现这一点,但由于这是一个Flask应用程序,下面是一种使用HTTP的方法

如果版本正常,只需返回相关的状态代码,如。如果有必要,可以在正文中添加JSON响应。如果您返回一个带有flask的字符串,状态代码将为200 OK,您可以在客户端中检查它

如果版本不同,请返回文件所在的URL。客户将不得不 下载文件。这是非常简单的使用。以下是下载文件的典型示例:


这是非常简单的。如果您的文件不是静态的,并且无法在服务器上运行(就像软件更新补丁可能不应该这样),那么您必须找到一种从数据库获取文件或动态生成文件的方法。

有多种方法可以实现这一点,但由于这是一个Flask应用程序,下面是一种使用HTTP的方法

如果版本正常,只需返回相关的状态代码,如。如果有必要,可以在正文中添加JSON响应。如果您返回一个带有flask的字符串,状态代码将为200 OK,您可以在客户端中检查它

如果版本不同,请返回文件所在的URL。客户将不得不 下载文件。这是非常简单的使用。以下是下载文件的典型示例:


这是非常简单的。如果您的文件不是静态的,并且无法在服务器上运行(就像软件更新补丁可能不应该这样),那么您必须找到一种从数据库获取文件或动态生成文件的方法。

有多种方法可以实现这一点,但由于这是一个Flask应用程序,下面是一种使用HTTP的方法

如果版本正常,只需返回相关的状态代码,如。如果有必要,可以在正文中添加JSON响应。如果您返回一个带有flask的字符串,状态代码将为200 OK,您可以在客户端中检查它

如果版本不同,请返回文件所在的URL。客户将不得不 下载文件。这是非常简单的使用。以下是下载文件的典型示例:

这是非常简单的。如果您的文件不是静态的,并且不能在服务器上运行(就像软件更新补丁可能不应该这样),那么您必须找到从数据库获取文件或动态生成文件的方法。

Requirements Review
  • 拥有web应用程序,为版本化资源提供服务。例如,它可以是带有应用程序的文件
  • have client,它只允许在服务器上的资源版本和客户端在本地已经可用的版本不同的情况下获取资源
  • 客户端知道资源的版本字符串
  • 如果新版本可用,允许客户端学习新版本字符串
解决方案的类似HTTP的设计 如果仅在客户端尚未下载应用程序的情况下才允许下载应用程序,则可以使用以下设计:

  • 使用
    etag
    标题。这通常包含一些字符串,描述您希望从该url获取的资源的唯一状态。在您的情况下,它可能是应用程序的当前版本号
  • 在您的请求中,使用标题“if none match”,提供客户端当前应用程序的版本号。这将导致HTTP状态代码
    306-未修改
    ,以防客户端和服务器共享相同版本的资源。如果不同,您只需提供资源的内容并使用它即可。您的资源还应在
    etag
    中表示资源的当前版本,您的客户应注意此版本,或从其他来源(如下载的文件)查找新版本名称
此设计遵循HTTP原则

etag
这是重点展示的原则,你应该详细说明提供资源的真实内容

from flask import Flask, Response, request
import werkzeug.exceptions

app = Flask(__name__)

class NotModified(werkzeug.exceptions.HTTPException):
    code = 304
    def get_response(self, environment):
        return Response(status=304)

@app.route('/download/app')
def downloadapp():
    currver = "1.0"
    if request.if_none_match and currver in request.if_none_match:
        raise NotModified
    def generate():
        yield "app_file_part 1"
        yield "app_file_part 2"
        yield "app_file_part 3"
    return Response(generate(), headers={"etag": currver})


if __name__ == '__main__':
    app.run(debug=True)
客户端仅获取资源(如果是新资源) 使用web服务器的web部件替代解决方案(例如NGINX) 假设资源是静态文件,仅在某个时间更新,则您应能够配置web服务器(例如NGINX)以服务于该资源,并在配置中为
etag
标题声明版本字符串的显式值

请注意,由于未提出要求,本替代解决方案未在此处详细说明(且未进行测试)

客户机实现不会因此而改变(在这里,它会回报设计遵循HTTP概念)。

需求审查
  • 拥有web应用程序,为版本化资源提供服务。它可以是一个应用程序文件
    from flask import Flask, Response, request
    import werkzeug.exceptions
    
    app = Flask(__name__)
    
    class NotModified(werkzeug.exceptions.HTTPException):
        code = 304
        def get_response(self, environment):
            return Response(status=304)
    
    @app.route('/download/app')
    def downloadapp():
        currver = "1.0"
        if request.if_none_match and currver in request.if_none_match:
            raise NotModified
        def generate():
            yield "app_file_part 1"
            yield "app_file_part 2"
            yield "app_file_part 3"
        return Response(generate(), headers={"etag": currver})
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    import requests
    
    ver = "1.0"
    url = "http://localhost:5000/download/app"
    
    req = requests.get(url, headers={"If-None-Match": ver})
    
    if req.status_code == 200:
        print "new content of resource", req.content
        new_ver = req.headers["etag"]
    else:
        print "resource did not change since last time"