• python3基础知识复习 -- web开发入门


    Web开发

    • BS架构:Browser/Server模式, 客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。Web应用程序的修改和升级后,客户端马上就可以获得最新的变更,优于CS架构(client-server)
    • Web开发发展阶段:
      • 静态web: 早期使用,txt编写HTML
      • CGI : Common Gateway Interface,用C/C++编写,处理用户发送的动态数据作交互
      • ASP(MS)/JSP(Java)/PHP:Web应用特点是修改频繁,C/C++低级语言不适合,脚本语言更适合
      • MVC: Mode-View-Controller 简化web开发,解决直接用脚本语言嵌入HTML导致的可维护性差的问题, ASP --> ASP.Net

    HTTP协议简介

    HTTP请求

    总结一下HTTP请求的流程:

    步骤1:浏览器首先向服务器发送HTTP请求,请求包括:

    ​ 方法:GET还是POSTGET仅请求资源,POST会附带用户数据;

    ​ 路径:/full_url_path, /表示首页;

    ​ 域名:由Host头指定:Host: www.sina.com.cn

    ​ 以及其他相关的Header;

    ​ 如果是POST,那么请求还包括一个Body,包含用户数据。

    sina.web

    步骤2:服务器向浏览器返回HTTP响应,响应包括:

    ​ 响应代码:200表示成功,3xx表示重定向,4xx表示客户端发送的请求有错误(404 not found 网页不存在),5xx表示服务器端处理时发生了错误(500 internal server error 服务器内部错误);

    ​ 响应类型:由Content-Type指定,例如:Content-Type: text/html;charset=utf-8表示响应类型是HTML文本,并且编码是UTF-8Content-Type: image/jpeg表示响应类型是JPEG格式的图片;

    ​ 以及其他相关的Header;

    ​ 通常服务器的HTTP响应会携带内容,也就是有一个Body,包含响应的内容,网页的HTML源码就在Body中。
    在这里插入图片描述
    在这里插入图片描述

    步骤3:如果浏览器还需要继续向服务器请求其他资源,比如图片,就再次发出HTTP请求,重复步骤1、2。

    http协议的格式

    http协议分成两个大的部分,一个是请求,一个是相应。无论是请求还是相应都包含两个部分,一个是header,另外一个是body。(body是可选 的)

    在这里插入图片描述

    HTTP GET请求的格式:

    GET /path HTTP/1.1
    Header1: Value1
    Header2: Value2
    Header3: Value3
    
    • 1
    • 2
    • 3
    • 4

    HTTP POST请求的格式:

    注意:当遇到连续两个\r\n时,Header部分结束,后面的数据全部是Body。

    POST /path HTTP/1.1
    Header1: Value1
    Header2: Value2
    Header3: Value3
     
    body data goes here...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    HTTP响应的格式:

    再次注意:HTTP响应如果包含body,也是通过\r\n\r\n来分隔的。

    200 OK
    Header1: Value1
    Header2: Value2
    Header3: Value3
     
    body data goes here...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    请再次注意,Body的数据类型由Content-Type头来确定,如果是网页,Body就是文本,如果是图片,Body就是图片的二进制数据。

    当存在Content-Encoding时,Body数据是被压缩的(gzip), 下解压缩才能有数据。

    对于http 请求s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')

    在这里插入图片描述
    在这里插入图片描述

    • PS:
      HTTP之状态码

    状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别:

    1xx:指示信息–表示请求已接收,继续处理

    2xx:成功–表示请求已被成功接收、理解、接受

    3xx:重定向–要完成请求必须进行更进一步的操作

    4xx:客户端错误–请求有语法错误或请求无法实现

    5xx:服务器端错误–服务器未能实现合法的请求

    • PPS:
      常见状态码:

    200 OK //客户端请求成功

    400 Bad Request //客户端请求有语法错误,不能被服务器所理解

    401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用

    403 Forbidden //服务器收到请求,但是拒绝提供服务

    404 Not Found //请求资源不存在,eg:输入了错误的URL

    500 Internal Server Error //服务器发生不可预期的错误

    503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常

    NOTE1: 关于HTTP的request请求,参考【HTTP】超简洁的实例 ——关于HTTP协议分析_bandaoyu的博客-CSDN博客_http例程

    NOTE2: Book, HTTP权威指南 + 图解HTTP

    ·

    HTML协议

    • HTML文档就是一系列的Tag组成,最外层的Tag是<html>。规范的HTML也包含<head>...</head><body>...</body>,由于HTML是富文档模型,还有一系列的Tag用来表示链接、图片、表格、表单等等。

    • CSS是Cascading Style Sheets(层叠样式表)的简称,CSS用来控制HTML里的所有元素如何展现

      <html>
      <head>
        <title>Hello</title>
        <style>
          h1 {
            color: #333333;
            font-size: 48px;
            text-shadow: 3px 3px 3px #666666;
          }
        </style>
      </head>
      <body>
        <h1>Hello, world!</h1>
      </body>
      </html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

    在这里插入图片描述

    • JavaScript - 和java没关系, 增加HTML的交互性,可以内嵌或者外部链接到HTML, 脚本语言,不需要编译

      <html>
      <head>
      	<title>Hello </title>
      	<style>
      		h1 {
      		color: #333333;
      		font-size: 48px;
      		text-shadow: 3px 3px 3px #666666
      		}
      	</style>
      	<script> # 内嵌,点击字体更换颜色
      		function change() {
      			document.getElementsByTagName('h1')[0].style.color = '#ff0000';
      		}
      	</script>
      </head>
      <body>
      	<h1 onclick="change()">Hello, The Shadow!</h1>
      </body>
      </html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20

    在这里插入图片描述

    • 熟练学习HTML、CSS和JavaScript是必须的:

      http://www.w3schools.com/

      以及一个对应的中文版本:

      http://www.w3school.com.cn/

    WSGI接口

    一个Web应用的本质就是:

    1. 浏览器发送一个HTTP请求;
    2. 服务器收到请求,生成一个HTML文档;
    3. 服务器把HTML文档作为HTTP响应的Body发送给浏览器;
    4. 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。

    静态服务器 Apache、Nginx、Lighttpd就是做上面的事, 但是如果要生成动态HTML,上面的所有步骤自己实现很麻烦,所有关于HTTP请求,解析,发送响应需要用专门的服务器软件实现, 一个统一接口WSGI:Web Server Gateway Interface解决了这个问题。

    WSGI,也可称作Python Web Server Gateway Interface,开始于2003年,为Python语言定义Web服务器和服务器端程序的通用接口规范。WSGI的接口分为两个:一个是与Web服务器的接口,一个是与服务器端程序的接口;WSGI Server与Web服务器的接口包括uwsgi、fast cgi等。

    在这里插入图片描述

    python内置一个的WSGI服务器的参考实现(完全符合WSGI标准,但是不考虑任何运行效率,仅供开发和测试使用),这个模块叫wsgiref.

    运行WSGI服务

    我们先编写hello.py,实现Web应用程序的WSGI处理函数:

    # hello.py
    
    def application(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')]) # 发送response header
        body = '<h1>Hello, %s!</h1>' % (environ['PATH_INFO'][1:] or 'web') # 提取request中的PATH_INFO信息并传给body,[1:]表示读取第二个字符后面的,因为第一个字符永远是'/'
        return [body.encode('utf-8')] # 返回body(html)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    然后,再编写一个server.py,负责启动WSGI服务器,加载application()函数:

    # server.py
    # 从wsgiref模块导入:
    from wsgiref.simple_server import make_server
    # 导入我们自己编写的application函数:
    from hello import application
    
    # 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
    httpd = make_server('', 8000, application)
    print('Serving HTTP on port 8000...')
    # 开始监听HTTP请求, 需要手动停掉的Ctrl+C
    httpd.serve_forever()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

    • environ:一个包含所有HTTP请求信息的dict对象;
    • start_response:一个发送HTTP响应的函数。只能调用一次因为只能发送一次header
      • 参数1:HTTP响应码
      • 参数2:一组list表示的header, 每个header用2个str组成的tuple
    • 函数的return作为HTTP响应的Body发送给浏览器

    可以看出整个application()函数本身没有涉及到任何解析HTTP的部分,我们只需考虑如何响应请求即可。

    application()函数必须由WSGI服务器来调用,如上面的wsgiref.

    效果:

    在这里插入图片描述
    在这里插入图片描述

    总结: 无论多么复杂的Web应用程序,入口都是一个WSGI处理函数。HTTP请求的所有输入信息都可以通过environ获得,HTTP响应的输出都可以通过start_response()加上函数返回值作为Body。

    但是复杂的Web应用程序,光靠一个WSGI函数来处理还是太底层了,需要再抽象出Web框架,进一步简化Web开发。

    Web框架

    WSGI可以针对每个HTTP请求写出一个函数,但是处理不同的URL(GET/POST/PUT/DELETE)就会很繁琐,而且维护性差。

    Web框架是在WSGI接口之上进一步抽象,让我们专注于用一个函数处理一个URL,至于URL到函数的映射,交给框架做。

    常见的Python Web框架还有:

    • Flask: 最流行的Web框架;
    • Django:全能型Web框架;
    • web.py:一个小巧的Web框架;
    • Bottle:和Flask类似的Web框架;
    • Tornado:Facebook的开源异步Web框架。

    Flask通过Python的装饰器在内部自动地把URL和函数给关联起来。

    eg:

    写一个app.py,处理3个URL,分别是:
    GET /:首页,返回Home;
    GET /signin:登录页,显示登录表单;
    POST /signin:处理登录表单,显示登录结果。
    NOTE: 实际的Web App应该拿到用户名和口令后,去数据库查询再比对,来判断用户是否能登录成功。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    from flask import Flask
    from flask import request
    
    app = Flask(__name__)
    
    @app.route('/', methods=['GET', 'POST']) # GET /:首页
    def home():
        return '<h1>Welcome back Home</h1>'
    
    @app.route('/signin', methods=['GET'])  # GET /signin:登录页
    def signin_form():
        return '''<form action="/signin" method="post">
                <p><input name="username"></p>
                <p><input name="password" type="password"></p>
                <p><button type="submit">Sign in</button></p>
                </form>'''
    
    @app.route('/signin', methods=['POST'])  # POST /signin:处理登录表单
    def signin():
        if request.form['username'] == "admin" and request.form['password'] == "password":
            return "<h3>Hello admin!</h3>"
        else:
            return "<h3>Bad username or password!</h3>"
    
    if __name__ == "__main__":
        app.run(debug=True) # 开启debug模式可以方便差错,production环境不开
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    使用模板 MVC

    模板承包了webapp的HTML的展示,使得我们不用手打这么多HTML代码,使用模板,我们需要预先准备一个HTML文档,里面嵌入了一些变量和指令,然后,根据我们传入的数据,替换后,得到最终的HTML,发送给用户。

    在这里插入图片描述

    这种模式就是Model-View-Controller,中文名“模型-视图-控制器”。

    • controller: python写得处理URL函数负责业务逻辑,比如检测用户名合法性,读取用户信息之类。
    • view: 模板就负责显示逻辑,通过变量替换得到最终的HTML;
    • mode: 用来传变量给给View的,这样View在替换变量的时候,就可以从Model中取出相应的数据。
    • 这样就可以将python代码和HTML代码最大程度分离。

    常用的是jinja2模板,用{{ name }}表示一个需要替换的变量。用{% ... %}表示指令循环、条件判断等指令语句。比如循环输出页码:

    {% for i in page_list %}
        <a href="/page/{{ i }}">{{ i }}</a>
    {% endfor %}
    
    >>> 如果`page_list`是一个list:`[1, 2, 3, 4, 5]`,上面的模板将输出5个超链接。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    除了Jinja2,常见的模板还有:

    • Mako:用<% ... %>${xxx}的一个模板;
    • Cheetah:也是用<% ... %>${xxx}的一个模板;
    • Django:Django是一站式框架,内置一个用{% ... %}{{ xxx }}的模板。

    下面使用jinja2模板改写例子:

    ## app.py ##
    
    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    from flask import Flask
    from flask import request
    from flask import render_template
    
    app = Flask(__name__)
    
    @app.route('/', methods=['GET', 'POST'])
    def home():
        return render_template('home.html') # Flask通过render_template()函数来实现模板的渲染。
    
    @app.route('/signin', methods=['GET'])
    def signin_form():
        return render_template('form.html')
    
    @app.route('/signin', methods=['POST'])
    def signin():
        username = request.form['username']
        password = request.form['password']
        if username == "admin" and password == "password":
            return render_template('signin.html', username = username)
        
        return render_template('form.html', message='Bad username or password!', username = username)
    
    if __name__ == "__main__":
        app.run(debug=True)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    编辑模板如下:

    ## home.html ##
    <html>
    <head>
    	<title>Home</title>
    </head>
    <body>
    	<h1 style="font-style:italic">Home</h1>
    </body>
    </html>
    
    
    ## form.html ##
    <html>
    <head>
    	<title>Please sign in</title>
    </head>
    
    <body>
    	{% if message %}   # 通过是否检测到message来显示username/passwor错误的情况
    	<p style="color:red">{{message}}</p>
    	{% endif %}
    	<form action="/signin" method="post">
    		<p><input name="username" placeholder="Username" value="{{username}}"></p>
    		<p><input name="password" placeholder="Password" type="password"></p>
    		<p><button type="submit">Sing In</button></p>
    	</form>
    </body>
    </html>
    
    ## signin.html ##
    <html>
    <head>
        <title>Welcome, {{username}}</title>
    </head>
    <body>
        <p>Welcome, {{username}}!</p>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    NOTE: 一定要把模板放到正确的templates目录下,templatesapp.py在同级目录下

    在这里插入图片描述
    得到的效果和之前一样:
    login

  • 相关阅读:
    uniapp遇到的问题
    Redis(二)入门:概述与安装
    NKOJP5682果老师炸桥
    子矩阵的前缀和(二维数组)
    把备考高项当成项目:我的信息系统项目管理师项目实施方案
    C++基础算法离散化及区间合并篇
    基于DDR3的串口传图帧缓存系统设计实现
    JS中的错误处理机制
    Java 字节与进制转换
    Origin | 条形图地图绘制及经纬度的拾取
  • 原文地址:https://blog.csdn.net/peanutfish/article/details/125464904