User Login Principles
User login and registration functions have almost become standard in Web applications. Therefore, it is necessary to add a user management module to Todo List to learn how users log in.
The HTTP protocol is stateless, which means that each complete HTTP request-response process is relatively independent, and the Web server cannot tell whether two consecutive requests were sent by the same user (client). In order for the server to remember the user, there is a technology called cookies. I drew a diagram to illustrate how Cookie works:
First, the browser sends a GET request to the server/path. When the server returns a response to the browser, two keys are Set in the response header as the set-cookie header field. Then the browser automatically saves the set-cookie header field after receiving it. The browser automatically adds the Cookie request header field to all subsequent requests.
This is the general working principle of Cookie. In summary, Cookie has the following characteristics:
Cookie
The server and browser need to work togetherSet-Cookie
Set in the response header fieldCookie
And you can set more than one at a timeSet-Cookie
Response header field. Browser receivedSet-Cookie
In response to the header fieldautomaticsaveCookie
Content will be passed on subsequent requestsautomaticaddCookie
Request header field to carryCookie
Content to the server.Cookie
Always in the form of key-value pairs, such asa=123
,b=456
More than,Cookie
use;
Space.Cookie
Does not restrict request methods, anyHTTP
Either request method can be usedCookie
.- For safety reasons,
Cookie
There are domain name restrictions. Any vendor’s browser in the saveCookie
I keep track of thisCookie
Which domain name does it belong to? When sending a request to the server, only the domain name belongs to this domain nameCookie
To be carried. Such as access to127.0.0.1:8000
The browser will only carry the ones under the domain nameCookie
If the browser also saves itwww.jd.com
Under the domain ofCookie
Is not to be carried to127.0.0.1:8000
Server. Cookie
You can set the expiration time, as shown aboveCookie
Value is the keya=123
In this case, the browser’s default handling mechanism is to delete this automatically when the browser is closedCookie
. You can do this if you need to set the expiration timeSet-Cookie: a=123; Max-Age=60
At this time,a=123
This articleCookie
The expiration time is 60 seconds, after which the browser will automatically save this itemCookie
Delete it.
How do I view cookies in Chrome? Open Chrome developer Tools, select the Application TAB, and click on Cookies to see all the Cookies recorded under the current domain name.
If cookies are used to implement user login, the process is as follows:
- The login page for accessing Web applications is displayed.
- Enter the user name and password on the login page and click the login button to log in.
- The server receives the user name and password from the browser, checks the database for the existence of the user, and, once authenticated, adds it to the response returned to the browser
Set-Cookie: username=zhangsan
Response header field. - The browser receives a message with
Set-Cookie: username=zhangsan
After the response to the header field, theusername=zhangsan
Save it. - When the browser requests a page of the Web application againautomaticcarry
Cookie: username=zhangsan
Request header field. - The server receives the browser request and parses it
Cookie
Request header field, the server knows that the request iszhangsan
It’s on its way.
Although Cookie can be used to achieve user login, it is not reliable to directly store user information username=zhangsan to Cookie. Because usernames are easy to reveal and easy to guess. If the attacker knows that zhangsan exists in our system, he does not need to know zhangsan’s password. You only need to add the Cookie username=zhangsan to the 127.0.0.1:8000 domain name in the browser (you can manually change the Cookie in the Chorme developer tools). The browser will automatically carry this Cookie when it requests 127.0.0.1:8000 server next time. After receiving this request, the server will think that the request is zhangsan from this user, so that the website attacker can cheat the login mechanism of the server. Therefore, in order to solve this problem, someone proposed a concept called Session.
Session is not a technical implementation, but an idea. When cookies are used to realize user login, user information username=zhangsan is directly stored in the browser Cookie in plaintext. With the Session mechanism, user information can be saved to the server (in any storage medium), for example, it can be saved as a JSON object in a file:
{
"6d78289e237c48719201640736226e39": "zhangsan"."3cdb8c488abb4eccad2c7cc8c3c9d065": "lisi"
}
Copy the code
The key of the object is a random string called session_id and the value is the user name. Instead of sending the user name directly to the browser in plain text when the server returns the response, session_ID is placed in the set-cookie field at the head of the response: Session_id = 6 d78289e237c48719201640736226e39 sent to the browser. This change doesn’t change anything on the browser side, the browser still saves the Cookie locally and carries it with it the next time it sends a request. However, the addition of Session mechanism makes the user login mechanism more secure. Because session_id is a randomly generated string, a malicious user cannot forge the session_id even if he knows the user name.
However, with the addition of the Session mechanism, the server’s mechanism for authenticating users’ cookies has to be slightly modified. In the past, the server only needs to parse the Cookie sent by the browser to get the user name Zhangsan, but now after parsing the Cookie to get session_id, The server also needs to look up the value of the session_id key in the JSON object where all sessions are stored to get the current login user name.
After the Session mechanism is added, the login process is as follows:
- The login page for accessing Web applications is displayed.
- Enter the user name and password on the login page and click the login button to log in.
- The server receives the user name and password transmitted by the browser, and then searches the database for the existence of the user. After the authentication is passed
zhangsan
The user generates a randomsession_id
And thensession_id
And user names are stored as key-value pairsJSON
File, and then add to the response returned to the browserSet-Cookie: session_id=6d78289e237c48719201640736226e39
Response header field. - The browser receives a message with
Set-Cookie: session_id=6d78289e237c48719201640736226e39
After the response to the header field, thesession_id=6d78289e237c48719201640736226e39
Save it. - When the browser requests a page of the Web application againautomaticcarry
Cookie: session_id=6d78289e237c48719201640736226e39
Request header field. - The server receives the browser request and parses it
Cookie
The request header field getssession_id
And then to store all of themSession
的JSON
Find in file6d78289e237c48719201640736226e39
thissession_id
The corresponding user namezhangsan
The server knows that the request iszhangsan
It’s on its way.
The above is the most common mechanism to realize user login by using Session + Cookie. Session is just an idea, and it can also be used without Cookie. There are many ways to realize user login, and interested readers can explore them according to their own needs.
User management function design
Todo List is a Todo List application with user management.
- For the model layer, it needs to be added
User
,Session
The two model classes deal with users andSession
. - For the view layer, it needs to be added
register.html
(Registration),login.html
(Login) Two HTML pages. - For the controller layer, implementation is required
register
(Registration),login
(Login) Two view functions.
Except for the functionality that needs to be added to each layer of the MVC pattern. We also need to add two JSON files user.json and session.json to store user and session information respectively.
In addition, when the Todo List program is added to the user management function, only logged in users can view and manage their own Todo. So we need to make some changes to the existing view functions in the Todo management section.
User management function coding implementation
Based on the above analysis of user management functions, the current Todo List program directory structure is designed as follows:
Todo_list ├ ─ ─ for server py ├ ─ ─ tests │ ├ ─ ─ test_controllers. Py └ ─ ─ todo ├ ─ ─ just set py ├ ─ ─ config. Py ├ ─ ─ controllers │ ├ ─ ─ Set py │ ├ ─ ─ auth. Py │ ├ ─ ─ the static. Py │ └ ─ ─ todo. Py ├ ─ ─ the db │ ├ ─ ─ the session. The json │ ├ ─ ─ todo. Json │ └ ─ ─ the user. The json ├ ─ ─ Logs │ └ ─ ─ todo. Log ├ ─ ─ models │ ├ ─ ─ just set py │ ├ ─ ─ session. Py │ ├ ─ ─ todo. Py │ └ ─ ─ user. Py ├ ─ ─ the static │ ├ ─ ─ CSS │ │ └ ─ ─ style.css. CSS │ └ ─ ─ the favicon. Ico ├ ─ ─ templates │ ├ ─ ─ auth │ │ ├ ─ ─ the login. The HTML │ │ └ ─ ─ the register. The HTML │ └ ─ ─ todo │ ├ ─ ─ Edit the HTML │ └ ─ ─ index. HTML └ ─ ─ utils ├ ─ ─ just set py ├ ─ ─ error. Py ├ ─ ─ HTTP. Py ├ ─ ─ logging. Py └ ─ ─ templating. PyCopy the code
The Session model class is written in the models/session.py file:
# todo_list/todo/models/session.py
import uuid
import datetime
from . import Model
class Session(Model) :
Session model class
def __init__(self, **kwargs) :
For security purposes, the Session ID does not use an incremented number, but instead uses a UUID
self.id = kwargs.get('id')
if self.id is None:
self.id = uuid.uuid4().hex
self.user_id = kwargs.get('user_id', -1)
self.expire_in = kwargs.get('expire_in')
if self.expire_in is None:
now = datetime.datetime.now()
expire_in = now + datetime.timedelta(days=1)
self.expire_in = expire_in.strftime('%Y-%m-%d %H:%M:%S')
def is_expired(self) :
""" Determine whether the Session is expired ""
now = datetime.datetime.now()
return datetime.datetime.strptime(self.expire_in, '%Y-%m-%d %H:%M:%S') <= now
def save(self) :
""" overrides the parent class's save method, filtering out sessions that have expired when saved.
models = [model.__dict__ for model in self.all(a)if model.id! = self.id and not model.is_expired()]
if not self.is_expired():
models.append(self.__dict__)
self._save_db(models)
Copy the code
The Session Model class inherits from Model. Unlike the Todo model class, which implements only __init__ methods, the Session model class also implements is_EXPIRED and save methods. The is_expired method is used to determine whether the current Session is expired, since the user’s login time is usually limited. The save method filters out expired sessions while saving the current Session object.
I designed the following JSON object to store the Session data of user login:
{
"id": "6d78289e237c48719201640736226e39"."user_id": 2."expire_in": "The 2020-05-31 22:27:55"
}
Copy the code
Id is session_id, user_id corresponds to the ID of the current login user. In this way, the user can be found through the Session object. Expire_in indicates the expiration time of the Session.
To implement a random session_id, get a random string from the UUID in the Session model’s __init__ method.
The User model class is written in the models/user.py file:
# todo/models/user.py
import hashlib
from . import Model
from todo.config import SECRET
class User(Model) :
"" User model class ""
def __init__(self, **kwargs) :
self.id = kwargs.get('id')
self.username = kwargs.get('username'.' ')
self.password = kwargs.get('password'.' ')
@classmethod
def generate_password(cls, raw_password) :
""" Generate password """
md5 = hashlib.md5()
md5.update(SECRET.encode('utf-8'))
md5.update(raw_password.encode('utf-8'))
return md5.hexdigest()
@classmethod
def validate_password(cls, raw_password, password) :
""" Verify password """
md5 = hashlib.md5()
md5.update(SECRET.encode('utf-8'))
md5.update(raw_password.encode('utf-8'))
return md5.hexdigest() == password
Copy the code
For security reasons, passwords are usually not stored in the database as clear text. This way, if our Web application is dragged to the library, the plaintext password reduces the risk of the user being hit by the library.
Since passwords are not stored in files in plain text, the User model implements two methods: generating passwords and checking passwords. The generate_password method is called to pass in the original password, and the result is an encrypted string that can be stored in a file and cannot be decrypted. During authentication, pass the original password (the password entered by the user) and the encrypted character string into the validate_password method to verify the original password.
The MD5 algorithm is used to encrypt the user password. Md5 algorithm is a widely used hash algorithm, but it has the risk of collision, so it is salted in the code, which can greatly reduce the probability of collision.
During password authentication, you do not need to decrypt the encrypted password stored in the file. You only need to encrypt the original password in the same way and compare the two encrypted strings to see if they are equal. Because the MD5 algorithm must get the same output for the same input, that is, the encryption result of the same data is the same each time.
Strictly speaking, MD5 is not an encryption algorithm, but a hashing algorithm. Because the encrypted data can be decrypted through the encryption algorithm, and the hash algorithm is a summary of information, can not be reversed through the summary of the original data. But many people are used to the MD5 algorithm is called the encryption algorithm, so I also use the encryption algorithm to introduce it. For more knowledge about MD5 algorithm, you can search related materials to learn by yourself.
The user information will be stored in a DB /user.json file in the following format:
{
"id": 1."username": "user"."password": "7fff062fcb96c6f041df7dbc3fa0dcaf"
}
Copy the code
Having created the Session and User models, we will implement the User registration function.
The HTML for the registration page is as follows:
<! -- todo_list/todo/templates/auth/register.html -->
<! DOCTYPEhtml>
<html>
<head>
<meta charset="UTF-8">
<title>Todo List | Register</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<h1 class="container">Register</h1>
<div class="container">
<form class="register" action="/register" method="post">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<button>registered</button>
</form>
</div>
</body>
</html>
Copy the code
Append the CSS code for the registration page to the style.css file:
/* todo_list/todo/static/css/style.css */
.register {
width: 100%;
max-width: 600px;
text-align: center;
}
.register input {
width: 100%;
height: 40px;
padding: 0 4px;
}
.register button {
margin-top: 20px;
}
Copy the code
At the controller level, write a register view function to handle the business logic of user registration:
# todo_list/todo/controllers/auth.py
def register(request) :
""" Register view function ""
if request.method == 'POST':
Get the username and password from the form
form = request.form
logger(f'form: {form}')
username = form.get('username')
raw_password = form.get('password')
Verify that the username and password are valid
if not username or not raw_password:
return 'Invalid username or password'.encode('utf-8')
user = User.find_by(username=username, ensure_one=True)
if user:
return 'Username already exists'.encode('utf-8')
Hash the password, create and save the user information
password = User.generate_password(raw_password)
user = User(username=username, password=password)
user.save()
Redirect to login page after successful registration
return redirect('/login')
return render_template('auth/register.html')
Copy the code
The registered view function can receive two types of requests, GET or POST. If it is a GET request, it indicates that the user wants to access the registration page and directly returns the CORRESPONDING HTML of the registration page. If the request is POST, the user clicks the registration button on the registration page, and the registration logic needs to be processed.
After successful registration, the user will be redirected to the login page, so we will implement the user login function.
The HTML of the login page is as follows:
<! -- todo_list/todo/templates/auth/login.html -->
<! DOCTYPEhtml>
<html>
<head>
<meta charset="UTF-8">
<title>Todo List | Login</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<h1 class="container">Login</h1>
<div class="container">
<form class="login" action="/login" method="post">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<button>The login</button>
</form>
</div>
</body>
</html>
Copy the code
Append the CSS code for the login page to the style.css file:
/* todo_list/todo/static/css/style.css */
.login {
width: 100%;
max-width: 600px;
text-align: center;
}
.login input {
width: 100%;
height: 40px;
padding: 0 4px;
}
.login button {
margin-top: 20px;
}
Copy the code
At the controller level, write a login view function to handle the business logic of the user login:
# todo_list/todo/controllers/auth.py
def login(request) :
""" Login view function ""
If the user is already logged in, redirect directly to the home page
if current_user(request):
return redirect('/index')
if request.method == 'POST':
message = 'Incorrect username or password'.encode('utf-8')
Get the username and password from the form
form = request.form
logger(f'form: {form}')
username = form.get('username')
raw_password = form.get('password')
Verify that username and password are correct
if not username or not raw_password:
return message
user = User.find_by(username=username, ensure_one=True)
if not user:
return message
password = user.password
if not User.validate_password(raw_password, password):
return message
Create Session and write session_ID to Cookie
session = Session(user_id=user.id)
session.save()
cookies = {
'session_id': session.id,}return redirect('/index', cookies=cookies)
return render_template('auth/login.html')
Copy the code
Login view functions can also receive both GET and POST requests. If the request is a GET request, it indicates that the user wants to access the login page and directly returns the CORRESPONDING HTML of the login page. If the request is POST, the user clicks the login button on the login page, and the login logic needs to be processed.
The current_user function is called in the logon view function to determine whether the user is currently logged in. The current_user function is implemented as follows:
# todo_list/todo/utils/auth.py
def current_user(request) :
""" Get the current logged-in user ""
Get session_ID from Cookie
cookies = request.cookies
logger(f'cookies: {cookies}')
session_id = cookies.get('session_id')
Find Session and verify whether it is expired
session = Session.get(session_id)
if not session:
return None
if session.is_expired():
session.delete()
return None
Find the current logged-in user
user = User.get(session.user_id)
if not user:
return None
return user
Copy the code
In the current_user function, the cookies attribute of the request object is used to obtain the Cookie information carried in the current request. The code for how the Request class parses the Cookie information carried in the request can be viewed in the source repository in this section.
To implement user login, create a Session object based on user_id and write the session_ID of the Session object into the browser Cookie. So make the following changes to the redirect function and Response class:
# todo_list/todo/utils/http.py
def redirect(url, status=302, cookies=None) :
""" "Redirect """
headers = {
'Location': url,
}
body = ' '
return Response(body, headers=headers, status=status, cookies=cookies)
class Response(object) :
""" Response class ""
Get the reason phrase based on the status code
reason_phrase = {
200: 'OK'.302: 'FOUND'.405: 'METHOD NOT ALLOWED',}def __init__(self, body, headers=None, status=200, cookies=None) :
The default response header specifies that the type of the response content is HTML
_headers = {
'Content-Type': 'text/html; charset=utf-8',}if headers is not None:
_headers.update(headers)
self.headers = _headers # response headers
self.body = body # response body
self.status = status # status code
self.cookies = cookies # Cookie
def __bytes__(self) :
""" Construct response message """
# status line 'HTTP/1.1 200 OK\r\n'
header = F 'HTTP / 1.1{self.status} {self.reason_phrase.get(self.status, "")}\r\n'
# response header
header += ' '.join(f'{k}: {v}\r\n' for k, v in self.headers.items())
# Cookie
if self.cookies:
header += 'Set-Cookie: ' + \
'; '.join(f'{k}={v}' for k, v in self.cookies.items())
# a blank line
blank_line = '\r\n'
# response body
body = self.body
# body supports either STR or bytes
if isinstance(body, str):
body = body.encode('utf-8')
response_message = (header + blank_line).encode('utf-8') + body
return response_message
Copy the code
Then, when the login view function finishes processing the login logic and executes the return redirect(‘/index’, cookies=cookies) on the last line, you can redirect to the home page and complete the login.
Now you can test the registration and login functions in the browser:
After a successful login, the user is redirected to the home page to display all the toDo of the current user.
Todo is not currently associated with the user, and in order todo so, you need to change the todo model and the JSON object format in which todo is stored.
The __init__ method of the Todo model needs to be able to accept user_id:
# todo_list/todo/models/todo.py
from . import Model
class Todo(Model) :
"""
Todo 模型类
"""
def __init__(self, **kwargs) :
self.id = kwargs.get('id')
self.user_id = kwargs.get('user_id', -1)
self.content = kwargs.get('content'.' ')
Copy the code
We need to store the user_id in the JSON object that stores todo:
{
"id": 4."user_id": 1."content": "hello world"
}
Copy the code
This will query all toDos associated with the current user’s user_id.
Modify the Todo List home page view function to get the current login user and query all toDos associated with it.
# todo_list/todo/controllers/todo.py
def index(request) :
""" Home view function ""
user = current_user(request)
todo_list = Todo.find_by(user_id=user.id, sort=True, reverse=True)
context = {
'todo_list': todo_list,
}
return render_template('todo/index.html', **context)
Copy the code
The home page of the program will display the todo of the current user after successful login:
You can sign up for another account, open the Todo List program in another browser, and try adding a few ToDos to another user to see what happens.
After todo is associated with the user, new operations on toDO need to verify that the user is logged in. For toDo delete, modify, and query operations, only the creator of the todo has permission, so verify not only that the user is logged in, but also that the toDO of the operation belongs to the current logged-in user.
We could certainly put all the validation operations in view functions, but if you look closely, you’ll see that all the operations on Todo have one thing in common: they all need to verify that the user is logged in. So it would be more elegant to write a decorator that validates the login, so that all view functions that need to validate the user’s login only need to type this decorator.
# todo_list/todo/utils/auth.py
def login_required(func) :
""" Validate login decorator """
@functools.wraps(func)
def wrapper(request) :
user = current_user(request)
if not user:
return redirect('/login')
result = func(request)
return result
return wrapper
Copy the code
Using the Todo List home page view function as an example, verify that the login decorator is used as follows:
# todo_list/todo/controllers/auth.py
@login_required
def index(request) :
""" Home view function ""
user = current_user(request)
todo_list = Todo.find_by(user_id=user.id, sort=True, reverse=True)
context = {
'todo_list': todo_list,
}
return render_template('todo/index.html', **context)
Copy the code
You don’t need to make any changes to the code inside the index view function, just add the @login_required decoration to the function definition.
The Todo belonging only to the currently logged user is queried inside the view function by passing the user_id keyword argument to the find_by method of the Todo model.
The other view functions related to Todo are listed below and will not be covered here.
# todo_list/todo/controllers/auth.py
@login_required
def new(request) :
""" New Todo view function """
form = request.form
logger(f'form: {form}')
content = form.get('content')
if content:
user = current_user(request)
if user:
todo = Todo(content=content, user_id=user.id)
todo.save()
return redirect('/index')
@login_required
def edit(request) :
""" Edit the Todo view function
if request.method == 'POST':
form = request.form
logger(f'form: {form}')
id = int(form.get('id', -1))
content = form.get('content')
if id! = -1 and content:
user = current_user(request)
if user:
todo = Todo.find_by(id=id, user_id=user.id, ensure_one=True)
if todo:
todo.content = content
todo.save()
return redirect('/index')
args = request.args
logger(f'args: {args}')
id = int(args.get('id', -1))
if id= = -1:
return redirect('/index')
user = current_user(request)
if not user:
return redirect('/index')
todo = Todo.find_by(id=id, user_id=user.id, ensure_one=True)
if not todo:
return redirect('/index')
context = {
'todo': todo,
}
return render_template('todo/edit.html', **context)
@login_required
def delete(request) :
""" Delete todo view function """
form = request.form
logger(f'form: {form}')
id = int(form.get('id', -1))
if id! = -1:
user = current_user(request)
if user:
todo = Todo.find_by(id=id, user_id=user.id, ensure_one=True)
if todo:
todo.delete()
return redirect('/index')
Copy the code
Perfect project
A more complete Web project should include a mechanism for global exception handling, because you cannot enumerate all possible exceptions in every function of the program. Exceptions that are not caught are likely to reveal important information about a program if exposed directly to the user.
Here I designed two exception pages, 404 page to tell the user that the page visited does not exist, 500 page to tell the user that the server has an unknown error.
The HTML code for the 404 page is as follows:
<! -- todo_list/todo/templates/error/404.html -->
<! DOCTYPEhtml>
<html>
<head>
<meta charset="UTF-8">
<title>Todo List | Page Not Found</title>
</head>
<body>
<p>Page Not Found</p>
</body>
</html>
Copy the code
The HTML code for page 500 is as follows:
<! -- todo_list/todo/templates/error/500.html -->
<! DOCTYPEhtml>
<html>
<head>
<meta charset="UTF-8">
<title>Todo List | Internal Server Error</title>
</head>
<body>
<p>Internal Server Error</p>
</body>
</html>
Copy the code
Create a new error/ directory in the Templates template directory to house the 404 and 500 global exception pages.
Here are the 404 and 500 page handlers:
# todo_list/todo/utils/error.py
from todo.utils.templating import render_template
from utils.http import Response
def page_not_found() :
""" Handle 400 exception """
body = render_template('error/404.html')
return Response(body, status=400)
def internal_server_error() :
""" Handle 500 exception """
body = render_template('error/500.html')
return Response(body, status=500)
errors = {
404: page_not_found,
500: internal_server_error,
}
Copy the code
Because server.py is the entry and exit of the server program, this file is a good place to write code for global exception catching.
# todo_list/server.py
def process_connection(client) :
""" Processing client requests """
request_bytes = b''
while True:
chunk = client.recv(BUFFER_SIZE)
request_bytes += chunk
if len(chunk) < BUFFER_SIZE:
break
Request message
request_message = request_bytes.decode('utf-8')
logger(f'request_message: {request_message}')
Parse request packets
request = Request(request_message)
try:
Construct a response message from the request message
response_bytes = make_response(request)
except Exception as e:
logger(e)
# Return to user 500 page
response_bytes = bytes(errors[500] ())# return response
client.sendall(response_bytes)
# close the connection
client.close()
def make_response(request, headers=None) :
""" Construct response message """
The default status code is 200
status = 200
Handle static resource requests
if request.path.startswith('/static'):
route, methods = routes.get('/static')
else:
try:
route, methods = routes.get(request.path)
except TypeError:
Return the user to the 404 page
return bytes(errors[404] ())If the request method is not allowed to return the 405 status code
if request.method not in methods:
status = 405
data = 'Method Not Allowed'
else:
Route is actually the index view function we defined in controllers. Py
data = route(request)
If the Response object is returned, the Response message is directly obtained
if isinstance(data, Response):
response_bytes = bytes(data)
else:
The Response object must be constructed first, and then the Response packet must be obtained
response = Response(data, headers=headers, status=status)
response_bytes = bytes(response)
logger(f'response_bytes: {response_bytes}')
return response_bytes
Copy the code
If the URL path accessed by the user does not match the view function, the user can be returned with a 404 page. When an uncaught exception occurs before the response is returned, it is caught by global exception handling in the process_Connection function and can be returned to the user 500 page. Remember to write real exception information into the log for easy troubleshooting.
Here is a screenshot of the page when a 404 or 500 exception is encountered:
At this point, all the functions of the Todo List program are developed. Finally, leave an assignment for readers to try to implement the user logout function.
This chapter source: chapter8
Contact me:
-
WeChat: jianghushinian
-
Email address: [email protected]
-
Jianghushini.cn