全部产品
云市场
云游戏

Python HTTP 函数

更新时间:2020-03-25 10:19:57

本文介绍了 Python HTTP 函数,为函数设置 HTTP 触发器后,可以方便处理发来的 HTTP 请求,方便搭建 Web 应用。

函数入口

Python runtime 中函数的签名遵循 WSGI ( Python Web Server Gateway Interface)规范。您可以使用 WSGI 规范对请求进行处理。

HTTP 触发器提供两种函数入口方式,分别是:

  • 第一种方式
  1. # Method 1: User provide the function. FC call the function to process request and send back response.
  2. HELLO_WORLD = b"Hello world!\n"
  3. def handler(environ, start_response):
  4. context = environ['fc.context']
  5. request_uri = environ['fc.request_uri']
  6. for k, v in environ.items():
  7. if k.startswith("HTTP_"):
  8. # process custom request headers
  9. pass
  10. # get request_body
  11. try:
  12. request_body_size = int(environ.get('CONTENT_LENGTH', 0))
  13. except (ValueError):
  14. request_body_size = 0
  15. request_body = environ['wsgi.input'].read(request_body_size)
  16. # get request_method
  17. request_method = environ['REQUEST_METHOD']
  18. # get path info
  19. path_info = environ['PATH_INFO']
  20. # get server_protocol
  21. server_protocol = environ['SERVER_PROTOCOL']
  22. # get content_type
  23. try:
  24. content_type = environ['CONTENT_TYPE']
  25. except (KeyError):
  26. content_type = " "
  27. # get query_string
  28. try:
  29. query_string = environ['QUERY_STRING']
  30. except (KeyError):
  31. query_string = " "
  32. print 'request_body: {}'.format(request_body)
  33. print 'method: {}\n path: {}\n query_string: {}\n server_protocol: {}\n'.format(request_method, path_info, query_string, server_protocol)
  34. # do something here
  35. status = '200 OK'
  36. response_headers = [('Content-type', 'text/plain')]
  37. start_response(status, response_headers)
  38. # return value must be iterable
  39. return [HELLO_WORLD]
  • 第二种方式
  1. # Method 2: User provide the callable class object. FC call the object to process request and send back response.
  2. HELLO_WORLD = b"Hello world!\n"
  3. class AppClass:
  4. """Produce the same output, but using a class
  5. """
  6. def __init__(self, environ, start_response):
  7. self.environ = environ
  8. self.start = start_response
  9. def __iter__(self):
  10. status = '200 OK'
  11. response_headers = [('Content-type', 'text/plain')]
  12. self.start(status, response_headers)
  13. yield HELLO_WORLD
  14. def handler(environ, start_response):
  15. return AppClass(environ, start_response)

入口函数参数

  • environ : environ 参数是一个 Python 字典,里面存放了所有和客户端相关的信息,详情参见 environ 参数。函数计算增加了两个自定义的键,分别是 fc.contextfc.request_uri

    • fc.context : 和事件函数的 context 意义相同。
    • fc.request_uri : string 类型,请求的 uri。

    注意:environ 中的 HTTP_Variables ,里面包含请求头, 例如某个请求头是 'x-Custom-key':'value' , 在 environ 中会表现为 environ['HTTP_X_CUSTOM_KEY']='value', 在这里 WSGI 对请求头中的键做了处理,处理方式为 key = "HTTP_" + k.upper().replace("-","_")

  • start_response : start_response 参数是一个可调用者(callable), 具体介绍请参见 the-start-response-callable 。start_response 参数是函数计算 runtime 提供的,它包含两个必要的位置参数和一个可选参数。为方便说明,可以将它们命名为 statusresponse_headersexc_info, 代码示例如下所示。

    1. # Provided by FC runtime.
    2. # status: a string like '200 OK' or '403 FORBIDDEN'
    3. # return: must be a write(body_data) callable
    4. def start_response(status, response_headers, exc_info=None):
    5. ...
    • status : 一个字符串,表示 HTTP 响应状态。
    • response_headers : 一个列表,包含(header_name, header_value)形式的元组,表示 HTTP 响应头。
    • exc_info (可选) : 出错时,服务端需要返回给浏览器的信息。

当应用对象根据 environ 参数的内容执行完业务逻辑后,就需要把结果返回给服务端。HTTP 响应需要包含响应状态,响应头和响应体,因此在应用对象将 body 作为返回值之返回前,需要先调用 start_response() ,将 status 和 headers 的内容返回给服务端,告知服务端应用对象要开始返回 body 了。

获取请求体

您可以直接使用 WSGI 获取 raw body

示例代码如下所示。

  1. # get request_body
  2. try:
  3. request_body_size = int(environ.get('CONTENT_LENGTH', 0))
  4. except (ValueError):
  5. request_body_size = 0
  6. request_body = environ['wsgi.input'].read(request_body_size)

部署框架

从上面示例中的Method2,您可以看出利用 Flask、Django 等基于 WSGI 协议的前端框架构建的工程可以运行在函数计算的 python runtime 中,下文介绍了部署 Flask框架到函数计算的过程。

  1. from flask import Flask
  2. from flask import request
  3. from flask import make_response
  4. app = Flask(__name__)
  5. @app.route('/', methods=['GET', 'POST'])
  6. def home():
  7. resp = make_response('<h1>Home</h1>', 200)
  8. return resp
  9. @app.route('/signin', methods=['GET'])
  10. def signin_form():
  11. # action url 中的service_name,function_name need replace
  12. html = '''<form action="/2016-08-15/proxy/service_name/func_name/signin" method="post">
  13. <p><input name="username"></p>
  14. <p><input name="password" type="password"></p>
  15. <p><button type="submit">Sign In</button></p>
  16. </form>'''
  17. resp = make_response(html, 200)
  18. return resp
  19. @app.route('/signin', methods=['POST'])
  20. def signin():
  21. if request.form['username'] == 'admin' and request.form['password'] == 'password':
  22. html = '<h3>Hello, admin!</h3>'
  23. else:
  24. html = '<h3>Bad username or password.</h3>'
  25. resp = make_response(html, 200)
  26. return resp
  27. @app.route('/signin2', methods=['GET'])
  28. def signin2():
  29. if request.args.get('username') == 'admin' and request.args.get('password') == 'password':
  30. html = '<h3>Hello2, admin!</h3>'
  31. else:
  32. html = '<h3>Bad username or password.</h3>'
  33. resp = make_response(html, 200)
  34. return resp
  35. def handler(environ, start_response):
  36. # maybe pre do something here
  37. return app(environ, start_response)

详细操作步骤请参见部署基于 python wsgi web 框架的工程到函数计算