Web Framework Nature
We can understand this: all Web applications are essentially a socket server, and the user's browser is a socket client. This allows us to implement the web framework ourselves.
Semi-finished custom web framework
Import Socketsk = Socket.socket () sk.bind (("127.0.0.1", "a") Sk.listen () while True: conn, addr = sk.accept () data = Conn.recv (8096) conn.send (b "OK") conn.close ()
It can be said that Web services are essentially expanded on the basis of these more than 10 lines of code. This piece of code is their ancestor.
User's browser input URL, will send data to the server, what data will the browser send? How to send? Who's going to fix this? You this website is this stipulation, his that website according to his that stipulation, this internet still can play?
Therefore, there must be a unified rule, let everyone send messages, receive messages when there is a format basis, can not be written casually.
This rule is the HTTP protocol, after the browser sends the request information, the server replies to the response information, should follow this rule.
The HTTP protocol mainly specifies the communication format between the client and the server, how does the HTTP protocol specify the message format?
Let's first print what the message we received on the server is.
Import Socketsk = Socket.socket () sk.bind (("127.0.0.1", "a") Sk.listen () while True: conn, addr = sk.accept () data = Conn.recv (8096) print (data) # Prints the message sent by the browser conn.send (b "OK") conn.close ()
Output:
B ' Get/http/1.1\r\nhost:127.0.0.1:8080\r\nconnection:keep-alive\r\nupgrade-insecure-requests:1\r\nuser-agent: mozilla/5.0 (Windows NT 10.0; Win64; x64) applewebkit/537.36 (khtml, like Gecko) chrome/64.0.3282.186 safari/537.36\r\naccept:text/html,application/xhtml +xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\ndnt:1\r\naccept-encoding:gzip, deflate, br\r\ naccept-language:zh-cn,zh;q=0.9\r\ncookie:csrftoken= Rkbxh1d3m97iz03rpbojx1br6mhhudhyx5pszuxxg3boewh1lxfpgogwn93zh3zv\r\n\r\n '
Then we'll look at what the browser's response message is when we visit the blog site.
The response information can be found in the Network tab of the Browser debug window.
Click View Source to display the following:
We find that the messages sent and received need to be in a certain format, and here we need to understand the HTTP protocol.
HTTP protocol Introduction
HTTP protocol format requirements for sending and receiving messages
Each HTTP request and response follows the same format, and an HTTP contains both the header and body, where the body is optional. The header of the HTTP response has a Content-Type
content format that indicates the response. such as an text/html
HTML page.
Format of the HTTP GET request:
Format of HTTP response:
Virgo custom Web Framework
After the above supplementary study, we know that in order to let us write the Web server properly, we have to let our Web server respond to the message to the client in accordance with the rules of the HTTP protocol response status line, so that we implement a serious web framework.
Import Socketsock = Socket.socket (socket.af_inet, socket. Sock_stream) Sock.bind ((' 127.0.0.1 ', 8000)) Sock.listen () while True: conn, addr = sock.accept () data = CONN.RECV (8096) # reply message plus response status line Conn.send (b "http/1.1 ok\r\n\r\n") conn.send (b "OK") Conn.close ()
We simply demonstrated the nature of the web framework through more than 10 lines of code.
Next, let's continue to refine our custom web framework!
Return different content based on different paths
Is this the end of it? How do we make our Web service return different content depending on the URL the user requests?
We can get the path of the request URL from the request related data, then take the path to make a judgment ...
"" "returns different contents according to different paths in the URL" "" Import Socketsk = Socket.socket () sk.bind (("127.0.0.1", 8080) # bind IP and Port sk.listen () # Listening while 1: # Waiting for connection conn, add = sk.accept () data = Conn.recv (8096) # receive the message from the client # Fetch the path from the data data = str (data, encoding= "UTF8") # Converts the received byte type to a string # press \ r \ n Split data1 = Data.split ("\ r \ n") [0] URL = Data1.split () [1] # URL is the access path separated from the message we sent from the browser conn.send (b ' http/1.1 ok\r\n\r\n ') # because to follow the HTTP protocol, So the reply message also adds the status line # returns different content according to different path if url = = "/index/": response = B "index" elif url = = "/home/": Response = B "Home" else: response = B "404 Not found!" Conn.send (response) Conn.close ()
Return different content based on different paths-function edition
The above code addresses the need for different URL paths to return different content.
But the problem comes again, if there are many ways to judge how to do? Do you want to write the If judgment? Of course not, we have a smarter way.
"" returns different content based on different paths in the URL-function version "" "Import Socketsk = Socket.socket () sk.bind ((" 127.0.0.1 ", 8080) # bind IP and Port Sk.listen () # Listen # will return a different content part encapsulated into a function def index (URL): s = "This is the {} page!" ". Format (URL) return bytes (s, encoding=" UTF8 ") def Home (URL): s =" This is the {} page! " ". Format (URL) return bytes (s, encoding=" UTF8 ") while 1: # waits for connection conn, add = sk.accept () data = CONN.RECV ( 8096) # receives a message from the client # from data to path data = str (data, encoding= "UTF8") # Convert the received byte type to a string # press \r\ N split data1 = Data.split ("\ r \ n") [0] url = data1.split () [1] # URL is the access path separated from the message we sent from the browser conn.send ( B ' http/1.1 ok\r\n\r\n ') # because we want to follow the HTTP protocol, the reply message also adds a status line # Depending on the path to return different content, response is the specific response body if url = = "/ index/": response = index (URL) elif url = ="/home/": response = home (URL) else: response = B" 404 Not found! " Conn.send (response) Conn.close ()
Return different content based on different paths-function advanced version
It looks like the code above is going to write the if judgment, what should I do? We still have a way! (As long as the thought does not slide, the method is always more than the problem!) )
"" returns different content based on different paths in the URL-function Advanced "" "Import Socketsk = Socket.socket () sk.bind ((" 127.0.0.1 ", 8080) # bind IP and Port Sk.listen () # Listening # will return a different content part encapsulated into a function def index (URL): s = "This is the {} page!" ". Format (URL) return bytes (s, encoding=" UTF8 ") def Home (URL): s =" This is the {} page! " ". Format (URL) return bytes (s, encoding=" UTF8 ") # defines the correspondence between a URL and the actual function to be performed list1 = [("/index/", Index), ("/home/", Hom e),]while 1: # Wait for connection conn, add = sk.accept () data = Conn.recv (8096) # receive the message from the client # from data fetch to path data = str (DA TA, encoding= "UTF8") # Convert data from received byte type to string # Press \ r \ n Split data1 = Data.split ("\ r \ n") [0] url = data1.split () [1] # URL is me We separated the access path from the message sent from the browser conn.send (b ' http/1.1-ok\r\n\r\n ') # because we want to follow the HTTP protocol, so the reply message will also add the status line # returns different contents according to different paths Func = No NE # defines a variable that holds the function name to be executed for I in list1:if i[0] = = Url:func = i[1] Break if func: Response = func (URL) else:response = B "404 Not Found!" # Returns the specific response message conn.send (response) Conn.close ()
Returns the specific HTML file
Perfectly solves the problem of different URLs returning different content. But I don't want to just return a few strings, I want to return the full HTML content to the browser, what should I do?
No problem, no matter what the content, the final is converted into byte data sent out. We can open the HTML file, read the binary data inside it, and then send it to the browser.
"" "returns different content based on a different path in the URL-function advanced returns a separate HTML page" "" Import Socketsk = Socket.socket () sk.bind (("127.0.0.1", 8080)) # Bind IP and Port sk.listen () # Listen # will return different content parts encapsulated into function def index (URL): # Read the contents of the Index.html page with open ("Index.html", "R", encoding= " UTF8 ") as F:s = F.read () # Returns the byte data return bytes (s, encoding=" UTF8 ") def Home (URL): With open (" home.html "," R ", encoding=" UTF8 ") as F:s = F.read () return bytes (s, encoding=" UTF8 ") # defines the correspondence between a URL and the actual function to be performed list1 = [("/I ndex/", index), ("/home/", Home),]while 1: # Wait for connection conn, add = sk.accept () data = Conn.recv (8096) # receive messages from clients # take path from data = str (data, encoding= "UTF8") # Convert data from received byte type to string # Press \ r \ n Split data1 = Data.split ("\ r \ n") [0] url = data1.split () [1] # URL is the message we sent from the browser to separate the access path Conn.send (b ' http/1.1 ok\r\n\r\n ') # because to follow the HTTP protocol, so the reply message also added state Line # returns different content depending on the path func = None # Defines a variable that holds the function name to be executed for I in list1:if i[0] = = Url:func = i[1] Break if Func:resPonse = func (URL) else:response = B "404 Not Found!" # Returns the specific response message conn.send (response) Conn.close ()
Let the Web page live up
This page can be displayed, but it's static. The content of the page will not change, I want to be a dynamic site.
No problem, I have a way to solve it. I chose to use string substitution to implement this requirement. (The time stamp is used here to simulate dynamic Data)
"" "returns different content based on a different path in the URL-the function advanced returns the HTML page to make the Web page dynamic" "" Import socketimport Timesk = Socket.socket () sk.bind (("127.0.0.1", 8080) # bind IP and Port sk.listen () # Listen # will return different content parts encapsulated into function def index (URL): With open ("Index.html", "R", encoding= "UTF8") as F: s = F.read () now = str (Time.time ()) s = S.replace ("@@[email protected]@", today) # define special symbols in the Web page, use the dynamic Bytes (s, encoding= "UTF8") def Home (URL): With open ("home.html", "R", encoding= "UTF8") as F: s = f.read () return bytes (s, encoding= "UTF8") # defines the correspondence between a URL and the actual function to be performed list1 = [("/index/", Index), ("/home/ ", Home),]while 1: # Wait for connection conn, add = sk.accept () data = Conn.recv (8096) # receive the message from the client # Fetch the path from data to data = STR (data, encoding= "UTF8") # converts data from received byte type to string # Press \ r \ n Split data1 = Data.split ("\ r \ n") [0] url = data1.split () [1] # The URL is the access path separated from the message that we sent from the browser conn.send (b ' http/1.1-ok\r\n\r\n ') # because we want to follow the HTTP protocol, so the reply message also has a status line # returns different content based on different paths c = None # Defines a variable that holds the name of the function that will be executed FOr I in list1:if i[0] = = Url:func = i[1] break If Func:response = func (URL) E Lse:response = B "404 Not Found!" # Returns the specific response message conn.send (response) Conn.close ()
Well, in this pause ...
Server programs and Applications
For a real-world Python Web application, it is generally divided into two parts: server programs and Applications.
The server program is responsible for encapsulating the socket server and collating the various data requested when the request arrives.
The application is responsible for the specific logical processing. In order to facilitate the development of the application, there are many web frameworks, such as Django, Flask, web.py and so on. Different frameworks have different ways of developing them, but in any case, the applications you develop will have to work with the server program to provide services to the user.
In this way, the server program needs to provide different support for different frameworks. This chaotic situation is bad for both the server and the framework. For the server, you need to support a variety of frameworks, for the framework, only the servers that support it can be used by the development of the application.
Standardization becomes particularly important at this time. We can set up a standard that is supported by the framework as long as the server program supports this standard, so they can work with it. Once the criteria are determined, both parties are implemented. In this way, the server can support more frameworks that support standards, and the framework can use more servers that support standards.
WSGI (Web server Gateway Interface) is a specification that defines the interface format between a Web application and a Web server program written in Python, enabling decoupling between Web applications and Web server programs.
The common WSGI server has Uwsgi, Gunicorn. The standalone WSGI server provided by the Python standard library is called the Wsgiref,django development environment with this module to do the server.
Continue from here ...
Wsgiref
We use the Wsgiref module to replace the socket Server section of our own web framework:
"" "returns different content based on a different path in the URL-function advanced returns an HTML page to make the page dynamic Wsgiref module" "" Import timefrom wsgiref.simple_server import make_server# will return different content parts encapsulated into function def index (URL): With open ("Index.html", "R", encoding= "UTF8") as F:s = F.read () now = str (Time.time ()) s = S.replace ("@@[email protected]@", Now) return bytes (s, encoding= "UTF8") def Home (URL): With open ("home.html", "R", encoding= "UTF8") as F:s = F.read () return bytes (s, encoding= "UTF8") # defines a URL and actually executes the function Correspondence List1 = [("/index/", Index), ("/home/", Home),]def run_server (environ, start_response): Start_response (' 2 XX OK ', [(' Content-type ', ' Text/html;charset=utf8 '),] # Set the HTTP response status code and header information url = environ[' path_info ' # Fetch the URL of the user input Func = None for i in list1:if i[0] = = Url:func = i[1] break if func:response = Func (URL) else:response = B "404 Not Found!" return [Response,]if __name__ = = ' __main__ ': httpd = make_server (' 127.0.0.1 ', 8090, run_server) Print ("I'm waiting for you in 8090 ...") Httpd.serve_forever ()
Jinja2
The above code implements a simple dynamic, I can completely query the data from the database, and then to replace the corresponding content in my HTML, and then sent to the browser to complete the rendering. This process is equivalent to rendering the data in HTML templates. Essentially, the HTML content uses some special symbols to replace the data to be presented. The special symbols I use here are defined by me, in fact template rendering has a ready-made tool:jinja2
Download JINJA2:
Pip Install JINJA2
<! DOCTYPE html>
Render index2.html files using JINJA2:
From wsgiref.simple_server import make_serverfrom jinja2 import templatedef Index (): With open ("index2.html", "R") as F : data = f.read () template = Template (data) # Generate templates file ret = Template.render ({"Name": "Alex", "hobby_list": [" Perm "," Bubble Bar "]}) # Fill the data into the template return [bytes (ret, encoding=" UTF8 "),]def Home (): With open (" home.html "," RB ") as F: data = F.read () return [data,]# defines a URL and function correspondence url_list = [("/index/", Index), ("/home/", Home),]def Run_ser Ver (environ, start_response): Start_response (' OK ', [(' Content-type ', ' Text/html;charset=utf8 '),] # Set the HTTP response status code and Header information url = environ[' path_info ' # takes the URL of the user input func = None # The function to be executed for I in url_list:if i[0] = = URL: func = i[1] # Go to the previously defined URL list and find the function that the URL should execute if func: # If you can find the function to execute return func () # returns the function Line results Else:return [bytes ("404 Without This Page", encoding= "UTF8"),]if __name__ = = ' __main__ ': httpd = Make_server (', 800 0, Run_server) print ("Serving HTTP on port 8000 ...") Httpd.serve_forever ()
Now the data is written by ourselves, can we query the data from the database to populate the page?
To connect to a database using Pymysql:
conn = Pymysql.connect (host= "127.0.0.1", port=3306, user= "root", passwd= "xxx", db= "xxx", charset= "UTF8") cursor = Conn.cursor (cursor=pymysql.cursors.dictcursor) cursor.execute ("Select name, age, department_id from userinfo") user_ List = Cursor.fetchall () cursor.close () Conn.close ()
Create a user table for the test:
CREATE TABLE User ( ID int auto_increment PRIMARY KEY, name Char (TEN) NOT NULL, hobby char (a) NOT null) engine=i Nnodb DEFAULT Charset=utf8;
The principle of the template is string substitution, as long as we follow the JINJA2 syntax in the HTML page, the interior will be replaced by the specified syntax to achieve dynamic return content.
DjangoDjango website download page
Install (Install the latest LTS version):PIP3 Install django==1.11.9
Create a Django project:The following command creates a Django project named "MySite":
Django-admin Startproject MySite
Catalogue Description:mysite/├──manage.py # Manage files └──mysite # project directory ├──__init__.py ├──settings.py # configuration ├──urls.py< c7/># route--url and function correspondence └──wsgi.py # runserver command makes simple Web server using WSGIREF module
Run the Django Project:Python manage.py runserver 127.0.0.1:8000
Template file Configuration:TEMPLATES = [ { ' backend ': ' django.template.backends.django.DjangoTemplates ', ' DIRS ': [Os.path.join ( Base_dir, "template")], # Template folder location ' app_dirs ': True, ' OPTIONS ': { ' context_processors ': [ ' Django.template.context_processors.debug ', ' django.template.context_processors.request ', ' Django.contrib.auth.context_processors.auth ', ' django.contrib.messages.context_processors.messages ', ], }, },]
Static file configuration:Static_url = '/static/' # The static folder prefix used in HTML staticfiles_dirs = [ os.path.join (Base_dir, "static"), # Static file storage location]
Can't you see? There is a picture of the truth:
When you start learning, you can temporarily disable the CSRF middleware in the configuration file to facilitate form submission testing.
middleware = [ ' Django.middleware.security.SecurityMiddleware ', ' Django.contrib.sessions.middleware.SessionMiddleware ', ' Django.middleware.common.CommonMiddleware ', # ' Django.middleware.csrf.CsrfViewMiddleware ', ' Django.contrib.auth.middleware.AuthenticationMiddleware ', ' django.contrib.messages.middleware.MessageMiddleware ', ' Django.middleware.clickjacking.XFrameOptionsMiddleware ',]
Django Essentials three-piece set:From django.shortcuts import HttpResponse, Render, redirect
HttpResponseInternally, a string parameter is passed back to the browser.
For example:
def index (Request): # Business logic Code return HttpResponse ("OK")
RenderIn addition to the request parameter, it accepts a template file to be rendered and a dictionary parameter that holds the specific data.
Populate the template file with the data, and return the results to the browser. (Similar to the jinja2 we used above)
For example:
def index (Request): # Business logic Code return render (Request, "index.html", {"name": "Alex", "hobby": ["Perm", "Bubble Bar"]})
redirectAccepts a URL parameter that represents a jump to the specified URL.
For example:
def index (Request): # Business logic Code return Redirect ("/home/")
How is redirection going?After-school exercises:Django Version Login
Start Django Error:Django startup times Wrong "Unicodeencodeerror ..."
This error is usually reported because the computer name is Chinese, and the computer name changed to English will restart the computer.
Django Startup Error "Syntaxerror:generator expression must be parenthesized"
This error is probably due to the use of Python3.7.0, and currently (2018-06-12) Python3.7.0 and Django have a bit of compatibility issues, to return to the Python3.6 environment.
Web framework Nature and the first Django instance