当前位置:首页 > 技术 > 正文内容

Python接口测试框架搭建实战(上)

访客 技术 2026年6月27日 2

本节内容概要

  • 测试数据分离 - 从Excel文件读取测试数据
  • 日志记录功能实现
  • 测试报告邮件发送
  • 配置文件管理
  • 框架目录结构优化

![框架初始结构](https://upload-images.jianshu.io/upload_images/7575721-93d65d7b90ec067d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

一、测试数据分离 - 从Excel读取数据

在前面的测试代码中,测试数据直接硬编码在Python文件中,这样的方式不利于数据的维护和修改。我们将测试数据存储在Excel文件中,实现代码与数据的分离。

创建Excel文件`api_test_data.xlsx`,包含两个工作表`LoginTest`和`RegisterTest`,并放置在项目根目录。

Excel数据结构

Excel数据表结构

在Excel中增加headers列,用于存储请求头信息,格式为JSON。

LoginTest工作表:

case_name url method headers data expect_res
test_login_success http://115.28.108.130:5000/api/user/login/ POST {} {"name":"zhangsan","password":"123456"} <h1>登录成功</h1>
test_login_password_error http://115.28.108.130:5000/api/user/login/ POST {} {"name":"zhangsan","password":"1234567"} <h1>失败,用户名或密码错误</h1>

RegisterTest工作表:

case_name url method headers data expect_res
test_register_success http://115.28.108.130:5000/api/user/reg/ POST {} {"name":"lisi","password":"123456"} {"code":"100000","msg":"成功","data":
test_register_already_exists http://115.28.108.130:5000/api/user/reg/ POST {} {"name":"wangwu","password":"123456"} {"code":"100001","msg":"失败,用户已存在","data":{"name":"wangwu","password":"e10adc3949ba59abbe56e057f20f883e"}}

Excel读取实现

使用Python的xlrd库来读取Excel文件。

pip install xlrd
import xlrd

# 打开Excel文件
workbook = xlrd.open_workbook("api_test_data.xlsx")
# 通过工作表名称获取 sheet 对象
sheet = workbook.sheet_by_name("LoginTest")

# 获取行列数
print(f"数据行数:{sheet.nrows}")
print(f"数据列数:{sheet.ncols}")

# 获取单个单元格内容
print(f"第一行第一列:{sheet.cell(0, 0).value}")

# 获取整行数据
print(f"第一行数据:{sheet.row_values(0)}")

# 合并标题与数据行为字典
first_row = sheet.row_values(0)
second_row = sheet.row_values(1)
print(dict(zip(first_row, second_row)))

# 遍历所有数据行
for i in range(sheet.nrows):
    print(f"第{i}行数据:{sheet.row_values(i)}")

执行结果:

数据行数:3
数据列数:5
第一行第一列:case_name
第一行数据:['case_name', 'url', 'method', 'data', 'expect_res']
{'case_name': 'test_login_success', 'url': 'http://115.28.108.130:5000/api/user/login/', 'method': 'POST', 'data': '{"name":"zhangsan","password":"123456"}', 'expect_res': '<h1>登录成功</h1>'}
第0行数据:['case_name', 'url', 'method', 'data', 'expect_res']
第1行数据:['test_login_success', 'http://115.28.108.130:5000/api/user/login/', 'POST', '{"name":"zhangsan","password":"123456"}', '<h1>登录成功</h1>']
第2行数据:['test_login_password_error', 'http://115.28.108.130:5000/api/user/login/', 'POST', '{"name":"zhangsan","password":"1234567"}', '<h1>失败,用户不存在</h1>']

封装Excel读取工具类

新建`data_helper.py`文件,封装两个函数:

  • `load_excel_data(file_name, sheet_name)`:一次性读取指定工作表的所有数据
  • `find_test_case(all_data, case_name)`:根据用例名称查找对应数据
  • import xlrd
    
    def load_excel_data(file_name, sheet_name):
        """
        读取Excel文件中指定工作表的所有数据
        返回:列表形式,每个元素为一条用例数据的字典
        """
        result_data = []
        workbook = xlrd.open_workbook(file_name)
        sheet = workbook.sheet_by_name(sheet_name)
        
        # 获取标题行
        header_row = sheet.row_values(0)
        
        # 跳过标题行,从第二行开始遍历
        for i in range(1, sheet.nrows):
            row_data = sheet.row_values(i)
            case_dict = dict(zip(header_row, row_data))
            result_data.append(case_dict)
        
        return result_data
    
    def find_test_case(data_list, case_name):
        """
        从数据列表中查找指定用例名的数据
        返回:找到返回用例数据字典,未找到返回None
        """
        for item in data_list:
            if case_name == item.get('case_name'):
                return item
        return None
    
    if __name__ == '__main__':
        # 测试代码
        all_cases = load_excel_data("api_test_data.xlsx", "LoginTest")
        case_info = find_test_case(all_cases, 'test_login_success')
        print(case_info)
    

    输出结果:

    {'case_name': 'test_login_success', 'url': 'http://115.28.108.130:5000/api/user/login/', 'method': 'POST', 'data': '{"name":"zhangsan","password":"123456"}', 'expect_res': '<h1>登录成功</h1>'}
    

    测试用例中应用数据

    登录测试用例 - test_login.py

    import unittest
    import requests
    from data_helper import load_excel_data, find_test_case
    import json
    
    class TestUserLogin(unittest.TestCase):
        
        @classmethod
        def setUpClass(cls):
            # 读取该测试类的所有用例数据,整个类只执行一次
            cls.case_data_list = load_excel_data("api_test_data.xlsx", "LoginTest")
    
        def test_login_success(self):
            # 从数据列表中查找对应用例数据
            case_data = find_test_case(self.case_data_list, 'test_login_success')
            if not case_data:
                print("未找到用例数据")
                return
            
            url = case_data.get('url')
            data = case_data.get('data')
            expect_res = case_data.get('expect_res')
    
            # 发送POST请求,data参数需要转换为字典
            response = requests.post(url=url, data=json.loads(data))
            
            # 断言验证
            self.assertEqual(response.text, expect_res)
    
    if __name__ == '__main__':
        unittest.main(verbosity=2)
    

    注册测试用例 - test_register.py

    import unittest
    import requests
    from db_helper import query_db, execute_db
    from data_helper import load_excel_data, find_test_case
    import json
    
    class TestUserRegister(unittest.TestCase):
    
        @classmethod
        def setUpClass(cls):
            cls.case_data_list = load_excel_data("api_test_data.xlsx", "RegisterTest")
    
        def test_register_success(self):
            case_data = find_test_case(self.case_data_list, 'test_register_success')
            if not case_data:
                print("未找到用例数据")
                return
            
            url = case_data.get('url')
            data_dict = json.loads(case_data.get('data'))
            expect_dict = json.loads(case_data.get('expect_res'))
            
            user_name = data_dict.get("name")
    
            # 环境准备:清理已存在的用户
            if query_db(f"SELECT * FROM users WHERE name='{user_name}'"):
                execute_db(f"DELETE FROM users WHERE name='{user_name}'")
    
            # 发送注册请求
            response = requests.post(url=url, json=data_dict)
            
            # 响应结果断言
            self.assertDictEqual(response.json(), expect_dict)
            
            # 数据库数据验证
            result = query_db(f"SELECT * FROM users WHERE name='{user_name}'")
            self.assertTrue(len(result) > 0, "用户未成功注册到数据库")
            
            # 清理测试数据
            execute_db(f"DELETE FROM users WHERE name='{user_name}'")
    
    if __name__ == '__main__':
        unittest.main(verbosity=2)
    

    二、日志功能实现

    创建`logger.py`配置文件,配置日志输出格式和级别。

    import logging
    
    # 日志基础配置
    logging.basicConfig(
        level=logging.DEBUG,  # 日志级别
        format='[%(asctime)s] %(levelname)s [%(funcName)s:%(filename)s, %(lineno)d] %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S',
        filename='run_log.txt',
        filemode='a'
    )
    
    if __name__ == '__main__':
        logging.info("测试日志输出")
    

    运行后会在当前目录生成`run_log.txt`,内容示例:

    [2018-09-11 18:08:17] INFO [<module>: logger.py, 38] 测试日志输出
    

    日志级别说明

    • CRITICAL - 严重错误,导致程序停止运行
    • ERROR - 错误信息,影响功能运行
    • WARNING - 警告信息,提示潜在问题
    • INFO - 一般信息,记录正常流程
    • DEBUG - 调试信息,详细执行过程

    级别优先级:CRITICAL > ERROR > WARNING > INFO > DEBUG

    设置level=DEBUG时,所有级别都会输出;设置level=ERROR时,仅输出ERROR和CRITICAL。

    日志格式化参数

    • %(levelno)s - 日志级别数值
    • %(levelname)s - 日志级别名称
    • %(pathname)s - 执行程序完整路径
    • %(filename)s - 执行程序文件名
    • %(funcName)s - 所在函数名
    • %(lineno)d - 所在行号
    • %(asctime)s - 时间戳
    • %(thread)d - 线程ID
    • %(process)d - 进程ID
    • %(message)s - 日志内容

    项目中集成日志

    修改`db_helper.py`,添加日志记录

    import pymysql
    from logger import *
    
    def query_db(sql):
        conn = get_db_connection()
        cursor = conn.cursor()
        
        logging.debug(f"执行SQL查询:{sql}")
        cursor.execute(sql)
        conn.commit()
        query_result = cursor.fetchall()
        logging.debug(f"查询结果:{query_result}")
        
        cursor.close()
        conn.close()
        return query_result
    
    def execute_db(sql):
        conn = get_db_connection()
        cursor = conn.cursor()
        
        logging.debug(f"执行SQL语句:{sql}")
        try:
            cursor.execute(sql)
            conn.commit()
        except Exception as error:
            conn.rollback()
            logging.error(f"SQL执行失败:{str(error)}")
        finally:
            cursor.close()
            conn.close()
    

    在测试用例中使用日志

    import unittest
    import requests
    from data_helper import load_excel_data, find_test_case
    import json
    from logger import logging
    
    class TestUserLogin(unittest.TestCase):
        
        @classmethod
        def setUpClass(cls):
            cls.case_data_list = load_excel_data("api_test_data.xlsx", "LoginTest")
    
        def test_login_success(self):
            case_data = find_test_case(self.case_data_list, 'test_login_success')
            if not case_data:
                logging.error("用例数据不存在")
                return
            
            url = case_data.get('url')
            data = case_data.get('data')
            expect_res = case_data.get('expect_res')
    
            response = requests.post(url=url, data=json.loads(data))
            
            # 输出详细日志信息
            logging.info(f"测试用例:test_login_success")
            logging.info(f"请求地址:{url}")
            logging.info(f"请求参数:{data}")
            logging.info(f"预期结果:{expect_res}")
            logging.info(f"实际结果:{response.text}")
            
            self.assertEqual(response.text, expect_res)
    
    if __name__ == '__main__':
        unittest.main(verbosity=2)
    

    生成的日志文件内容:

    [2018-09-13 10:34:49] INFO [test_login_success: test_login.py, 20] 测试用例:test_login_success
    [2018-09-13 10:34:49] INFO [test_login_success: test_login.py, 21] 请求地址:http://115.28.108.130:5000/api/user/login/
    [2018-09-13 10:34:49] INFO [test_login_success: test_login.py, 22] 请求参数:{"name":"zhangsan","password":"123456"}
    [2018-09-13 10:34:49] INFO [test_login_success: test_login.py, 23] 预期结果:<h1>登录成功</h1>
    [2018-09-13 10:34:49] INFO [test_login_success: test_login.py, 24] 实际结果:<h1>登录成功</h1>
    

    封装日志输出函数

    由于每个测试用例都需要记录大量日志信息,创建`logger_helper.py`统一处理。

    from logger import logging
    import json
    
    def record_test_log(case_name, url, request_data, expected, actual_response):
        """
        统一记录测试用例的日志信息
        """
        if isinstance(request_data, dict):
            request_data = json.dumps(request_data, ensure_ascii=False)
        
        logging.info(f"用例名称:{case_name}")
        logging.info(f"请求地址:{url}")
        logging.info(f"请求参数:{request_data}")
        logging.info(f"预期结果:{expected}")
        logging.info(f"实际结果:{actual_response}")
    

    简化后的测试用例

    import unittest
    import requests
    from data_helper import load_excel_data, find_test_case
    import json
    from logger import logging
    from logger_helper import record_test_log
    
    class TestUserLogin(unittest.TestCase):
        
        @classmethod
        def setUpClass(cls):
            cls.case_data_list = load_excel_data("api_test_data.xlsx", "LoginTest")
    
        def test_login_success(self):
            case_data = find_test_case(self.case_data_list, 'test_login_success')
            if not case_data:
                logging.error("用例数据不存在")
                return
            
            url = case_data.get('url')
            data = case_data.get('data')
            expect_res = case_data.get('expect_res')
    
            response = requests.post(url=url, data=json.loads(data))
            
            # 使用封装好的日志函数
            record_test_log('test_login_success', url, data, expect_res, response.text)
            
            self.assertEqual(response.text, expect_res)
    
    if __name__ == '__main__':
        unittest.main(verbosity=2)
    

    三、测试报告邮件发送

    测试完成后,需要自动将测试报告发送到指定邮箱。Python发送邮件通过SMTP服务实现。

    前提条件:发送邮件的邮箱需要开启SMTP服务

    邮件发送流程

    1. 编写邮件内容(MIME格式)
    2. 组装邮件头(发件人、收件人、主题)
    3. 连接SMTP服务器并发送

    基础邮件发送

    import smtplib
    from email.mime.text import MIMEText
    
    # 1. 编写邮件内容
    msg = MIMEText('这是一封测试邮件', 'plain', 'utf-8')
    
    # 2. 组装邮件头
    msg['From'] = 'test_sender@sina.com'
    msg['To'] = 'receiver@example.com'
    msg['Subject'] = '接口测试报告'
    
    # 3. 连接SMTP服务器并发送
    smtp = smtplib.SMTP_SSL('smtp.sina.com')
    smtp.login('your_email', 'your_password')
    smtp.sendmail('sender@example.com', 'receiver@example.com', msg.as_string())
    smtp.quit()
    

    发送HTML邮件及附件

    import smtplib
    from email.mime.text import MIMEText
    from email.mime.multipart import MIMEMultipart
    from email.header import Header
    
    # 读取HTML报告内容
    with open('test_report.html', 'r', encoding='utf-8') as f:
        email_content = f.read()
    
    # 创建混合格式邮件
    email_msg = MIMEMultipart()
    email_msg.attach(MIMEText(email_content, 'html', 'utf-8'))
    
    # 设置邮件头
    email_msg['From'] = 'test_sender@sina.com'
    email_msg['To'] = 'receiver@example.com'
    email_msg['Subject'] = Header('接口测试报告', 'utf-8')
    
    # 添加附件
    attachment = MIMEText(open('test_report.html', 'rb').read(), 'base64', 'utf-8')
    attachment["Content-Type"] = 'application/octet-stream'
    attachment["Content-Disposition"] = 'attachment; filename="test_report.html"'
    email_msg.attach(attachment)
    
    # 发送邮件
    smtp = smtplib.SMTP_SSL('smtp.sina.com')
    smtp.login('test_sender@sina.com', 'password123')
    smtp.sendmail('test_sender@sina.com', 'receiver@example.com', email_msg.as_string())
    smtp.quit()
    

    封装邮件发送函数

    import smtplib
    from email.mime.text import MIMEText
    from email.mime.multipart import MIMEMultipart
    from email.header import Header
    from logger import logging
    
    def send_test_report(report_file_path):
        """
        发送测试报告邮件
        """
        email_msg = MIMEMultipart()
        
        # 添加邮件正文
        with open(report_file_path, 'r', encoding='utf-8') as f:
            email_content = f.read()
        email_msg.attach(MIMEText(email_content, 'html', 'utf-8'))
    
        # 设置邮件头
        email_msg['From'] = 'test_sender@sina.com'
        email_msg['To'] = 'receiver@example.com'
        email_msg['Subject'] = Header('接口测试报告', 'utf-8')
    
        # 添加附件
        attachment = MIMEText(open(report_file_path, 'rb').read(), 'base64', 'utf-8')
        attachment["Content-Type"] = 'application/octet-stream'
        attachment["Content-Disposition"] = f'attachment; filename="{report_file_path}"'
        email_msg.attach(attachment)
    
        try:
            smtp = smtplib.SMTP_SSL('smtp.sina.com')
            smtp.login('test_sender@sina.com', 'password123')
            smtp.sendmail('test_sender@sina.com', 'receiver@example.com', email_msg.as_string())
            logging.info("邮件发送成功!")
        except Exception as e:
            logging.error(f"邮件发送失败:{str(e)}")
        finally:
            smtp.quit()
    

    测试执行并发送邮件

    import unittest
    from HTMLTestReportCN import HTMLTestRunner
    from logger import logging
    from mail_helper import send_test_report
    
    logging.info("=================== 测试开始 ===================")
    
    # 发现并执行所有测试用例
    test_suite = unittest.defaultTestLoader.discover("./")
    
    # 生成测试报告
    with open("test_report.html", 'wb') as f:
        runner = HTMLTestRunner(stream=f, title="API测试报告", description="接口测试执行结果")
        runner.run(test_suite)
    
    # 发送邮件
    send_test_report('test_report.html')
    
    logging.info("=================== 测试结束 ===================")
    

    邮件发送效果

    四、配置文件管理

    将数据库配置、邮件配置、日志配置等统一放到`settings.py`文件中管理。

    import logging
    import os
    
    # 项目根目录
    project_root = os.path.dirname(os.path.abspath(__file__))
    
    # 路径配置
    data_dir = project_root
    test_case_dir = project_root
    log_file = os.path.join(project_root, 'log.txt')
    report_file = os.path.join(project_root, 'report.html')
    
    # 日志配置
    logging.basicConfig(
        level=logging.DEBUG,
        format='[%(asctime)s] %(levelname)s [%(funcName)s:%(filename)s, %(lineno)d] %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S',
        filename=log_file,
        filemode='a'
    )
    
    # 数据库配置
    db_host = '127.0.0.1'
    db_port = 3306
    db_user = 'test_user'
    db_password = 'test123456'
    db_name = 'api_test'
    
    # 邮件配置
    smtp_server = 'smtp.sina.com'
    smtp_user = 'test_sender@sina.com'
    smtp_password = 'password123'
    
    email_from = smtp_user
    email_to = 'receiver@example.com'
    email_subject = '接口测试报告'
    

    修改相关文件引用配置

    数据库连接 - db_helper.py

    import pymysql
    from settings import *
    
    def get_db_connection():
        return pymysql.connect(
            host=db_host,
            port=db_port,
            user=db_user,
            passwd=db_password,
            db=db_name,
            charset='utf8'
        )
    

    邮件发送 - mail_helper.py

    import smtplib
    from email.mime.text import MIMEText
    from email.mime.multipart import MIMEMultipart
    from email.header import Header
    from settings import *
    from logger import logging
    
    def send_test_report(report_file_path):
        email_msg = MIMEMultipart()
        email_msg.attach(MIMEText(open(report_file_path, 'r', encoding='utf-8').read(), 'html', 'utf-8'))
    
        email_msg['From'] = email_from
        email_msg['To'] = email_to
        email_msg['Subject'] = Header(email_subject, 'utf-8')
    
        attachment = MIMEText(open(report_file_path, 'rb').read(), 'base64', 'utf-8')
        attachment["Content-Type"] = 'application/octet-stream'
        attachment["Content-Disposition"] = f'attachment; filename="{os.path.basename(report_file_path)}"'
        email_msg.attach(attachment)
    
        try:
            smtp = smtplib.SMTP_SSL(smtp_server)
            smtp.login(smtp_user, smtp_password)
            smtp.sendmail(email_from, email_to, email_msg.as_string())
            logging.info("测试报告邮件已发送")
        except Exception as error:
            logging.error(f"邮件发送失败:{str(error)}")
        finally:
            smtp.quit()
    

    测试执行 - run_all.py

    import unittest
    from HTMLTestReportCN import HTMLTestRunner
    from settings import logging
    from mail_helper import send_test_report
    
    logging.info("=================== 测试开始 ===================")
    
    test_suite = unittest.defaultTestLoader.discover(test_case_dir)
    
    with open(report_file, 'wb') as f:
        runner = HTMLTestRunner(stream=f, title="API测试报告", description="接口测试结果")
        runner.run(test_suite)
    
    send_test_report(report_file)
    
    logging.info("=================== 测试结束 ===================")
    

    五、框架目录结构优化

    随着项目规模扩大,需要对文件进行分类管理,建立清晰的目录结构。

    初始文件结构

    创建目录结构

    • config/ - 项目配置文件
    • data/ - 测试数据文件
    • libs/ - 公共方法库
    • logs/ - 日志文件
    • reports/ - 测试报告
    • test_cases/ - 测试用例
      • user/ - 用户模块用例(需包含__init__.py)

    优化后的目录结构

    修改配置路径

    修改`config/settings.py`中的路径配置:

    import logging
    import os
    
    # 项目根目录(config目录的上一级)
    project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    # 路径配置
    data_dir = os.path.join(project_root, 'data')
    test_case_dir = os.path.join(project_root, 'test_cases')
    log_file = os.path.join(project_root, 'logs', 'run_log.txt')
    report_file = os.path.join(project_root, 'reports', 'test_report.html')
    

    处理导入路径问题

    由于目录结构变化,需要调整导入路径。使用sys.path添加项目根目录到搜索路径。

    libs/db_helper.py

    import pymysql
    import sys
    sys.path.append('..')  # 提升到项目根目录
    from config.settings import *
    

    test_cases/user/test_login.py

    import unittest
    import requests
    import json
    import os
    import sys
    sys.path.append('../..')  # 提升两级到项目根目录
    
    from config.settings import logging
    from libs.data_helper import load_excel_data, find_test_case
    from libs.logger_helper import record_test_log
    
    class TestUserLogin(unittest.TestCase):
        
        @classmethod
        def setUpClass(cls):
            excel_path = os.path.join(data_dir, "api_test_data.xlsx")
            cls.case_data_list = load_excel_data(excel_path, "LoginTest")
    
        def test_login_success(self):
            case_data = find_test_case(self.case_data_list, 'test_login_success')
            if not case_data:
                logging.error("用例数据不存在")
                return
            
            url = case_data.get('url')
            data = case_data.get('data')
            expect_res = case_data.get('expect_res')
    
            response = requests.post(url=url, data=json.loads(data))
            record_test_log('test_login_success', url, data, expect_res, response.text)
            self.assertEqual(response.text, expect_res)
    

    run_all.py(项目根目录)

    import unittest
    from libs.HTMLTestReportCN import HTMLTestRunner
    from config.settings import logging
    from libs.mail_helper import send_test_report
    
    logging.info("=================== 测试开始 ===================")
    
    test_suite = unittest.defaultTestLoader.discover(test_case_dir)
    
    with open(report_file, 'wb') as f:
        runner = HTMLTestRunner(stream=f, title="API测试报告", description="接口测试执行")
        runner.run(test_suite)
    
    send_test_report(report_file)
    
    logging.info("=================== 测试结束 ===================")
    

    注意事项

    • 同一目录下模块相互导入也需要从项目根目录导入
    • run_all.py位于项目根目录,无需修改sys.path
    • 运行测试后根据日志和报告调试,确保所有用例通过

    本节内容到此结束,下一节将继续完善框架功能,包括用例基类设计、用例标签管理和失败用例重试机制。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。