zoukankan      html  css  js  c++  java
  • Web应用

    Web应用

    一、Web应用的组成

    Web应用程序是基于B/S架构的(B:浏览器,S:服务端),B(浏览器)负责向S端发送请求信息,而S端会根据接收到的请求信息返回响应的数据给浏览器。

    S端,由server和application两大部分构成。

    Web应用组成

    二、开发一个Web应用

    无需开发浏览器(本质套接字客户端),只需要开发S端即可,S端的本质就是用套接字实现的

    import socket
    
    
    # server端函数
    def server(ip, port, app):
        sock = socket.socket()
        sock.bind((ip, port))
        sock.listen(5)
        while True:
            conn, addr = sock.accept()
    
            # 1.接收浏览器发来的请求信息
            recv_data = conn.recv(1024)
            # print(recv_data.decode("utf-8"))
    
            # 2.将请求信息交给application
            res = app(recv_data)
    
            # 3.像浏览器返回信息(此处并没有按照HTTP协议返回)
            conn.send(res)
    
            conn.close()
    
    
    # 代表application
    def app(recv_data):
        # 处理业务逻辑
        return b'hello word'
    
    
    if __name__ == '__main__':
        # 在客户端浏览器输入:http://127.0.0.1:8080 会报错
        server('127.0.0.1', 8080, app)
    
    

    目前S端已经可以正常接收浏览器发来的请求消息了,但是浏览器在接收到S端回复的响应消息时却无法正常解析,因为浏览器与S端之间收发消息默认使用的应用层协议是HTTP,浏览器默认回按照HTTP协议规定的格式发消息,而S端也必须按照HTTP协议的格式回消息才行。

    HTTP协议详解连接地址:

    HTTP详解链接

    S端修订版本:处理HTTP协议的请求消息,并按照HTTP协议的格式回复消息

    import socket
    
    
    # server端函数
    def server(ip, port, app):
        sock = socket.socket()
        sock.bind((ip, port))
        sock.listen(5)
        while True:
            conn, addr = sock.accept()
    
            # 1.接收浏览器发来的请求信息
            # 1.1 接收浏览器发来的HTTP协议的消息
            recv_data = conn.recv(1024)
            
            # 1.2对HTTP协议的消息加以处理,简单示范如下
            ll = recv_data.decode('utf-8').split('
    ')
            head_ll = ll[0].split(' ')
            environ = {}
            environ['PATH_INFO'] = head_ll[1]
            environ['method'] = head_ll[0]
            # 2.将请求信息处理后的结果environ交给application,这样application便无需再关注请求信息的处理,可以更加专注于业务逻辑的处理
            res = app(environ)
    
            # 3.按照HTTP协议向浏览器返回信息
            # 3.1返回响应首行
            conn.send(b'HTTP/1.1 200 OK
    ')
            # 3.2返回响应头(可以省略)
            conn.send(b'Content-Type: text/html
    
    ')
            # 3.3 返回响应体
            conn.send(res)
    
            conn.close()
    
    
    # 代表application
    def app(recv_data):
        # 处理业务逻辑
        return b'hello word'
    
    
    if __name__ == '__main__':
        # 在客户端浏览器输入:http://127.0.0.1:8080 
        server('127.0.0.1', 8080, app)
    

    此时,重启S端后,在客户端浏览器输入:http://127.0.0.1:8080 便可以看到正常结果hello word了

    我们不仅可以回复hello word这样的普通字符,还可以夹杂HTML标签,浏览器在接收到消息后会对解析出的HTML标签加以渲染

    import socket
    
    
    # server端函数
    def server(ip, port, app):
        sock = socket.socket()
        sock.bind((ip, port))
        sock.listen(5)
        while True:
            conn, addr = sock.accept()
    
            recv_data = conn.recv(1024)
    
            ll = recv_data.decode('utf-8').split('
    ')
            head_ll = ll[0].split(' ')
            environ = {}
            environ['PATH_INFO'] = head_ll[1]
            environ['method'] = head_ll[0]
    
            res = app(environ)
    
            conn.send(b'HTTP/1.1 200 OK
    ')
    
            conn.send(b'Content-Type: text/html
    
    ')
    
            conn.send(res)
    
            conn.close()
    
    
    # 代表application
    def app(recv_data):
        # 处理业务逻辑
        return b'<h1>hello web</h1>'
    
    
    if __name__ == '__main__':
        # 在客户端浏览器输入:http://127.0.0.1:8080 
        server('127.0.0.1', 8080, app)
    

    更进一步我们还可以返回一个文件,例如timer.html,内容如下

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h2>
            {{time}}
        </h2>
    </body>
    </html>
    

    S端程序如下

    import socket
    
    
    # server端函数
    def server(ip, port, app):
        sock = socket.socket()
        sock.bind((ip, port))
        sock.listen(5)
        while True:
            conn, addr = sock.accept()
    
            recv_data = conn.recv(1024)
    
            ll = recv_data.decode('utf-8').split('
    ')
            head_ll = ll[0].split(' ')
            environ = {}
            environ['PATH_INFO'] = head_ll[1]
            environ['method'] = head_ll[0]
    
            res = app(environ)
    
            conn.send(b'HTTP/1.1 200 OK
    ')
    
            conn.send(b'Content-Type: text/html
    
    ')
    
            conn.send(res)
    
            conn.close()
    
    
    # 代表application
    def app(recv_data):
        # 处理业务逻辑:打开文件,读取文件内容并返回
        with open('timer.html','r',encoding='utf-8') as f:
            data = f.read()
        return data.encode('utf-8')
    
    
    if __name__ == '__main__':
        # 在客户端浏览器输入:http://127.0.0.1:8080 
        server('127.0.0.1', 8080, app)
    

    上述S端为浏览器返回的都是静态页面(内容都是固定的),我们还可以返回动态页面(内容是变化的)

    import socket
    
    
    # server端函数
    def server(ip, port, app):
        sock = socket.socket()
        sock.bind((ip, port))
        sock.listen(5)
        while True:
            conn, addr = sock.accept()
    
            recv_data = conn.recv(1024)
    
            ll = recv_data.decode('utf-8').split('
    ')
            head_ll = ll[0].split(' ')
            environ = {}
            environ['PATH_INFO'] = head_ll[1]
            environ['method'] = head_ll[0]
    
            res = app(environ)
    
            conn.send(b'HTTP/1.1 200 OK
    ')
    
            conn.send(b'Content-Type: text/html
    
    ')
    
            conn.send(res)
    
            conn.close()
    
    
    # 代表application
    def app(recv_data):
        # 处理业务逻辑
        with open('timer.html','r',encoding='utf-8') as f:
            data = f.read()
        improt time
        now = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())
        data = data.replace('{{time}}',now) # 字符串替换
        return data.encode('utf-8')
    
    
    if __name__ == '__main__':
        # 在客户端浏览器输入:http://127.0.0.1:8080 
        server('127.0.0.1', 8080, app)
    

    三、Web框架的由来

    综上案例我们可以发现一个规律,在开发S端时,server的功能时复杂且固定的(处理socket消息的收发和HTTP协议的处理),而app中的业务逻辑却各不相同(不同的软件就应该由不同的业务逻辑),重复开发复杂且固定的server是毫无意义的,由一个wsgiref模块帮我们写好了server的功能,这样我们便只需要专注于挨批评功能的编写即可

    img

    from wsgiref.simple_server import make_server
    
    
    def app(environ, response):
        '''
        :param environ: 请求相关的所有数据
        :param response: 响应相关的所有数据
        :return:
        '''
        # 1.返回HTTP协议的响应首行和响应头信息
        response('200 OK', [('Content-Type', 'text/html')])
        # 2.处理业务逻辑:根据请求url的不同返回不同的页面内容
        if environ.get('PATH_INFO') == '/index':
            with open('index.html', 'r', encoding='utf8') as f:
                data = f.read()
        elif environ.get('PATH_INFO') == '/timer':
            with open('timer.html', 'r', encoding='utf8') as f:
                data = f.read()
            import time
            now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
            data = data.replace('{{ time }}', now)  # 字符串替换
        else:
            data = '<h1>hello,web</h1>'
        return [data.encode('utf-8')]
    
    
    if __name__ == '__main__':
        # 当接收请求时,wsgiref模块会对该请求加以处理,然后调用app函数,自动传入两个参数
        # 1. enciron 是一个字典,存放了HTTP的请求信息
        # 2. response是一个功能,用于返回HTTP协议的响应首行和响应头信息
        s = make_server('127.0.0.1', 8080, app)
        s.serve_forever() # 在浏览器输入http://127.0.0.1:8080/index和http://127.0.0.1:8080/timer会看到不同的页面内容
    
    

    index.html文件内容

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h2>主页</h2>
    </body>
    </html>
    

    上述案例中app在处理业务逻辑时需要根据不同的url地址返回不同的页面内容,当url地址越来越多,需要写一堆if判断,代码不够清晰,耦合程度高,所以我们做出以下优化

    from wsgiref.simple_server import make_server
    
    
    def index(environ):
        with open('index.html', 'r', encoding='utf8') as f:
            data = f.read()
        return data.encode('utf-8')
    
    
    def timer(environ):
        with open('timer.html', 'r', encoding='utf8') as f:
            data = f.read()
        return data.encode('utf-8')
    
    
    url_patterns = {
        ('/index', index),
        ('/timer', timer)
    }
    
    
    def app(environ, response):
        '''
        :param environ: 请求相关的所有数据
        :param response: 响应相关的所有数据
        :return:
        '''
        # 1.返回HTTP协议的响应首行和响应头信息
        response('200 OK', [('Content-Type', 'text/html')])
        reuqest_url = environ.get('PATH_INFO')
        for url in url_patterns:
            if url[0] == reuqest_url:
                data = url[1](environ)
        else:
            data = b'404'
        return [data]
    
    
    if __name__ == '__main__':
        # 当接收请求时,wsgiref模块会对该请求加以处理,然后调用app函数,自动传入两个参数
        # 1. enciron 是一个字典,存放了HTTP的请求信息
        # 2. response是一个功能,用于返回HTTP协议的响应首行和响应头信息
        s = make_server('127.0.0.1', 8080, app)
        s.serve_forever()  # 在浏览器输入http://127.0.0.1:8080/index和http://127.0.0.1:8080/timer会看到不同的页面内容
    
    

    随着业务逻辑复杂度的增加,处理业务逻辑的函数以及url_patterns中的映射关系都会不断增多,此时仍然把所有代码都放到一个文件中,程序的可读性和可扩展性都会变差,所以我们应该将现有的代码拆分到不同文件中

    project

    views.py内容如下:

    def index(environ):
        with open('index.html', 'r', encoding='utf8') as f:
            data = f.read()
        return data.encode('utf-8')
    
    
    def timer(environ):
        import datetime
        now = datetime.datetime.now().strftime('%y-%m-%d %X')
        with open('timer.html', 'r', encoding='utf8') as f:
            data = f.read()
        data = data.replace('{{ time }}',now)
        return data.encode('utf-8')
    

    urls.py内容

    # 路径跟函数的映射关系
    from app01.views import * 
    url_patterns = {
        ('/index', index),
        ('/timer', timer)
    }
    

    main.py内容

    from wsgiref.simple_server import make_server
    from mysite.urls import url_patterns 
    def app(environ, response):
        '''
        :param environ: 请求相关的所有数据
        :param response: 响应相关的所有数据
        :return:
        '''
        # 1.返回HTTP协议的响应首行和响应头信息
        response('200 OK', [('Content-Type', 'text/html')])
        reuqest_url = environ.get('PATH_INFO')
        for url in url_patterns:
            if url[0] == reuqest_url:
                data = url[1](environ)
        else:
            data = b'404'
        return [data]
    
    
    if __name__ == '__main__':
        # 当接收请求时,wsgiref模块会对该请求加以处理,然后调用app函数,自动传入两个参数
        # 1. enciron 是一个字典,存放了HTTP的请求信息
        # 2. response是一个功能,用于返回HTTP协议的响应首行和响应头信息
        s = make_server('127.0.0.1', 8080, app)
        s.serve_forever()  # 在浏览器输入http://127.0.0.1:8080/index和http://127.0.0.1:8080/timer会看到不同的页面内容
    
    

    至此,我们就针对application的开发自定义了一个框架,所以说框架的本质就是一系列功能的集合体,不同的功能放到不同的文件中,有了该框架,可以让我们专注于业务逻辑的编写,极大的提高了开发web应用的效率(开发web应用的框架可以简称为web框架),比如我们新增一个业务逻辑,要求为:浏览器输入http://127.0.0.1:8080/home 就能访问到home.html页面,在框架的基础上具体开发步骤如下:

    步骤一:在templates文件夹下新增home.html

    步骤二:在urls.py的url_patterns中新增一条映射关系

    url_patterns = {
        ('/index', index),
        ('/timer', timer),
        ('/home',home) #新增的映射关系
    }
    

    步骤三:在views.py中新增一个名为home的函数

    def index(environ):
        with open('templates/home.html', 'r', encoding='utf8') as f:
            data = f.read()
        return data.encode('utf-8')
    
  • 相关阅读:
    建模时选择SVM还是LR?
    decision_function详解
    HIVE SQL与SQL的区别
    数据不平衡如何处理
    Spring Boot 两步集成 日志收集ELK与分布式系统监控CAT
    Web Application Integrate with Box
    字符编解码的故事(ASCII,ANSI,Unicode,Utf-8区别)
    SharePoint Client Api Search user 几种方法
    C#,反射和直接调用的效率差别
    System.Data.SqlClient.SqlConnectionStringBuilder 链接字符串的问题
  • 原文地址:https://www.cnblogs.com/Lin2396/p/11708331.html
Copyright © 2011-2022 走看看