Day21Web开发

作者: 林清猫耳 | 来源:发表于2018-04-15 17:22 被阅读98次

    HTTP协议简介

    在Web应用中,服务器把网页传给浏览器,实际上就是把网页的HTML代码发送给浏览器,让浏览器显示出来。而浏览器和服务器之间的传输协议是HTTP,所以:

    • HTML是一种用来定义网页的文本,会HTML,就可以编写网页;
    • HTTP是在网络上传输HTML的协议,用于浏览器和服务器的通信。

    HTTP请求

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

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

    路径:/full/url/path;

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

    以及其他相关的Header;

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

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

    响应代码:200表示成功,3xx表示重定向,4xx表示客户端发送的请求有错误,5xx表示服务器端处理时发生了错误;

    响应类型:由Content-Type指定;

    以及其他相关的Header;

    通常服务器的HTTP响应会携带内容,也就是有一个Body,包含响应的内容,网页的HTML源码就在Body中。

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

    Web采用的HTTP协议采用了非常简单的请求-响应模式,从而大大简化了开发。当我们编写一个页面时,我们只需要在HTTP请求中把HTML发送出去,不需要考虑如何附带图片、视频等,浏览器如果需要请求图片和视频,它会发送另一个HTTP请求,因此,一个HTTP请求只处理一个资源。

    HTTP协议同时具备极强的扩展性,虽然浏览器请求的是http://www.sina.com.cn/的首页,但是新浪在HTML中可以链入其他服务器的资源,比如<img src="http://i1.sinaimg.cn/home/2013/1008/U8455P30DT20131008135420.png">,从而将请求压力分散到各个服务器上,并且,一个站点可以链接到其他站点,无数个站点互相链接起来,就形成了World Wide Web,简称WWW。

    HTTP格式

    每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。

    HTTP协议是一种文本协议,所以,它的格式也非常简单。HTTP GET请求的格式:

    GET /path HTTP/1.1
    Header1: Value1
    Header2: Value2
    Header3: Value3
    

    每个Header一行一个,换行符是\r\n。

    HTTP POST请求的格式:

    POST /path HTTP/1.1
    Header1: Value1
    Header2: Value2
    Header3: Value3
    
    body data goes here...
    

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

    HTTP响应的格式:

    200 OK
    Header1: Value1
    Header2: Value2
    Header3: Value3
    
    body data goes here...
    

    HTTP响应如果包含body,也是通过\r\n\r\n来分隔的。请再次注意,Body的数据类型由Content-Type头来确定,如果是网页,Body就是文本,如果是图片,Body就是图片的二进制数据。

    当存在Content-Encoding时,Body数据是被压缩的,最常见的压缩方式是gzip,所以,看到Content-Encoding: gzip时,需要将Body数据先解压缩,才能得到真正的数据。压缩的目的在于减少Body的大小,加快网络传输。

    HTML简介

    最简单的HTML:

    <html>
    <head>
      <title>Hello</title>
    </head>
    <body>
      <h1>Hello, world!</h1>
    </body>
    </html>
    

    可以用文本编辑器编写HTML,然后保存为hello.html,双击或者把文件拖到浏览器中,就可以看到效果:

    Hello, world!
    HTML文档就是一系列的Tag组成,最外层的Tag是<html>。规范的HTML也包含<head>...</head><body>...</body>(注意不要和HTTP的Header、Body搞混了),由于HTML是富文档模型,所以,还有一系列的Tag用来表示链接、图片、表格、表单等等。

    CSS简介

    CSS是Cascading Style Sheets(层叠样式表)的简称,CSS用来控制HTML里的所有元素如何展现,比如,给标题元素<h1>加一个样式,变成48号字体,灰色,带阴影:

    <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>
    
    CSS

    WSGI接口

    了解了HTTP协议和HTML文档,我们其实就明白了一个Web应用的本质就是:

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

    WSGI:Web Server Gateway Interface
    WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求。我们来看一个最简单的Web版本的“Hello, web!”:

    def application(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        return [b'<h1>Hello, web!</h1>']
    

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

    • environ:一个包含所有HTTP请求信息的dict对象;
    • start_response:一个发送HTTP响应的函数。
      application()函数中,调用:
    start_response('200 OK', [('Content-Type', 'text/html')])
    

    就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每个Header用一个包含两个strtuple表示。
    通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。
    然后,函数的返回值b'<h1>Hello, web!</h1>'将作为HTTP响应的Body发送给浏览器。
    有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,通过start_response()发送Header,最后返回Body。
    整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,我们只负责在更高层次上考虑如何响应请求就可以了。
    application()函数必须由WSGI服务器来调用。


    使用Web框架

    由于用Python开发一个Web框架十分容易,所以Python有上百个开源的Web框架。这里我们先不讨论各种Web框架的优缺点,直接选择一个比较流行的Web框架——Flask来使用。
    写一个app.py,处理3个URL,分别是:

    • GET /:首页,返回Home
    • GET /signin:登录页,显示登录表单;
    • POST /signin:处理登录表单,显示登录结果。

    同一个URL/signin分别有GET和POST两种请求,映射到两个处理函数中。
    Flask通过Python的装饰器在内部自动地把URL和函数给关联起来,所以,我们写出来的代码就像这样:

    from flask import Flask
    from flask import request
    
    app = Flask(__name__)
    
    @app.route('/', methods=['GET', 'POST'])
    def home():
        return '<h1>Home</h1>'
    
    @app.route('/signin', methods=['GET'])
    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'])
    def signin():
        # 需要从request对象读取表单内容:
        if request.form['username']=='admin' and request.form['password']=='password':
            return '<h3>Hello, admin!</h3>'
        return '<h3>Bad username or password.</h3>'
    
    if __name__ == '__main__':
        app.run()
    

    运行python app.py,Flask自带的Server在端口5000上监听:

    * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
    

    打开浏览器,输入首页地址http://localhost:5000/

    首页
    再在浏览器地址栏输入http://localhost:5000/signin,会显示登录表单:
    登录表单
    输入预设的用户名admin和口令password,登录成功:
    登陆成功
    输入其他错误的用户名和口令,登录失败:
    登录失败

    实际的Web App应该拿到用户名和口令后,去数据库查询再比对,来判断用户是否能登录成功。

    除了Flask,常见的Python Web框架还有:

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

    使用模板

    俗话说得好,不懂前端的Python工程师不是好的产品经理。有Web开发经验的同学都明白,Web App最复杂的部分就在HTML页面。HTML不仅要正确,还要通过CSS美化,再加上复杂的JavaScript脚本来实现各种交互和动画效果。总之,生成HTML页面的难度很大。

    由于在Python代码里拼字符串是不现实的,所以,模板技术出现了。
    使用模板,我们需要预先准备一个HTML文档,这个HTML文档不是普通的HTML,而是嵌入了一些变量和指令,然后,根据我们传入的数据,替换后,得到最终的HTML,发送给用户:

    eg
    这就是传说中的MVC:Model-View-Controller,中文名“模型-视图-控制器”。
    Python处理URL的函数就是C:Controller,Controller负责业务逻辑,比如检查用户名是否存在,取出用户信息等等;
    包含变量{{ name }}的模板就是V:View,View负责显示逻辑,通过简单地替换一些变量,View最终输出的就是用户看到的HTML。
    { 'name': 'Michael' }
    

    只是因为Python支持关键字参数,很多Web框架允许传入关键字参数,然后,在框架内部组装出一个dict作为Model。

    现在,我们把上次直接输出字符串作为HTML的例子用高端大气上档次的MVC模式改写一下:

    from flask import Flask, request, render_template
    
    app = Flask(__name__)
    
    @app.route('/', methods=['GET', 'POST'])
    def home():
        return render_template('home.html')
    
    @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-ok.html', username=username)
        return render_template('form.html', message='Bad username or password', username=username)
    
    if __name__ == '__main__':
        app.run()
    

    Flask通过render_template()函数来实现模板的渲染。和Web框架类似,Python的模板也有很多种。Flask默认支持的模板是jinja2,编写jinja2模板:

    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 %}
      <p style="color:red">{{ message }}</p>
      {% endif %}
      <form action="/signin" method="post">
        <legend>Please sign in:</legend>
        <p><input name="username" placeholder="Username" value="{{ username }}"></p>
        <p><input name="password" placeholder="Password" type="password"></p>
        <p><button type="submit">Sign In</button></p>
      </form>
    </body>
    </html>
    

    signin-ok.html

    登录成功的模板:

    <html>
    <head>
      <title>Welcome, {{ username }}</title>
    </head>
    <body>
      <p>Welcome, {{ username }}!</p>
    </body>
    </html>
    
    目录
    启动python app.py,看看使用模板的页面效果:
    首页
    登陆失败
    登陆成功

    通过MVC,我们在Python代码中处理M:Model和C:Controller,而V:View是通过模板处理的,这样,我们就成功地把Python代码和HTML代码最大限度地分离了。
    使用模板的另一大好处是,模板改起来很方便,而且,改完保存后,刷新浏览器就能看到最新的效果。
    在Jinja2模板中,我们用{{ name }}表示一个需要替换的变量。很多时候,还需要循环、条件判断等指令语句,在Jinja2中,用{% ... %}表示指令。

    {% for i in page_list %}
        <a href="/page/{{ i }}">{{ i }}</a>
    {% endfor %}
    

    如果page_list是一个list:[1, 2, 3, 4, 5],上面的模板将输出5个超链接。
    除了Jinja2,常见的模板还有:

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

    相关文章

      网友评论

        本文标题:Day21Web开发

        本文链接:https://www.haomeiwen.com/subject/hmsdkftx.html