Article From:https://www.cnblogs.com/JetpropelledSnake/p/9170400.html

 

WebEssence of frame

We can understand this: all Web applications are essentially a socket server, and the user’s browser is a socket client. In this way, we can implement the Web framework ourselves.

Custom web framework for semi-finished products

Copy code
import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 80))
sk.listen()


while True:
    conn, addr = sk.accept()
    data = conn.recv(8096)
    conn.send(b"OK")
    conn.close()
Copy code

It can be said that Web services are basically extended on the basis of more than ten lines of code. This code is their ancestor.

The user’s browser will send data to the server when it enters the address. What data will the browser send? How do you do it? Who is this? Your website is the rule. Can his Internet play still according to his rule?

Therefore, there must be a unified rule, so that when you send messages and receive messages, there is a format basis and can not be written at random.

This rule is the HTTP protocol. After the browser sends the request information or the server responds to the response information, it must follow this rule.

HTTPThe 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 out what messages we receive on the server side.

Copy code
import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 80))
sk.listen()


while True:
    conn, addr = sk.accept()
    data = conn.recv(8096)
    print(data)  # Print out the message sent by the browserConn.send (b"OK")
    conn.close()
Copy code

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 let’s take a look at the response message that browsers receive when we visit the blog website.

The response related information can be seen in the network tab of the browser debug window.

clickview sourceThe following is shown as follows:

We found that the message needed to send and receive must be in a certain format, so we need to know about the HTTP protocol.

HTTPIntroduction of the agreement

HTTPProtocol format requirements for sending and receiving messages

Each HTTP request and response follow the same format. A HTTP contains two parts of Header and Body, where Body is optional. There is one in the Header of the HTTP responseContent-TypeThe content format of the response. astext/htmlRepresent the HTML web page.

HTTP GETThe format of the request:

 

HTTPThe format of the response:

 

 A maiden version of the custom web framework

After the supplementary learning above, we know that to make the web server end of our own write up, we must let our Web server add the response state according to the rules of the HTTP protocol when answering the message to the client, so that we have implemented a positive one.The Web framework.

Copy code
import socket

sock = 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)
    # The message to the reply is added to the response state line
    conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
    conn.send(b"OK")
    conn.close()
Copy code

We have briefly demonstrated the essence of the web framework through more than ten lines of code.

Next let’s continue to improve our custom web framework.

 

Return different contents according to different paths

Is this the end of this? How can our Web service return different content according to the URL of the user request?

A trivial matter, we can get the path of requesting URL from the relevant data, and then take the path to make a judgement.

Copy code
"""
Return different contents according to different paths in URL"""

import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080))  # Bind IP and port
sk.listen()  # Monitor


while 1:
    # Wait for a connection
    conn, add = sk.accept()
    data = conn.recv(8096)  # Receiving the message sent by the client
    # Take the path from data
    data = str(data, encoding="utf8")  # Converts the received byte type data to a string.
    # \r\n segmentation
    data1 = data.split("\r\n")[0]
    url = data1.split()[1]  # urlIs the access path separated from the messages sent by the browser.
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # Because the HTTP protocol should be followed, the reply message should also be added to the status line.
    # Return different contents according to different paths
    if url == "/index/":
        response = b"index"
    elif url == "/home/":
        response = b"home"
    else:
        response = b"404 not found!"

    conn.send(response)
    conn.close()
Copy code

 

Returning different contents according to different paths — function version

The above code solves the need for different URL paths to return different contents.

But the question is coming again. If there are many, many ways to decide what to do? Do you want to write if judgment by one by one? Of course not, we have a smarter way.

Copy code
"""
According to different paths in URL to return different contents -- function version"""

import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080))  # Bind IP and port
sk.listen()  # Monitor


# It will be returned to different parts of the content to be encapsulated into functions
def index(url):
    s = "This is a {} page!".format(url)
    return bytes(s, encoding="utf8")


def home(url):
    s = "This is a {} page!".format(url)
    return bytes(s, encoding="utf8")


while 1:
    # Wait for a connection
    conn, add = sk.accept()
    data = conn.recv(8096)  # Receiving the message sent by the client
    # Take the path from data
    data = str(data, encoding="utf8")  # Converts the received byte type data to a string.
    # \r\n segmentation
    data1 = data.split("\r\n")[0]
    url = data1.split()[1]  # urlIs the access path separated from the messages sent by the browser.
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # Because the HTTP protocol should be followed, the reply message should also be added to the status line.
    # Different contents are returned according to different paths. 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()
Copy code

 

Returning different contents according to different paths — function advanced edition

It seems that the above code should be judged by if one by one. What should we do? We still have a way! As long as the mind does not slide, the method is always more than the problem.

Copy code
"""
According to different paths in URL, return different contents -- advanced version of function."""

import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080))  # Bind IP and port
sk.listen()  # Monitor


# It will be returned to different parts of the content to be encapsulated into functions
def index(url):
    s = "This is a {} page!".format(url)
    return bytes(s, encoding="utf8")


def home(url):
    s = "This is a {} page!".format(url)
    return bytes(s, encoding="utf8")


# Define the correspondence between a URL and the function to be executed.
list1 = [
    ("/index/", index),
    ("/home/", home),
]

while 1:
    # Wait for a connection
    conn, add = sk.accept()
    data = conn.recv(8096)  # Receiving the message sent by the client
    # Take the path from data
    data = str(data, encoding="utf8")  # Converts the received byte type data to a string.
    # \r\n segmentation
    data1 = data.split("\r\n")[0]
    url = data1.split()[1]  # urlIs the access path separated from the messages sent by the browser.
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # Because the HTTP protocol should be followed, the reply message should also be added to the status line.
    # Return different contents according to different paths
    func = None  # Defines a variable that holds the name of the function 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!"

    # Return to a specific response message
    conn.send(response)
    conn.close()
Copy code

 

Return to a specific HTML file

It perfectly solves the problem that different URL returns different contents. But I don’t want to just return a few strings. I want to return the complete HTML content to the browser. What should I do?

No problem. Whatever it is, it is converted to byte data. We can open the HTML file, read out its internal binary data, and then send it to the browser.

Copy code
"""
According to different paths in URL, return different contents -- advanced version of function.Return to a separate HTML page"""

import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080))  # Bind IP and port
sk.listen()  # Monitor


# It will be returned to different parts of the content to be encapsulated into functions
def index(url):
    # Read the content of the index.html page
    with open("index.html", "r", encoding="utf8") as f:
        s = f.read()
    # Return 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")


# Define the correspondence between a URL and the function to be executed.
list1 = [
    ("/index/", index),
    ("/home/", home),
]

while 1:
    # Wait for a connection
    conn, add = sk.accept()
    data = conn.recv(8096)  # Receiving the message sent by the client
    # Take the path from data
    data = str(data, encoding="utf8")  # Converts the received byte type data to a string.
    # \r\n segmentation
    data1 = data.split("\r\n")[0]
    url = data1.split()[1]  # urlIs the access path separated from the messages sent by the browser.
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # Because the HTTP protocol should be followed, the reply message should also be added to the status line.
    # Return different contents according to different paths
    func = None  # Defines a variable that holds the name of the function 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!"

    # Return to a specific response message
    conn.send(response)
    conn.close()
Copy code

 

Make web pages dynamic

This web page can be displayed, but it is static. The content of the page will not change. What I want is a dynamic website.

No problem, I have a way to solve it. I chose to use string substitution to achieve this requirement. (using timestamps to simulate dynamic data)

Copy code
"""
According to different paths in URL, return different contents -- advanced version of function.Return to the HTML pageMake web pages dynamic"""

import socket
import time

sk = socket.socket()
sk.bind(("127.0.0.1", 8080))  # Bind IP and port
sk.listen()  # Monitor


# It will be returned to different parts of the content to be encapsulated into functions
def index(url):
    with open("index.html", "r", encoding="utf8") as f:
        s = f.read()
        now = str(time.time())
        s = s.replace("@@oo@@", now)  # Define special symbols in web pages and replace special symbols in advance with dynamic 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")


# Define the correspondence between a URL and the function to be executed.
list1 = [
    ("/index/", index),
    ("/home/", home),
]

while 1:
    # Wait for a connection
    conn, add = sk.accept()
    data = conn.recv(8096)  # Receiving the message sent by the client
    # Take the path from data
    data = str(data, encoding="utf8")  # Converts the received byte type data to a string.
    # \r\n segmentation
    data1 = data.split("\r\n")[0]
    url = data1.split()[1]  # urlIs the access path separated from the messages sent by the browser.
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # Because the HTTP protocol should be followed, the reply message should also be added to the status line.
    # Return different contents according to different paths
    func = None  # Defines a variable that holds the name of the function 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!"

    # Return to a specific response message
    conn.send(response)
    conn.close()
Copy code

 

All right, at this pause…

 

Server programs and Applications

For the real development of Python web program, it is usually divided into two parts: server program and application program.

The server program is responsible for encapsulating the socket server and sorting out all kinds of data requested when it comes.

The application is responsible for the specific logical processing. In order to facilitate the development of application programs, there are many Web frameworks, such as Django, Flask, web.py and so on. Different frameworks have different ways of development, but in any case, applications developed must be servers.Program coordination, to provide services for users.

 

In this way, the server program needs to provide different support for different frameworks. This chaotic situation is not good for servers or frameworks. For the server, different frameworks are needed to support the framework, and only the servers that support it can be used by the developed applications.

At this time, standardization becomes particularly important. We can set up a standard, so long as the server program supports this standard and the framework supports this standard, then they can cooperate with it. Once the standard is determined, both sides are implemented. In this way, the server can support more standard supporting frameworks.To use more servers that support the standard.

WSGI(Web Server Gateway Interface)It is a specification that defines the interface format between the web application written in Python and the web server program to decouple the web application from the web server program.

The common WSGI servers are uwsgi and Gunicorn. The independent WSGI server provided by the Python standard library is called wsgiref.DjangoThe development environment uses this module to do the server.

 

Continue from here…

 

wsgiref

We use the wsgiref module to replace the socket server part of our own web framework:

Copy code
"""
According to different paths in URL, return different contents -- advanced version of function.Return to the HTML pageMake web pages dynamicWsgiref module version"""

import time
from wsgiref.simple_server import make_server


# It will be returned to different parts of the content to be encapsulated into functions
def index(url):
    with open("index.html", "r", encoding="utf8") as f:
        s = f.read()
        now = str(time.time())
        s = s.replace("@@oo@@", 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")


# Define the correspondence between a URL and the function to be executed.
list1 = [
    ("/index/", index),
    ("/home/", home),
]


def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # Setting the state code and header information of the HTTP response
    url = environ['PATH_INFO']  # Take 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 at 8090.")
    httpd.serve_forever()
Copy code

 

jinja2

The above code implements a simple dynamic. I can completely query the data from the database, then replace the corresponding content in my HTML, and then send it to the browser to complete the rendering. This process is equivalent to the HTML template rendering data. In essence, it is the use of some special features in HTML contentDifferent symbols are used to replace the data to be displayed. The special symbol I use here is what I define. Actually, there is a ready tool for template rendering.jinja2

Download jinja2:

pip install jinja2

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Title</title>
</head>
<body>
    <h1>Name: {{name}}</h1>
    <h1>Hobbies:</h1>
    <ul>
        {% for hobby in hobby_list %}
        <li>{{hobby}}</li>
        {% endfor %}
    </ul>
</body>
</html>

index2.htmlfile

Use jinja2 to render the index2.html file:

Copy code
from wsgiref.simple_server import make_server
from jinja2 import Template


def index():
    with open("index2.html", "r") as f:
        data = f.read()
    template = Template(data)  # Generate template files
    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, ]


# Defining the correspondence of a URL and a function
URL_LIST = [
    ("/index/", index),
    ("/home/", home),
]


def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # Setting the state code and header information of the HTTP response
    url = environ['PATH_INFO']  # Take 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 to find the function that URL should perform.
            break
    if func:  # If you can find a function to execute
        return func()  # Return the execution result of a function
    else:
        return [bytes("404No page", encoding="utf8"), ]


if __name__ == '__main__':
    httpd = make_server('', 8000, run_server)
    print("Serving HTTP on port 8000...")
    httpd.serve_forever()
Copy code

Now the data is written by ourselves. Can we query the data from the database to fill the pages?

Using pymysql to connect to the database:

Copy code
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()
Copy code

Create a test user table:

CREATE TABLE user(
  id int auto_increment PRIMARY KEY,
  name CHAR(10) NOT NULL,
  hobby CHAR(20) NOT NULL
)engine=innodb DEFAULT charset=UTF8;

The principle of a template is string substitution. As long as we follow the syntax rules of the jinja2 in the HTML page, the inside will be replaced in accordance with the specified syntax to achieve the dynamic return.

 

Django

DjangoOfficial website downloading 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

Directory introduction:

Copy code
mysite/
├── manage.py  # Management documentsMySite__init__.pySettings.py configurationUrls.py routing--> URLThe corresponding relation with the functionWsgi.py runserver command uses wsgiref module to make simple web server.
Copy code

Run the Django project:

python manage.py runserver 127.0.0.1:8000

Template file configuration:

Copy code
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, "template")],  # templateFolder 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',
            ],
        },
    },
]
Copy code

Static file configuration:

STATIC_URL = '/static/'  # HTMLStatic folder prefix used in
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),  # Static file storage location
]

Can’t you see it? There is a picture of the truth:

At the beginning of learning, you can temporarily disable the CSRF Middleware in the configuration file to facilitate the form submit test.

Copy code
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',
]
Copy code

DjangoBasic three pieces of basic set:

from django.shortcuts import HttpResponse, render, redirect

HttpResponse

A string parameter inside the incoming is returned to the browser.

For example:

def index(request):
    # Business logic code
    return HttpResponse("OK")

render

In addition to the request parameter, a template file to be rendered and a dictionary parameter for preserving specific data are also accepted.

Fill the data into the template file, and finally return the result to the browser. (similar to the jinja2 we use above)

For example:

def index(request):
    # Business logic code
    return render(request, "index.html", {"name": "alex", "hobby": ["Perm", "Bubble bar"]})

redirect

A URL parameter is accepted to indicate the jump to the specified URL.

For example:

def index(request):
    # Business logic code
    return redirect("/home/")

What is the redirection?

 

Practice:

DjangoVersion login

Start Django report wrong:

Django Start the times wrong UnicodeEncodeError…

It’s usually because of this mistake.Computer nameFor Chinese, change the name of the computer to restart the computer.

 

Leave a Reply

Your email address will not be published. Required fields are marked *