爬虫代码已复制

HTMX + Flask 实现用户登录:动态交互的完整教程

1739088315阅读数:97
上一篇:Python爬虫实战C01爬虫实战练习案例解析
下一篇:C04 - Python爬虫实战案例练习提示

htmx 实现用户登录

htmx 官方文档过于简洁,而且有些细节容易被忽略,而且目前很难找到使用 htmx 的完整项目作为参考,所以有可能会导致大家在学习 htmx 的过程中卡壳。作者就以用户登录为例,带大家领略 htmx 的魅力。本示例包含了登录表单的验证、登录信息提交、错误处理等实际项目开发中必需的功能。

htmx 完整项目源码

完整的 htmx 用户登录源码已经上传至 GitHub

使用到的 htmx 特性

htmx 属性

htmx 事件

htmx API

htmx 响应头

项目结构

完整项目总共3个文件,按 Flask 默认的目录结构搭建。

  • app.py
  • templates/login.html
  • static/htmx.min.js

服务端

服务端使用 flask 实现,定义了3个路由,一个是首页:打开一个登录页面表单;第二是 dashboard:登录成功后展示的示例页面;最后一个是登录:用于处理前端 POST 过来的登录请求。

服务端源码

app.py

@app.route('/')
def index():
    return render_template('login.html')

@app.route('/dashboard')
def dashboard():
    # 登录成功后的页面
    return '<h1>Welcome to DashBoard</h1><h2>Login Successful.</h2>'

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    if username == 'admin' and password == 'admin':
        res = make_response('Login successful')
        res.headers['HX-Redirect'] = url_for('dashboard')
        return res
    else:
        # 登录失败,返回错误信息及400状态码
        return 'Login failed',400

服务端会验证前端传递过来的用户名/密码是否为admin/admin,如何不是则视为登录失败,返回 Login failed 消息,并把 HTTP 状态码设为400。

注意: 把 HTTP 状态码设为400这一步很重要,否则前端 htmx:responseError 捕捉不到登录失败的信号。当然,你也可以编写 javascript 代码来拦截处理,但这样就失去了 htmx 的灵魂。

如果登录成功,服务端会添加一个 HTTP Header:HX-Redirect = /dashboard,这相当于告诉前端 htmx 重定向到 /dashboard。

整个服务端的 Python 有效代码不到30行,当然实际的项目会比这个数字要大,因为这里只是一个示例,登录成功后的 session、cookie 之类的是没有进行处理的。

前端

前端主要是一个用户名输入框、一个密码输入框、一个提交按钮,加上一些响应 H5 表单验证的 javascript 代码以及一些处理登录失败时的错误提示的 javascript 代码。

前端源码

login.html

<div class="login">
    <h1>Login</h1>
    <!-- 两个 input 都加了required,并且监听了hx-on:change事件 -->
    <input type="text" id="username" name="username" placeholder="Username" required hx-on:change="clean_error(event)">
    <div id="username-error" class="error"></div>
    <input type="password" id="password" name="password" placeholder="Password" required hx-on:change="clean_error(event)">
    <div id="password-error" class="error"></div>
    <button type="button"
        hx-post="login"
        hx-include="#username,#password"
        hx-validate="true"
        hx-on:htmx:validation:halted="validation_error(event);"
        hx-on:htmx:response-error="bad_request(event);"
    >Login</button>
    <div id="error" class="error"></div>
</div>

由一个 div 包裹了一个登录表单,这里我们并没有使用常规的 <form> 表单标签,而是直接把 input 组件放在 div 里面,并把每个 input 都设置了一个 id ,我们要提交表示时就借助 hx-include 来整合表单数据。

在提交按钮上我们不仅设置了 hx-post ,还使用了 hx-validate 开启 H5 的表单验证功能,同时监听了表单验证错误事件 htmx:validation:halted 和 HTTP 状态码不是2xx、3xx 的服务器错误事件 htmx:responseError 。

同时可以通过以下 javascript 代码对表单验证的错误消息以及用户登录失败后服务器返回的错误消息进行展示。

// H5验证失败时触发 hx-on:htmx:validation:halted
function validation_error(event) {
    event.detail.errors.forEach(err => {
        const elt = htmx.find(`#${err.elt.id}-error`);
        if(elt){
            elt.innerHTML = err.message;
        }
    });

}
// 服务器返回400时触发 hx-on:htmx:responseError
function bad_request(event) {
    const elt = htmx.find('#error');
    if(elt){
        elt.innerHTML = event.detail.xhr.response;
    }
}

htmx 的一些坑

htmx 官方网站的文档上写的事件是驼峰命名的,但也在 事件与日志 中有提到:所有事件都以两个不同的名称触发,分别是驼峰命名法及烤肉串命名法。

如果不仔细地把官方文档都通读一遍,就会根据 htmx:responseError 的文档页上的说明(其实这个页面啥也没说)把代码写成 hx-on:htmx:responseError 。

然而,经测试,hx-on:htmx:responseError 这样的代码是无效的,必须把大写字母改成小写,并在前面加一个横杠。