一、Web框架的本质:

  我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以自己实现Web框架了。

  • 半成品自定义web框架

    import socket
    
    server = socket.socket()
    server.bind(("127.0.0.1", 8080))
    server.listen() while True:
    conn, addr = server.accept()
    data = conn.recv(8096)
    conn.send(b"OK")
    conn.close()

    可以说Web服务本质上都是在这十几行代码基础上扩展出来的。这段代码是web服务的核心。
    那么用户的浏览器输入一个网址,会给服务端发送数据,那么服务端会发送什么格式的数据来进行浏览器与服务端直接的收发通信呢?
    必须有一个统一的规则,让双方发送消息、接收消息的时候有个格式依据,这样就不会出乱子。
    这个规则就是HTTP-超文本传输协议
    先看看浏览器访问服务端会发生什么:

    import socket
    
    server = socket.socket()
    server.bind(("127.0.0.1", 8080))
    server.listen() while True:
    conn, addr = server.accept()
    data = conn.recv(8096)
    print(data)
    conn.send(b"OK")
    conn.close()

    输出:

    b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nDNT: 1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n\r\n'

    这就是浏览器给服务端发送的请求数据,接下来得知道,HTTP请求和相应的格式:

    数据格式之GET请求:
      请求首行
           请求头(一堆k,v键值对)

    请求体(post请求携带的数据)
    数据格式之响应:
      响应首行
      响应头(一堆k,v键值对)

      响应体(post请求携带的数据)

    接下来继续完善web框架:(根据不同路径返回不同的内容):

    '''
    根据URL中不同的路径返回不同的内容
    ''' import socket # 创建server服务连接
    server = socket.socket()
    server.bind(('127.0.0.1', 8080)) # 绑定服务端IP和端口(本机做服务端则为127.0.0.1,这样socket就会自动识别。
    server.listen(5) # 半连接池 # 下面是通信循环:
    while True:
    conn, addr = server.accept() # 等待客户的连接,这个地方是阻塞点
    data_bytes = conn.recv(1024) # 接收客户的发来的请求数据
    data = data_bytes.decode('utf-8') # 解码收到的数据
    data1 = data.split('\r\n')[0] # 分离出第一行数据(第一个\r\n之前的数据)
    url = data1.split()[1] # 继续分离,用空格来分割,然后取索引为1的元素就是请求的访问路径
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
    # 根据不同的路径返回不同的内容:
    if url == '/index':
    response = b'index'
    elif url == '/home':
    response = b'home'
    else:
    response = b'404 not found!'
    conn.send(response)
    conn.close()

    继续优化:用函数来返回不同的内容:

    import socket
    
    # 创建server服务连接
    server = socket.socket()
    server.bind(('127.0.0.1', 8080)) # 绑定服务端IP和端口(本机做服务端则为127.0.0.1,这样socket就会自动识别。
    server.listen(5) # 半连接池 def index(url):
    response = b'index'
    return response def home(url):
    response = b'home'
    return response url_list = [
    ('/index', index),
    ('/home', home),
    ] # 下面是通信循环:
    while True:
    conn, addr = server.accept() # 等待客户的连接,这个地方是阻塞点
    data_bytes = conn.recv(1024) # 接收客户的发来的请求数据
    data = data_bytes.decode('utf-8') # 解码收到的数据
    data1 = data.split('\r\n')[0] # 分离出第一行数据(第一个\r\n之前的数据)
    url = data1.split()[1] # 继续分离,用空格来分割,然后取索引为1的元素就是请求的访问路径
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
    # 根据不同的路径返回不同的内容:
    func = None
    for i in url_list:
    if i[0] == url:
    func = i[1]
    break
    if func:
    response = func(url)
    else:
    response = b'404 not found!' conn.send(response)
    conn.close()

    返回具体的HTML文件

    完美解决了不同URL返回不同内容的问题。 但是我不想仅仅返回几个字符串,我想给浏览器返回完整的HTML内容,这又该怎么办呢?

    没问题,不管是什么内容,最后都是转换成字节数据发送出去的。 我们可以打开HTML文件,读取出它内部的二进制数据,然后再发送给浏览器。

    import socket
    
    # 创建server服务连接
    server = socket.socket()
    server.bind(('127.0.0.1', 8080)) # 绑定服务端IP和端口(本机做服务端则为127.0.0.1,这样socket就会自动识别。
    server.listen(5) # 半连接池 def index(url):
    # 读取index.html页面的内容
    with open("index.html", "r", encoding="utf8") as f:
    s = f.read()
    # 返回字节数据
    return s.encode('utf-8') def home(url):
    # 读取index.html页面的内容
    with open("home.html", "r", encoding="utf8") as f:
    s = f.read()
    # 返回字节数据
    return s.encode('utf-8') url_list = [
    ('/index', index),
    ('/home', home),
    ] # 下面是通信循环:
    while True:
    conn, addr = server.accept() # 等待客户的连接,这个地方是阻塞点
    data_bytes = conn.recv(1024) # 接收客户的发来的请求数据
    data = data_bytes.decode('utf-8') # 解码收到的数据
    data1 = data.split('\r\n')[0] # 分离出第一行数据(第一个\r\n之前的数据)
    url = data1.split()[1] # 继续分离,用空格来分割,然后取索引为1的元素就是请求的访问路径
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
    # 根据不同的路径返回不同的内容:
    func = None
    for i in url_list:
    if i[0] == url:
    func = i[1]
    break
    if func:
    response = func(url)
    else:
    response = b'404 not found!' conn.send(response)
    conn.close()

    让网页动态起来

    这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。

    没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。(这里使用时间戳来模拟动态的数据)

    import socket
    import time # 创建server服务连接
    server = socket.socket()
    server.bind(('127.0.0.1', 8080)) # 绑定服务端IP和端口(本机做服务端则为127.0.0.1,这样socket就会自动识别。
    server.listen(5) # 半连接池 def index(url):
    with open("index.html", "r", encoding="utf8") as f:
    s = f.read()
    now = str(time.time())
    s = s.replace("@@oo@@", now) # 在网页中定义好特殊符号,用动态的数据去替换提前定义好的特殊符号
    return s.encode('utf-8') def home(url):
    # 读取index.html页面的内容
    with open("index.html", "r", encoding="utf8") as f:
    s = f.read()
    # 返回字节数据
    return s.encode('utf-8') url_list = [
    ('/index', index),
    ('/home', home),
    ] # 下面是通信循环:
    while True:
    conn, addr = server.accept() # 等待客户的连接,这个地方是阻塞点
    data_bytes = conn.recv(1024) # 接收客户的发来的请求数据
    data = data_bytes.decode('utf-8') # 解码收到的数据
    data1 = data.split('\r\n')[0] # 分离出第一行数据(第一个\r\n之前的数据)
    url = data1.split()[1] # 继续分离,用空格来分割,然后取索引为1的元素就是请求的访问路径
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
    # 根据不同的路径返回不同的内容:
    func = None
    for i in url_list:
    if i[0] == url:
    func = i[1]
    break
    if func:
    response = func(url)
    else:
    response = b'404 not found!' conn.send(response)
    conn.close()

    对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序

    服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。

    应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。

    这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。

    这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

    WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。

    常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。

    服务器程序,正常开发环境下用wsgiref,而在正式假设服务上线用uWSGI
    接下来利用wsgiref模块来替换上面写的web框架的socket server部分:

    import time
    from wsgiref.simple_server import make_server def index(url):
    with open("index.html", "r", encoding="utf8") as f:
    s = f.read()
    now = str(time.time())
    # 在网页中定义好特殊符号,用动态的数据去替换提前定义好的特殊符号
    s = s.replace("@@oo@@", now)
    return s def home(url):
    # 读取index.html页面的内容
    with open("index.html", "r", encoding="utf8") as f:
    s = f.read()
    return s def err():
    return '404 not found!' url_list = [
    ('/index', index),
    ('/home', home),
    ] def run(env, response):
    ''' :param env: 请求相关信息,一个大字典,里面装了一堆处理好了的键值对数据
    :param response: 相应相关信息
    :return:
    '''
    # 固定写法 后面列表里面一个个元祖会以响应头kv键值对的形式返回给客户端
    response('200 ok', [('username', 'sgt'), ('password', '')])
    # 获取用户访问路径:
    current_path = env.get('PATH_INFO')
    # 定义一个存储函数名的变量:
    func = None
    for i in url_list:
    if i[0] == current_path:
    func = i[1]
    break
    if func:
    res = func()
    else:
    res = err() return [res.encode('utf-8')] if __name__ == '__main__':
    server = make_server('127.0.0.1', 8080, run)
    server.serve_forever()

    jinja2

    上面的代码实现了一个简单的动态,我完全可以从数据库中查询数据,然后去替换我html中的对应内容,然后再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: jinja2

    下载jinja2:

    pip install jinja2
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    </head>
    <body>
    <h1>姓名:{{name}}</h1>
    <h1>爱好:</h1>
    <ul>
    {% for hobby in hobby_list %}
    <li>{{hobby}}</li>
    {% endfor %}
    </ul>
    </body>
    </html>

    index2 html文件

    使用jinja2渲染index2.html文件:

     
     
    from wsgiref.simple_server import make_server
    from jinja2 import Template def index():
    with open("index2.html", "r") as f:
    data = f.read()
    template = Template(data) # 生成模板文件
    ret = template.render({"name": "Alex", "hobby_list": ["烫头", "泡吧"]}) # 把数据填充到模板里面
    return [bytes(ret, encoding="utf8"), ] def home():
    with open("home.html", "rb") as f:
    data = f.read()
    return [data, ] # 定义一个url和函数的对应关系
    URL_LIST = [
    ("/index/", index),
    ("/home/", home),
    ] def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO'] # 取到用户输入的url
    func = None # 将要执行的函数
    for i in URL_LIST:
    if i[0] == url:
    func = i[1] # 去之前定义好的url列表里找url应该执行的函数
    break
    if func: # 如果能找到要执行的函数
    return func() # 返回函数的执行结果
    else:
    return [bytes("404没有该页面", encoding="utf8"), ] if __name__ == '__main__':
    httpd = make_server('', 8000, run_server)
    print("Serving HTTP on port 8000...")
    httpd.serve_forever()
     
     

    现在的数据是我们自己手写的,那可不可以从数据库中查询数据,来填充页面呢?

    使用pymysql连接数据库:

     
     
    conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("select name, age, department_id from userinfo")
    user_list = cursor.fetchall()
    cursor.close()
    conn.close()
     
     

    创建一个测试的user表:

    CREATE TABLE user(
    id int auto_increment PRIMARY KEY,
    name CHAR(10) NOT NULL,
    hobby CHAR(20) NOT NULL
    )engine=innodb DEFAULT charset=UTF8;

    模板的原理就是字符串替换,我们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。

前端Web框架的实现过程的更多相关文章

  1. 前端技术之:常见前端Web框架

    Express 声称是快速.自由.小巧的Node.js Web框架,官网地址如下: https://expressjs.com/ https://github.com/expressjs/expres ...

  2. 开源的前端web框架推荐

    B-JUI前端框架:http://demo.b-jui.com/ gentelella :https://colorlib.com/polygon/gentelella/ admui(收费):http ...

  3. python 实现web框架simfish

    python 实现web框架simfish 本文主要记录本人利用python实现web框架simfish的过程.源码github地址:simfish WSGI HTTP Server wsgi模块提供 ...

  4. Python学习 - 编写一个简单的web框架(一)

    自己动手写一个web框架,因为我是菜鸟,对于python的一些内建函数不是清楚,所以在写这篇文章之前需要一些python和WSGI的预备知识,这是一系列文章.这一篇只实现了如何处理url. 参考这篇文 ...

  5. 二、Web框架实现

    一.简单web(socket) 在前一篇WEB框架概述一文中已经详细了解了:从浏览器键入一个URL到返回HTML内容的整个过程.说到底,本质上其实就是一个socket服务端,用户的浏览器其实就是一个s ...

  6. 浅析Java Web框架技术

    一.Java Web框架技术的概念 所谓的Java框架,简单理解是一个可复用的设计构件,它规定了应用的体系结构,阐明了整个设计.协作构件之间的依赖关系.责任分配和控制流程,表现为一组抽象类以及其实例之 ...

  7. 从使用传统Web框架到切换到Spring Boot后的总结

    1.前言 其实我接触 Spring Boot 的时间并不长,所以还算一个初学者,这篇文章也算是我对 Spring Boot 学习以及使用过程中的复盘,如果文章出现描述错误或表达不清晰的地方,欢迎大家在 ...

  8. 2015年最全的移动WEB前端UI框架

    目前,众多互联网公司APP都嵌入了大量的HTML5,移动端的开发越来越重视,HTML5的运用场景也越来越多了.在移动WEB开发的过程中,使用合适的移动WEB UI框架可以大大提升我们的开发效率.下面P ...

  9. web前端UI框架

    分类:WEB前端 时间:2016年1月13日 目前,众多互联网公司APP都嵌入了大量的HTML5,移动端的开发越来越重视,HTML5的运用场景也越来越多了.在移动WEB开发的过程中,使用合适的移动WE ...

随机推荐

  1. 基于php的AWS存储服务

    近几天用到了aws的s3存储服务,公司内部的完全兼容aws并对其进行了封装,之前也用过,现在把经验总结一下. 既然要用,首先需要安装sdk,下边提供了几种安装方法 方法一:使用composer安装 1 ...

  2. python3错误之TypeError: 'dict_items' object is not callable

    这种错误出现在循环结构中套循环结构,而循环时内部循环为字典,外部循环为该字典调用items方法后取到的值,内部循环调用外部循环中遍历的结果: 解决方案: 将外部循环的items()方法调用改为.key ...

  3. 使用Hexo 搭建自己的博客

    使用Hexo 搭建自己的博客 最近一直都想着如何去搭建属于自己的博客,有空的时候就写写文章什么的. 本人对该博客系统的要求是: 博文的编写要采用现在流行的MarkDown来进行编写. 本人还不想去注册 ...

  4. AKOJ-2021-逆序对(归并,二分)

    链接:https://oj.ahstu.cc/JudgeOnline/problem.php?id=2021 题意: 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们 ...

  5. (动态规划)UVA-11400:Lighting System Design

    You are given the task to design a lighting system for a huge conference hall. After doing a lot of ...

  6. HDU 5230 ZCC loves hacking 大数字的整数划分

    http://acm.hdu.edu.cn/showproblem.php?pid=5230 把题目简化后,就是求 1---n - 1这些数字中,将其进行整数划分,其中整数划分中不能有重复的数字,如果 ...

  7. 图像分类丨ILSVRC历届冠军网络「从AlexNet到SENet」

    前言 深度卷积网络极大地推进深度学习各领域的发展,ILSVRC作为最具影响力的竞赛功不可没,促使了许多经典工作.我梳理了ILSVRC分类任务的各届冠军和亚军网络,简单介绍了它们的核心思想.网络架构及其 ...

  8. Java环境安装与Eclipse安装

    1.jdk下载安装 2.Eclipse下载安装 遇到的问题: 出现问题原因可能有两个:1)没有配置环境变量 2)jdk和eclipse安装的版本不一致,都是64位或者都是32位. 本人出现错误的原因: ...

  9. ES6语言特性,如何在低版本浏览器运行它

    Introduction ECMAScript 6 于今年6月正式成为了标准,关于ES6,我们最大的疑虑可能是客户端浏览器还没有完全支持它,也就node.js用用. 有很多种手段可以解决这个问题,比如 ...

  10. Java 8新特性--Lambda表达式作为返回值

    lambda表达式作为方法的返回值: