Use web framework

From native WSGI to modern framework (Flask practical introduction)

In the early days of Python Web development, we had to directly face the "barbed wire" of the HTTP protocol and write the underlying request parsing and response structure by hand. Today, various full-featured/micro-frameworks have packaged these dirty work cleanly. This article will take you through the process of "from wheels to sports cars" and use Flask to build a complete login project so that you can get started easily.


1. Everything starts with WSGI: a unified standard interface

Python can have so many excellent web frameworks, and it is inseparable from a key industry-level abstract specification - WSGI (Web Server Gateway Interface). It specifies the handshake method between "Web server (such as Nginx, Gunicorn)" and "Python application", so you no longer need to write separate adaptation code for each server.

What does the original WSGI application look like?

Just write a callable object (usually a function) that accepts two fixed parameters:

def application(environ, start_response):
    """
    environ: 所有HTTP请求信息的大字典
    start_response: 服务器提供的回调,用来发送状态码和响应头
    """
    # 1. 发送状态码与响应头
    start_response('200 OK', [('Content-Type', 'text/html; charset=utf-8')])
    # 2. 返回响应体(必须是字节可迭代对象)
    return [b'<h1>你好,这是手写的WSGI应用!</h1>']

Although interaction has been unified, it is still a galaxy away from real project development:

  1. Routers need to parse the URL themselves and find the corresponding processing function.
  2. The request method (GET/POST) needs to be distinguished manually
  3. You have to process the byte stream yourself when taking form parameters.
  4. No basic capabilities such as template engine, database abstraction, and security protection
  5. Once the code is complex, maintaining it is like defusing bombs

**So the answer is clear: we need frameworks. **


The core idea of ​​the framework is to replace "repeated handwritten underlying logic" with "conventions" or "configuration items" to help you stop dirty work and focus on business. It generally provides these "out of the box" capabilities:

  • Auto routing: A decorator can tie the URL to the processing function
  • Request/Response Encapsulation: Get parameters and set status codes directly through objects, no need to mess with native bytes
  • Built-in template engine: decoupling HTML and Python code, making the front-end comfortable to read
  • Security components: CSRF protection, password hashing, security headers, etc.
  • Plug-in Ecosystem: Database, API, WebSocket, you can add whatever you want.

3. The first choice for lightweight entry: Flask framework in action

Flask is the most popular microframework in the Python community - the core is only responsible for the basic encapsulation of routing and template rendering. It is very small, but it can be transformed into a full-featured development kit through plug-ins. Perfect for getting started quickly, prototyping, or small projects.

3.1 Step 1: Install Flask

Open the terminal and do it with one line of command:

pip install flask

3.2 Step 2: Write a "minimum runnable" application

# app.py
from flask import Flask

# 1. 创建Flask实例,__name__用来定位静态文件/模板的位置
app = Flask(__name__)

# 2. 用装饰器绑定URL和处理函数
@app.route('/')
def home():
    # 直接返回字符串,Flask会自动添加响应头
    return '<h1>欢迎来到我的Flask入门站!</h1>'

# 3. 启动开发服务器
if __name__ == '__main__':
    app.run(debug=True)  # debug=True 开启热重载

runpython app.py, open in the browserhttp://127.0.0.1:5000, you will see your first Web page!

3.3 Step 3: Process multiple HTTP methods + form data

Flask only responds to GET requests by default. If you want to support additional methods such as POST, you need to@app.routespecified inmethodsparameters, and userequestObject fetch form:

from flask import Flask, request

app = Flask(__name__)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # 从 request.form 中获取提交的字段
        username = request.form.get('username', '匿名用户')
        return f'<h3>Hello, {username}!欢迎登录!</h3>'
    else:
        # GET 请求:返回一个简单的登录表单
        return '''
        <form method="post" style="max-width: 300px; margin: 2rem auto;">
            <p>
                <input name="username" placeholder="输入用户名" style="width: 100%; padding: 0.5rem;">
            </p>
            <p>
                <button type="submit" style="width: 100%; padding: 0.5rem; background: #0d6efd; color: white; border: none; border-radius: 4px;">
                    立即登录
                </button>
            </p>
        </form>
        '''

This way we have a route that can receive user input and respond differently.

3.4 Step 4: Advanced small project - login system with password hashing and Session

Although the above example works, there are three obvious problems:

  • HTML is written directly in Python strings, making it difficult to maintain
  • There is no login status and it will be lost as soon as it is refreshed.
  • If the password is saved in clear text, it is equivalent to running naked.

Let's make a prototype that is in line with modern practice. We do not introduce additional databases, use memory dictionaries to simulate user data, and use the security tools that come with Flask and Werkzeug.

Built-in tools that will be used

  • session:Storage login status
  • flash: Pop up a prompt message on the next page (success/failure)
  • generate_password_hashandcheck_password_hash: Secure password hashing and verification provided by Werkzeug
  • render_template_string: Render strings containing Jinja2 syntax into HTML (inline templates are used here, and it is recommended to separate them into template files for actual projects)

Complete code

from flask import (
    Flask, request, redirect, url_for, session, flash, render_template_string
)
from werkzeug.security import generate_password_hash, check_password_hash

app = Flask(__name__)
# ⚠️ 生产环境一定要从环境变量中读取,不能写死在代码里!
app.secret_key = 'replace_this_with_a_secure_random_string_in_production'

# 模拟用户数据库(用户名 -> 密码哈希)
mock_user_db = {
    'test_user': generate_password_hash('test123456', method='pbkdf2:sha256')
}

# 首页:根据登录状态显示不同内容
@app.route('/')
def home():
    if 'username' in session:
        return f'''
        <h1>欢迎回来,{session['username']}!</h1>
        <a href="/logout">点击退出登录</a>
        '''
    return '''
        <h1>Flask登录示例</h1>
        <a href="/login">去登录</a>
    '''

# 登录页
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')

        # 校验用户是否存在且密码匹配
        if (username in mock_user_db and 
            check_password_hash(mock_user_db[username], password)):
            session['username'] = username          # 把用户名写进session
            flash('登录成功!', 'success')
            return redirect(url_for('home'))
        else:
            flash('用户名或密码错误!', 'danger')

    # GET请求 或 校验失败时,显示登录表单
    # 注意:flash 消息会在这个页面被显示出来
    return render_template_string('''
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
        <style>
            body { font-family: Arial, sans-serif; max-width: 400px; margin: 3rem auto; padding: 0 1rem; }
            .flash-success { color: #155724; background: #d4edda; padding: 0.75rem; border-radius: 4px; margin-bottom: 1rem; }
            .flash-danger { color: #721c24; background: #f8d7da; padding: 0.75rem; border-radius: 4px; margin-bottom: 1rem; }
            input { width: 100%; padding: 0.75rem; margin-bottom: 1rem; border: 1px solid #ced4da; border-radius: 4px; }
            button { width: 100%; padding: 0.75rem; background: #0d6efd; color: white; border: none; border-radius: 4px; cursor: pointer; }
        </style>
    </head>
    <body>
        <!-- 显示flash消息 -->
        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                {% for category, message in messages %}
                    <div class="flash-{{ category }}">{{ message }}</div>
                {% endfor %}
            {% endif %}
        {% endwith %}

        <h2>登录</h2>
        <form method="post">
            <input name="username" placeholder="用户名" required>
            <input name="password" type="password" placeholder="密码" required>
            <button type="submit">登录</button>
        </form>
    </body>
    </html>
    ''')

# 退出登录
@app.route('/logout')
def logout():
    session.pop('username', None)  # 清除session中的用户名
    flash('已成功退出!', 'success')
    return redirect(url_for('home'))

if __name__ == '__main__':
    app.run(debug=True)

Operation effect description

  1. First visit/, see the "Go to Login" link.
  2. Enter/login, enter the wrong username/password, and the page will display a red error prompt.
  3. Use the correcttest_user / test123456Log in, automatically jump back to the homepage, display the welcome message, and prompt "Login successful".
  4. Click Exit to clear the login status and return to the non-login status again.

🧠 Tip: This example usesrender_template_stringRendering inline templates is to make the entire logic in one file for easy demonstration. Real projects recommend extracting HTML totemplates/directory, userender_template()load.


4. Quick overview of mainstream Python web frameworks

Different needs require different “weapons”. Here is a quick selection reference for you:

Framework nameCore positioningBiggest featuresApplicable scenarios
FlaskMicro-frameworkLightweight and flexible, rich plug-in ecosystemGetting started, small projects, API prototypes
djangoFull-featured framework"Bring your own battery": ORM, backend management, authentication, etc.Large and complex applications (CMS, e-commerce)
FastAPIAsynchronous API frameworkAutomatically generate OpenAPI documents, performance close to GoHigh concurrency API, microservices
TornadoAsynchronous network frameworkNative support for WebSocket, long connectionReal-time communication (chat, barrage)

5. Summary and suggestions

  1. Don’t reinvent the wheel from native WSGI: The framework exists to allow you to write less low-level code.
  2. Select according to project scale: Choose Flask for small projects/rapid prototyping, choose Django for full-featured heavy-duty applications, and choose FastAPI for high real-time APIs.
  3. Always keep safety and practice in mind:
  • Never hardcodesecret_keyand database password, use environment variables.
  • The password must be hashed (used herepbkdf2:sha256It’s very safe).
  • Completely separate business logic, display templates, and configuration.
  1. Production environment deployment reminder:app.run()It is just a development server. When going online, you must use Gunicorn / uWSGI with Nginx, or use Docker directly.

I hope this tutorial from underlying principles to hands-on practice can help you smoothly open the door to Python web development!