Python+requests+unittest+excel搭建接口自动化测试框架

一、框架结构

工程目录【文末有配套视频和资料免费领取】

代码:基于python2编写

二、Case文件设计

三、基础包 base

3.1 封装get/post请求(runmethon.py)

import requests
import json
class RunMethod:def post_main(self,url,data,header=None):res = Noneif header !=None:res = requests.post(url=url,data=data,headers=header)else:res = requests.post(url=url,data=data)return res.json()def get_main(self,url,data=None,header=None):res = Noneif header !=None:res = requests.get(url=url,data=data,headers=header,verify=False)else:res = requests.get(url=url,data=data,verify=False)return res.json()def run_main(self,method,url,data=None,header=None):res = Noneif method == 'Post':res = self.post_main(url,data,header)else:res = self.get_main(url,data,header)return json.dumps(res,ensure_ascii=False,sort_keys=True,indent=2)

3.2 封装mock(mock.py)

from mock import mock
#模拟mock 封装
def mock_test(mock_method,request_data,url,method,response_data):mock_method = mock.Mock(return_value=response_data)res = mock_method(url,method,request_data)return res

四、数据操作包 operation_data

4.1 获取excel单元格中的内容(get_data.py)

#coding:utf-8
from tool.operation_excel import OperationExcel
import data_config
from tool.operation_json import OperetionJson
from tool.connect_db import OperationMysql
class GetData:def __init__(self):self.opera_excel = OperationExcel()#去获取excel行数,就是case的个数def get_case_lines(self):return self.opera_excel.get_lines()#获取是否执行def get_is_run(self,row):flag = Nonecol = int(data_config.get_run())run_model = self.opera_excel.get_cell_value(row,col)if run_model == 'yes':flag = Trueelse:flag = Falsereturn flag#是否携带headerdef is_header(self,row):col = int(data_config.get_header())header = self.opera_excel.get_cell_value(row,col)if header != '':return headerelse:return None#获取请求方式def get_request_method(self,row):col = int(data_config.get_run_way())request_method = self.opera_excel.get_cell_value(row,col)return request_method#获取urldef get_request_url(self,row):col = int(data_config.get_url())url = self.opera_excel.get_cell_value(row,col)return url#获取请求数据def get_request_data(self,row):col = int(data_config.get_data())data = self.opera_excel.get_cell_value(row,col)if data == '':return Nonereturn data#通过获取关键字拿到data数据def get_data_for_json(self,row):opera_json = OperetionJson()request_data = opera_json.get_data(self.get_request_data(row))return request_data#获取预期结果def get_expcet_data(self,row):col = int(data_config.get_expect())expect = self.opera_excel.get_cell_value(row,col)if expect == '':return Nonereturn expect#通过sql获取预期结果def get_expcet_data_for_mysql(self,row):op_mysql = OperationMysql()sql = self.get_expcet_data(row)res = op_mysql.search_one(sql)return res.decode('unicode-escape')def write_result(self,row,value):col = int(data_config.get_result())self.opera_excel.write_value(row,col,value)#获取依赖数据的keydef get_depend_key(self,row):col = int(data_config.get_data_depend())depent_key = self.opera_excel.get_cell_value(row,col)if depent_key == "":return Noneelse:return depent_key#判断是否有case依赖def is_depend(self,row):col = int(data_config.get_case_depend())depend_case_id = self.opera_excel.get_cell_value(row,col)if depend_case_id == "":return Noneelse:return depend_case_id#获取数据依赖字段def get_depend_field(self,row):col = int(data_config.get_field_depend())data = self.opera_excel.get_cell_value(row,col)if data == "":return Noneelse:return data

4.2 获取excel中每个列(data_config.py)

#coding:utf-8
class global_var:#case_idId = '0'request_name = '1'url = '2'run = '3'request_way = '4'header = '5'case_depend = '6'data_depend = '7'field_depend = '8'data = '9'expect = '10'result = '11'
#获取caseid
def get_id():return global_var.Id#获取url
def get_url():return global_var.urldef get_run():return global_var.rundef get_run_way():return global_var.request_waydef get_header():return global_var.headerdef get_case_depend():return global_var.case_dependdef get_data_depend():return global_var.data_dependdef get_field_depend():return global_var.field_dependdef get_data():return global_var.datadef get_expect():return global_var.expectdef get_result():return global_var.resultdef get_header_value():return global_var.header

4.3 解决数据依赖(dependent.py )

#coding:utf-8
import sys
import json
sys.path.append('C:/Users/lxz/Desktop/InterFace_JIA')
from tool.operation_excel import OperationExcel
from base.runmethod import RunMethod
from operation_data.get_data import GetData
from jsonpath_rw import jsonpath,parse
class DependdentData:def __init__(self,case_id):self.case_id = case_idself.opera_excel = OperationExcel()self.data = GetData()#通过case_id去获取该case_id的整行数据def get_case_line_data(self):rows_data = self.opera_excel.get_rows_data(self.case_id)return rows_data#执行依赖测试,获取结果def run_dependent(self):run_method = RunMethod()row_num  = self.opera_excel.get_row_num(self.case_id)request_data = self.data.get_data_for_json(row_num)#header = self.data.is_header(row_num)method = self.data.get_request_method(row_num)url = self.data.get_request_url(row_num)res = run_method.run_main(method,url,request_data)return json.loads(res)#根据依赖的key去获取执行依赖测试case的响应,然后返回def get_data_for_key(self,row):depend_data = self.data.get_depend_key(row)response_data = self.run_dependent()json_exe = parse(depend_data)madle = json_exe.find(response_data)return [math.value for math in madle][0]if __name__ == '__main__':order = {"data": {"_input_charset": "utf-8","body": "京东订单-1710141907182334","it_b_pay": "1d","notify_url": "http://order.imooc.com/pay/notifyalipay","out_trade_no": "1710141907182334","partner": "2088002966755334","payment_type": "1","seller_id": "yangyan01@tcl.com","service": "mobile.securitypay.pay","sign": "kZBV53KuiUf5HIrVLBCcBpWDg%2FnzO%2BtyEnBqgVYwwBtDU66Xk8VQUTbVOqDjrNymCupkVhlI%2BkFZq1jOr8C554KsZ7Gk7orC9dDbQlpr%2BaMmdjO30JBgjqjj4mmM%2Flphy9Xwr0Xrv46uSkDKdlQqLDdGAOP7YwOM2dSLyUQX%2Bo4%3D", "sign_type": "RSA","string": "_input_charset=utf-8&body=京东订单-1710141907182334&it_b_pay=1d&notify_url=http://order.imooc.com/pay/notifyalipay&out_trade_no=1710141907182334&partner=2088002966755334&payment_type=1&seller_id=yangyan01@tcl.com&service=mobile.securitypay.pay&subject=京东订单-1710141907182334&total_fee=299&sign=kZBV53KuiUf5HIrVLBCcBpWDg%2FnzO%2BtyEnBqgVYwwBtDU66Xk8VQUTbVOqDjrNymCupkVhlI%2BkFZq1jOr8C554KsZ7Gk7orC9dDbQlpr%2BaMmdjO30JBgjqjj4mmM%2Flphy9Xwr0Xrv46uSkDKdlQqLDdGAOP7YwOM2dSLyUQX%2Bo4%3D&sign_type=RSA", "subject": "京东订单-1710141907182334","total_fee": 299},"errorCode": 1000,"errorDesc": "成功","status": 1,"timestamp": 1507979239100}res = "data.out_trade_no"json_exe = parse(res)madle = json_exe.find(order)print [math.value for math in madle][0]

五、工具类包 tool

5.1 操作excel (operation_excel.py)

#coding:utf-8
import xlrd
from xlutils.copy import copy
class OperationExcel:def __init__(self,file_name=None,sheet_id=None):if file_name:self.file_name = file_nameself.sheet_id = sheet_id    else:self.file_name = '../dataconfig/case1.xls'self.sheet_id = 0self.data = self.get_data()#获取sheets的内容def get_data(self):data = xlrd.open_workbook(self.file_name)tables = data.sheets()[self.sheet_id]return tables#获取单元格的行数def get_lines(self):tables = self.datareturn tables.nrows#获取某一个单元格的内容def get_cell_value(self,row,col):return self.data.cell_value(row,col)#写入数据def write_value(self,row,col,value):'''写入excel数据row,col,value'''read_data = xlrd.open_workbook(self.file_name)write_data = copy(read_data)sheet_data = write_data.get_sheet(0)sheet_data.write(row,col,value)write_data.save(self.file_name)#根据对应的caseid 找到对应行的内容def get_rows_data(self,case_id):row_num = self.get_row_num(case_id)rows_data = self.get_row_values(row_num)return rows_data#根据对应的caseid找到对应的行号def get_row_num(self,case_id):num = 0clols_data = self.get_cols_data()for col_data in clols_data:if case_id in col_data:return numnum = num+1#根据行号,找到该行的内容def get_row_values(self,row):tables = self.datarow_data = tables.row_values(row)return row_data#获取某一列的内容def get_cols_data(self,col_id=None):if col_id != None:cols = self.data.col_values(col_id)else:cols = self.data.col_values(0)return colsif __name__ == '__main__':opers = OperationExcel()print opers.get_cell_value(1,2)

5.2判断字符串包含,判断字典是否相等(common_util.py)

#coding:utf-8
import json
class CommonUtil:def is_contain(self,str_one,str_two):'''判断一个字符串是否再另外一个字符串中str_one:查找的字符串str_two:被查找的字符串'''flag = Noneif isinstance(str_one,unicode):str_one = str_one.encode('unicode-escape').decode('string_escape')return cmp(str_one,str_two)if str_one in str_two:flag = Trueelse:flag = Falsereturn flagdef is_equal_dict(self,dict_one,dict_two):'''判断两个字典是否相等'''if isinstance(dict_one,str):dict_one = json.loads(dict_one)if isinstance(dict_two,str):dict_two = json.loads(dict_two)return cmp(dict_one,dict_two)

5.3 操作header(operation_herder.py)

# coding:utf-8
import requests
import json
from operation_json import OperetionJsonclass OperationHeader:def __init__(self ,response):self.response = json.loads(response)def get_response_url(self):'''获取登录返回的token的url'''url = self.response['data']['url'][0]return urldef get_cookie(self):'''获取cookie的jar文件'''url = self.get_response_url( ) +"&callback=jQuery21008240514814031887_1508666806688&_=1508666806689"cookie = requests.get(url).cookiesreturn cookiedef write_cookie(self):cookie = requests.utils.dict_from_cookiejar(self.get_cookie())op_json = OperetionJson()op_json.write_data(cookie)if __name__ == '__main__':url = "http://www.jd.com/passport/user/login"data = {"username" :"18513199586","password" :"111111","verify" :"","referer" :"https://www.jd.com"}res = json.dumps(requests.post(url ,data).json())op_header = OperationHeader(res)op_header.write_cookie()

5.4 操作json文件(operation_json.py)

#coding:utf-8
import json
class OperetionJson:def __init__(self,file_path=None):if file_path  == None:self.file_path = '../dataconfig/user.json'else:self.file_path = file_pathself.data = self.read_data()#读取json文件def read_data(self):with open(self.file_path) as fp:data = json.load(fp)return data#根据关键字获取数据def get_data(self,id):print type(self.data)return self.data[id]#写jsondef write_data(self,data):with open('../dataconfig/cookie.json','w') as fp:fp.write(json.dumps(data))if __name__ == '__main__':opjson = OperetionJson()print opjson.get_data('shop')

5.5 操作数据库(connect_db.py)

 #coding:utf-8
import MySQLdb.cursors
import json
class OperationMysql:def __init__(self):self.conn = MySQLdb.connect(host='localhost',port=3306,user='root',passwd='123456',db='le_study',charset='utf8',cursorclass=MySQLdb.cursors.DictCursor)self.cur = self.conn.cursor()#查询一条数据def search_one(self,sql):self.cur.execute(sql)result = self.cur.fetchone()result = json.dumps(result)return resultif __name__ == '__main__':op_mysql = OperationMysql()res = op_mysql.search_one("select * from web_user where Name='ailiailan'")print res

5.6 发送报告邮件(send_email.py)

#coding:utf-8
import smtplib
from email.mime.text import MIMEText
class SendEmail:global send_userglobal email_hostglobal passwordemail_host = "smtp.163.com"send_user = "jiaxiaonan666@163.com"password = "jia_668"def send_mail(self,user_list,sub,content):user = "jiaxiaonan"+"<"+send_user+">"message = MIMEText(content,_subtype='plain',_charset='utf-8')message['Subject'] = submessage['From'] = usermessage['To'] = ";".join(user_list)server = smtplib.SMTP()server.connect(email_host)server.login(send_user,password)server.sendmail(user,user_list,message.as_string())server.close()def send_main(self,pass_list,fail_list):pass_num = float(len(pass_list))fail_num = float(len(fail_list))count_num = pass_num+fail_num#90%pass_result = "%.2f%%" %(pass_num/count_num*100)fail_result = "%.2f%%" %(fail_num/count_num*100)user_list = ['609037724@qq.com']sub = "接口自动化测试报告"content = "此次一共运行接口个数为%s个,通过个数为%s个,失败个数为%s,通过率为%s,失败率为%s" %(count_num,pass_num,fail_num,pass_result,fail_result )self.send_mail(user_list,sub,content)if __name__ == '__main__':sen = SendEmail()sen.send_main([1,2,3,4],[2,3,4,5,6,7])

六、主函数

run_test.py

#coding:utf-8
import sys
sys.path.append("C:/Users/lxz/Desktop/InterFace_JIA")
from base.runmethod import RunMethod
from operation_data.get_data import GetData
from tool.common_util import CommonUtil
from operation_data.dependent_data import DependdentData
from tool.send_email import SendEmail
from tool.operation_header import OperationHeader
from tool.operation_json import OperetionJson
class RunTest:def __init__(self):self.run_method = RunMethod()self.data = GetData()self.com_util = CommonUtil()self.send_mai = SendEmail()#程序执行的def go_on_run(self):res = Nonepass_count = []fail_count = []#10  0,1,2,3rows_count = self.data.get_case_lines()for i in range(1,rows_count):is_run = self.data.get_is_run(i)if is_run:url = self.data.get_request_url(i)method = self.data.get_request_method(i)request_data = self.data.get_data_for_json(i)expect = self.data.get_expcet_data_for_mysql(i)header = self.data.is_header(i)depend_case = self.data.is_depend(i)if depend_case != None:self.depend_data = DependdentData(depend_case)#获取的依赖响应数据depend_response_data = self.depend_data.get_data_for_key(i)#获取依赖的keydepend_key = self.data.get_depend_field(i)request_data[depend_key] = depend_response_dataif header == 'write':res = self.run_method.run_main(method,url,request_data)op_header = OperationHeader(res)op_header.write_cookie()elif header == 'yes':op_json = OperetionJson('../dataconfig/cookie.json')cookie = op_json.get_data('apsid')cookies = {'apsid':cookie}res = self.run_method.run_main(method,url,request_data,cookies)else:res = self.run_method.run_main(method,url,request_data)if self.com_util.is_equal_dict(expect,res) == 0:self.data.write_result(i,'pass')pass_count.append(i)else:self.data.write_result(i,res)fail_count.append(i)self.send_mai.send_main(pass_count,fail_count)#将执行判断封装#def get_cookie_run(self,header):
if __name__ == '__main__':run = RunTest()run.go_on_run()

七、总结

Python+requests+unittest+excel搭建接口自动化测试框架是一种流行的方法,可以帮助测试人员以有效和可靠的方式管理API测试用例。以下是该框架的一些主要优点和总结:

  • 灵活和易于扩展:Python作为一种灵活的编程语言,可以轻松地扩展和定制测试框架,以满足特定项目或团队的需求。
  • 高效和可维护:使用Python+requests+unittest+excel框架进行API测试可以大大提高测试效率,并减少手动测试所需的时间和成本。测试脚本易于维护和更新。
  • 完整的测试覆盖:通过使用Excel表格来管理测试数据,可以确保所有测试用例都具有完整的测试覆盖,从而检测更多潜在的错误和缺陷。
  • 生成详细的测试报告:该框架可以生成详细的HTML测试报告,包括测试结果、错误信息和性能统计数据等,以便于分析和评估接口质量。
  • 适用于不同的测试场景:无论您是在测试Web应用程序、客户端应用程序还是其他类型的应用程序,都可以使用此框架进行API测试。它适用于各种测试场景,包括功能测试、性能测试和负载测试等。

总之,Python+requests+unittest+excel搭建接口自动化测试框架是一种功能强大且易于使用的API测试方法。通过这个框架,可以轻松管理和运行大量测试用例,并生成有用的测试报告,以便于分析和评估接口质量。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

同时,在这我为大家准备了一份软件测试视频教程(含面试、接口、自动化、性能测试等),就在下方,需要的可以直接去观看,也可以直接【点击文末小卡片免费领取资料文档】

软件测试视频教程观看处:

【2024最新版】Python自动化测试15天从入门到精通,10个项目实战,允许白嫖。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/236980.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

道路拆除的题解

目录 原题描述&#xff1a; 题目描述 输入格式 输出格式 样例 #1 样例输入 #1 样例输出 #1 样例 #2 样例输入 #2 样例输出 #2 提示 题目大意&#xff1a; 主要思路&#xff1a; 至于dis怎么求&#xff1f; 代码code&#xff1a; 原题描述&#xff1a; 题目描述 …

爬虫之牛刀小试(四):爬取B站番剧的简介

今天爬取的是b站。 如何爬取b站中的番剧呢&#xff1f; 首先我们来到番剧索引中&#xff0c;随便点开一部动漫&#xff0c;检查代码。 每个作品对应一个链接: https://www.bilibili.com/bangumi/play/ss…&#xff08;ss后面的数字称为ss号&#xff09; 发现关于动漫的信息…

uniapp中uview组件库丰富的CountTo 数字滚动使用方法

目录 #平台差异说明 #基本使用 #设置滚动相关参数 #是否显示小数位 #千分位分隔符 #滚动执行的时机 #API #Props #Methods #Event 该组件一般用于需要滚动数字到某一个值的场景&#xff0c;目标要求是一个递增的值。 注意 如果给组件的父元素设置text-align: cente…

腾讯云免费服务器怎么申请?腾讯云免费服务器申请难吗?

腾讯云免费服务器申请入口 https://curl.qcloud.com/FJhqoVDP 免费服务器可选轻量应用服务器和云服务器CVM&#xff0c;轻量配置可选2核2G3M、2核8G7M和4核8G12M&#xff0c;CVM云服务器可选2核2G3M和2核4G3M配置&#xff0c;腾讯云服务器网txyfwq.com分享2024年最新腾讯云免费…

如何使用 Helm 在 K8s 上集成 Prometheus 和 Grafana|Part 2

在 Part 1 中&#xff0c;我们一起了解了什么是 Prometheus 和 Grafana&#xff0c;以及使用这些工具的前提条件和优势。在本部分&#xff0c;将继续带您学习如何安装 Helm 以及如何使用 Prometheus Helm Charts。 开始使用 Helm 和 Helm Chart ArtifactHub 为 Helm Chart 提供…

uniapp 开发小程序的时候使用自定义 tabbar 时出现切换页面闪烁的情况

问题&#xff1a;在使用自定义组件的时候可以看到页面切换明显的闪烁, 这种体验是很不好的, 当然最好的方式就是使用原生导航栏, 不要搞花里胡哨的东西。 来看下体验不好的效果 优化调整 先说思路&#xff0c;就是仍然设置原生 tabbar, 在应用启动的时候主动隐藏原生 tabba…

vue3hooks的使用

在 Vue 3 中&#xff0c;hooks 是用于封装组件逻辑的方法&#xff0c;类似于 Vue 2 中的 mixins。 使用 Hooks 可以提高代码的可维护性、可读性、可复用性和可测试性&#xff0c;降低代码之间的耦合度&#xff0c;使得组件的状态更加可控和可预测。 要使用 hooks&#xff0c;…

半小时实现GPT纯血鸿蒙版

仅需半小时&#xff0c;即可实现纯血鸿蒙版本的ChatGPT&#xff01; 废话少说&#xff0c;先看效果图&#xff1a; 如上图所示&#xff0c;这个小Demo实现了AI智能问答。靠右加粗的文本是用户点击底部提交按钮后出现的&#xff1b;后面靠左对齐的普通文本是来自AI的回答内容。当…

Blazor中使用impress.js

impress.js是什么&#xff1f; 你想在浏览器中做PPT吗&#xff1f;比如在做某些类似于PPT自动翻页&#xff0c;局部放大之类&#xff0c;炫酷无比。 官方示例直接放到Blazor中是不可用的。几经尝试&#xff0c;用以下方法可以实现。 &#xff08;写文不易&#xff0c;请点赞、…

C语言宏定义小技巧

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、定义一年多少秒&#xff08;除闰年&#xff09;举例运行结果出现的问题原因 二、定义整型数据要避免的坑举例运行结果原因解决方法 三 、未完待续 前言 提…

统计学-R语言-4.3

文章目录 前言直方图茎叶图箱线图练习 前言 本篇介绍的是数值型数据怎么进行数据可视化&#xff0c;本篇介绍的有直方图、茎叶图、箱线图。 直方图 直方图&#xff08;Histogram&#xff09;用于描述连续型变量的频数分布&#xff0c;实际应用中常用于考察变量的分布是否对称…

通过代理连接sftp

通过nginx代理连接sftp 1.问题描述2.代码实现3.nginx配置3.1 创建sftp.stream文件3.2 修改nginx配置 4.重启nginx生效 1.问题描述 问题是这样的。我们现在需要在微服务所在内网的A机器连接到外网的sftp&#xff0c;但是网络又不能直接到达。然后A机器到B机器是通过的&#xff…

怎么找微信服务器的IP地址

首先&#xff0c;让微信客户端在PC端运行&#xff0c;在任务管理器->详细信息中&#xff0c;找到WeChat.exe的进程&#xff0c;找到PID 就是微信进程的ID号&#xff0c;如下图所示&#xff1a; 打开一个命令行窗口&#xff0c;cmd或者powershell窗口都可以&#xff0c;输入…

使用FreeBASIC设计8051单片机汇编编译器

在STC论坛上看到有人用C语言实现8051汇编编译器&#xff08;源码&#xff09;&#xff0c;好奇下&#xff0c;试着用FB写了一下。 基本原理就是通过分析汇编文件然后转换为机器码。以下是51汇编与机器码对应的表格&#xff08;数据来自网络&#xff0c;如果发现有误请联系QQ149…

Qt6安装教程

由于QT在5.14版本后不再有离线安装版本&#xff0c;均需要通过在线安装 1.下载exe安装包 打开Open Source Development | Open Source License | Qt&#xff0c;往下拉&#xff0c;找到红框所示的按钮 点进去后点击Download即可 2 安装 下载完成后可得到qt-unified-windows…

通过 CMake 制作库文件 静态库 和 动态库

hehedalinux:~/Linux/loveDBTeacher-v2$ tree . ├── CMakeLists.txt ├── include │ └── head.h ├── main.c └── src├── add.c├── div.c├── mult.c└── sub.c CMake Calc 项目 在这里有add.c,div.c,mult.c,sub.c,main.c,head.h 二、生成静态库 …

【抓包教程】BurpSuite联动雷电模拟器——安卓高版本抓包移动应用教程

前言 近期找到了最适合自己的高版本安卓版本移动应用抓HTTP协议数据包教程&#xff0c;解决了安卓低版本的问题&#xff0c;同时用最简单的办法抓到https的数据包&#xff0c;特此进行文字记录和视频记录。 前期准备 抓包工具&#xff1a;BurpSuite安卓模拟器&#xff1a;雷…

Docker之数据卷的使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《Docker之数据卷的使用》。&#x1f3af;&#x…

SSM框架学习笔记04 | SpringMVC

文章目录 一、SpringMVC简介二、 请求与响应1. 请求映射路径2. get请求与post请求3. 响应 二、REST风格1.简介 三、 SSM整合四、拦截器1. 定义拦截器2.配置拦截器3.拦截器执行顺序4.拦截器参数5.多个连接器工作流程分析6.拦截器链的运行顺序 一、SpringMVC简介 SpringMVC技术与…

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例3-1 CSS3过渡

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>CSS3 过渡</title> <style> /*显示*/ .box {width: 100px;height: 100px;background-color: #eee;/*透明度*/opacity: 1;/*过渡*/transition: 3s; } /…