This is the first day of my participation in the August Text Challenge.More challenges in August
Introduction:
Having written briefly about the WSGI protocol while summarizing the details of the Python project deployment, this article will discuss WSGI design ideas and how to write a WSGI application.
What is the WSGI protocol? What does WSGI do?
WSGI is not a Python module, framework, or API. In essence, WSGI is an interface specification for communication between Web servers and Web applications. It can also be understood as providing a relatively simple but comprehensive interface to support interaction between Web servers and Web applications.
WSGI
The WSGI protocol is divided into two parts: Server and Application:
- Server: Receives a request from the client, forwards the request to the application for processing, and then sends the response returned by the application to the client.
- Application: Receives a request from the WSGI server, processes the request, and returns the processed response to the server;
APPlication
The Application must be a callable object (a function, or a class that implements a __call__ method) that takes two arguments, environ (WSGI’s environment information) and start_Response (the function that sends the HTTP response request), Finally, the application needs to return an iterable containing one or more ByteStrings.
def make_app(environ, start_response) :
headers = [("Content-type"."text/html")]
status = "200 OK"
start_response(status, headers)
return [b'<h1>Hello, World</h1>']
Copy the code
Remark:
- The environ variable is a dictionary containing environment information;
- Start_response is also a callable object that sets the status code and response header for the response request;
- The name of the parameter does not have to be environ, start_Response, which requires the server to pass in positional parameters instead of keyword parameters.
Server (Server)
The Server is responsible for parsing HTTP requests and sending them to the application for processing. Python has a built-in WSGIref module that directly generates a WSGI server (for development testing only).
from wsgiref.simple_server import make_server
with make_server("".8000, make_app) as httpd:
print("Server on port 8000......")
httpd.serve_forever()
Copy the code
Remark:
- Create a WSGI server that listens on port 8000 and specifies a make_app function to handle requests;
- Serve_forever polls wait requests until shutdown() requests;
Simulate Get/Post request parsing
Following the simple simulation of a Get request to the WSGI server and then an HTML return, the simulation parses the Get request parameters and the JSON parameters of the Post
GET request parsing:
# -*- coding: utf-8 -*-
from wsgiref.simple_server import make_server
from urllib.parse import parse_qs
def application(environ, start_response) :
Check whether the request type is GET
headers = [("Content-Type"."text/html")]
method = environ['REQUEST_METHOD']
ifmethod ! ="GET":
status = "405 Method Not Allowed"
start_response(status, headers)
html = b"<h1>405 Method Not Allowed</h1>"
return [html]
Check whether the request path is valid
path = environ['PATH_INFO']
if path not in ["/hello"."/"]:
status = "404 Not Found"
start_response(status, headers)
html = b"<h1>404</h1>"
return [html]
status = "200 OK"
start_response(status, headers)
d = parse_qs(environ['QUERY_STRING'])
name = d.get("name"[]),if name:
name = name[0]
html = b"<h1> Hello, %s , Welcome to my website.</h1>" % name.encode("utf8")
else:
html = b"<h1> Hello, World.</h1>"
return [html]
with make_server("".8000, application) as httpd:
print("Server on 8000 Port......")
httpd.serve_forever()
Copy the code
Remark:
- Check REQUEST_METHOD in the environ dictionary to determine the request type. If the request type is not GET, the status page 405 is displayed.
- Check PATH_INFO in environ dictionary to check whether the requested path is valid. If the path is not in the preset list, the 404 status page is returned.
- Parse_qs parses QUER_STRING in the environ dictionary to check whether the request parameter name can be obtained.
Post request parsing:
# -*- coding: utf-8 -*-
from wsgiref.simple_server import make_server
from cgi import parse_qs
from faker import Faker
import json
def application(environ, start_response) :
f = Faker()
wsgi_input = environ["wsgi.input"]
try:
content_length = int(environ.get("CONTENT_LENGTH".0))
except (ValueError):
content_length = 0
request_body = wsgi_input.read(content_length)
request_json = json.loads(request_body.decode("utf8"))
status = "200 OK"
headers = [("Content-Type"."application/json")]
start_response(status, headers)
name = request_json.get("name"."")
user = {"name": name, "address": f.address(), "email": f.email() }
text = json.dumps(user)
return [text.encode("utf8")]
with make_server("".8000, application) as httpd:
print("Server on port 8000....")
httpd.serve_forever()
Copy the code
Remark:
- Json data is submitted through the wsgi.input file in the Environ dictionary. CONTENT_LENGTH is used to obtain the submitted JSON data.
- The request data is read, decoded into a dictionary through JSON, and then the corresponding key value is obtained. Here, faker forges data to simulate the process of querying the database through name.
- Finally, the obtained information is formed into a dictionary, which is encoded into a string through JSON and returned to the front end.
- Here there is no verification of the request mode, request path, request data is legitimate, pure test POST request process;
conclusion
As you can see from the example above, a Web application writes WSGI handlers to respond to each HTTP request, but later as you process more urls, the handler code gets bigger and harder to maintain. So by abstracting the routing and request method determination code from the WSGI interface and leaving it to the Web framework, developers just need to focus on the business logic code, such as Flask, a popular Python Web framework, Django.
Flask framework
Here’s a quick look at the Flask framework for writing a Web App
# -*- coding: utf-8 -*-
from flask import Flask, request, jsonify
from faker import Faker
app = Flask(__name__)
@app.route("/", methods=["GET"])
def index() :
name = request.args.get("name"."")
if name:
return f'<h1>Hello {name}, Welcom to my Website.'
else:
return '<h1>Hello,World</h1>'
@app.route("/hello", methods=["POST"])
def hello() :
data = request.json
name = data.get("name"."")
if not name:
response = {
"ret_code": 500."ret_info": "Name cannot be empty."
}
return jsonify(response)
else:
f = Faker()
response = {
"name": name,
"email": f.email(),
"address": f.address()
}
return jsonify(response)
if __name__ == "__main__":
app.run("0.0.0.0", port=5000, debug=True)
Copy the code
Remark:
- There are two urls that correspond to the WSGI interface above to resolve GET/POST requests.
- The Flask framework uses decorators to associate urls with functions, which is simple and convenient. In this way, you only need to focus on the code logic in the functions.
- Flask implementation of WSGI interface source code can be analyzed later, here press not table;
reference
WSGI specification (PEP3333)
www.python.org/dev/peps/pe…
Wsgiref module
Docs.python.org/zh-cn/3/lib…
WSGI tutorial:
Wsgi.tutorial.codepoint.net/application…