【python学习笔记】第14天

继续今天的python学习

昨天实现了WEB服务器的编写,今天进一步的写一下WEB框架,什么是框架?

emmmm,框架可以理解为一种可以直接拿来用的东西,写好之后大家都可以拿来用。

昨天的代码我们实现了静态和动态的服务器,今天的任务就是把静态和动态放到一起,不管是静态还是动态,我们都可以用同样的方法去访问

# WebFramework.py
import time
from webserver import HTTPserver

class Application(object):
    def __init__(self, urls):
        self.urls = urls

    def __call__(self, env, start_response):
        path = env.get("PATH_INFO", "/")
        for url, app in urls:
            if path == url:
                return app(env, start_response)
        # 如果出现错误,返回错误信息
        status = "404 Not Found"
        headers = [
            ("Content-Type", "Text/plain")

        ]
        start_response(status, headers)
        return "Not Found"

def ctime(env, start_response):
    status = "200 OK"
    headers = [
        ("Content-Type", "Text/plain")
    ]
    start_response(status, headers)
    return time.ctime()

def say_hello(env, start_response):
    status = "200 OK"
    headers = [
        ("Content-Type", "Text/plain")
    ]
    start_response(status, headers)
    return "Hello World"


if __name__ == "__main__":
    urls = [
        ("/", ctime),
        ("/ctime", ctime),
        ("/sayhello", say_hello)
    ]
    app = Application(urls)
    http_server = HTTPserver(app)
    http_server.bind(8080)
    http_server.start()

这就是写好的框架,我们来解读一下

首先定义了一个Application类,我们昨天是学习了一个叫做WSGI的东西,它规定了一个application函数,今天我们把它变成一个类,方便复用。昨天我们把想要实现的每一个功能都单独存储在一个文件当中,但是可想而知,当我们要实现的功能不断增大,那么我们要处理的文件数就不断增多,这是不方便的,所以我们想的是,能否把这些功能放到一个文件当中,变成不同的函数,我们要实现时去调用就可以了。所以有了urls这个列表,列表的每一个元素都是一个元组,元组的第一个元素是文件名,第二个元素是我们定义要实现的函数名,我们把每一个文件名对应成一个函数名,这样就做到了单文件多功能。

所以在Application类中定义了__call__()方法,把实例对象当成是函数来调用。今天env就派上了用场。

注:要调用py文件的功能要在Headers中添加"Content-Type: Text/plain",不然会直接提示下载文件。

框架写好了我们在昨天代码的基础上做一些修改,删除一些不必要的代码

# webserver.py
import socket
from multiprocessing import Process
import re
import os
import sys

# 设置静态文件根目录
HTML_ROOT_DIR = "./html"
WSGI_PYTHON_DIR = "./wsgipython"

class HTTPserver(object):
    """"""
    def __init__(self, application):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.app = application
        # self.server.listen(128)

    # 开启服务器
    def start(self):
        self.server.listen(128)
        while True:
            cli_socket, cli_address = self.server.accept()
            print("%s:%s已连接..." % cli_address)
            p = Process(target=self.handle_client, args=(cli_socket,))
            p.start()
            cli_socket.close()

    # WSGI的规定,在这里报文的头,因为后面的application返回的只是报文的body
    def start_response(self, status, headers):
        # 这是我们自己设置有的要响应的信息
        server_headers = [
            ("Server", "MyServer")
        ]
        # 设置报文头首行
        response_headers = "HTTP/1.1 " + status + os.linesep
        # 开始添加报文头
        for each in server_headers:
            response_headers += "%s: %s" % each
            response_headers += os.linesep
        for header in headers:
            response_headers += "%s: %s" % header
            response_headers += os.linesep
        self.response_headers = response_headers

    def handle_client(self, cli_socket):
        '''处理客户端请求'''
        # 获取用户请求数据
        data = cli_socket.recv(1024)
        request_header_lines = data.splitlines()
        for line in request_header_lines:
            print(line.decode("utf-8"))
        # 解析请求的报文
        request_start_line = request_header_lines[0].decode("utf-8")
        # 提取用户请求的文件名
        file_name = re.match(r"\w+ +(/[^ ]* )", request_start_line).group(1)
        file_name = file_name.strip()

        env = {
            "PATH_INFO":file_name
        }
        # application是WSGI规定的统一的接口,目的是保证调用脚本的通用性
        response_body = self.app(env, self.start_response)
        response = self.response_headers + os.linesep + response_body


        cli_socket.send(response.encode("utf-8"))
        cli_socket.close()

    def bind(self, port):
        self.server.bind(("", port))


def main():
    # 在这里需要把模块添加到path中才能导入,否则找不到
    sys.path.insert(1, WSGI_PYTHON_DIR)
    http_server = HTTPserver()
    http_server.bind(8080)
    http_server.start()


if __name__ == "__main__":
    main()

对比昨天的代码是不是清爽了许多?

接下来看下运行结果(注意,这次要在WebFramework.py当中运行的哦~)

 实现了一个文件多个函数,但是美中不足,我们还要修改一下代码,访问静态文件

# WebFramework.py
import time
from webserver import HTTPserver

HTML_DIR = "./html"

class Application(object):
    def __init__(self, urls):
        self.urls = urls

    def __call__(self, env, start_response):
        path = env.get("PATH_INFO", "/")
        if "/" == path:
            path = "/static/index.html"
        if path.startswith("/static"):
            file_name = path[7:]
            try:
                f = open(HTML_DIR + file_name, "rb")
            except:
                status = "404 Not Found"
                headers = [
                    ("Content-Type", "Text/plain")

                ]
                start_response(status, headers)
                return "NOT FOUND"
            else:
                status = "200 OK"
                headers = [
                    ("Content-Type", "Text/html")

                ]
                start_response(status, headers)
                data = f.read().decode("utf-8")
                f.close()
                return data

        for url, app in urls:
            if path == url:
                return app(env, start_response)
        # 如果出现错误,返回错误信息
        status = "404 Not Found"
        headers = [
            ("Content-Type", "Text/plain")

        ]
        start_response(status, headers)
        return "Not Found"

def ctime(env, start_response):
    status = "200 OK"
    headers = [
        ("Content-Type", "Text/plain")
    ]
    start_response(status, headers)
    return time.ctime()

def say_hello(env, start_response):
    status = "200 OK"
    headers = [
        ("Content-Type", "Text/plain")
    ]
    start_response(status, headers)
    return "Hello World"


if __name__ == "__main__":
    urls = [
        ("/ctime", ctime),
        ("/sayhello", say_hello)
    ]
    app = Application(urls)
    http_server = HTTPserver(app)
    http_server.bind(8080)
    http_server.start()

我们要访问静态文件时,要以/static开头

运行结果:

注意代码中的细节,访问html文件,Headers要修改,"Content-Type: Text/html",不然显示的就是你html的代码

 

实现了昨天我们要的功能,但是又来了一个问题,正常情况下应该启动webserver.py而不是框架,毕竟我们要运行的是服务器,那么接下来我们要修改代码,把服务器作为启动文件

# webserver2.py

import socket
from multiprocessing import Process
import re
import os
import sys

# 设置静态文件根目录
HTML_ROOT_DIR = "./html"
WSGI_PYTHON_DIR = "./wsgipython"

class HTTPserver(object):
    """"""
    def __init__(self, application):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.app = application
        # self.server.listen(128)

    # 开启服务器
    def start(self):
        self.server.listen(128)
        while True:
            cli_socket, cli_address = self.server.accept()
            print("%s:%s已连接..." % cli_address)
            p = Process(target=self.handle_client, args=(cli_socket,))
            p.start()
            cli_socket.close()

    # WSGI的规定,在这里报文的头,因为后面的application返回的只是报文的body
    def start_response(self, status, headers):
        # 这是我们自己设置有的要响应的信息
        server_headers = [
            ("Server", "MyServer")
        ]
        # 设置报文头首行
        response_headers = "HTTP/1.1 " + status + os.linesep
        # 开始添加报文头
        for each in server_headers:
            response_headers += "%s: %s" % each
            response_headers += os.linesep
        for header in headers:
            response_headers += "%s: %s" % header
            response_headers += os.linesep
        self.response_headers = response_headers

    def handle_client(self, cli_socket):
        '''处理客户端请求'''
        # 获取用户请求数据
        data = cli_socket.recv(1024)
        request_header_lines = data.splitlines()
        for line in request_header_lines:
            print(line.decode("utf-8"))
        # 解析请求的报文
        request_start_line = request_header_lines[0].decode("utf-8")
        # 提取用户请求的文件名
        file_name = re.match(r"\w+ +(/[^ ]* )", request_start_line).group(1)
        file_name = file_name.strip()

        env = {
            "PATH_INFO":file_name
        }
        # application是WSGI规定的统一的接口,目的是保证调用脚本的通用性
        response_body = self.app(env, self.start_response)
        response = self.response_headers + os.linesep + response_body


        cli_socket.send(response.encode("utf-8"))
        cli_socket.close()

    def bind(self, port):
        self.server.bind(("", port))


def main():
    # 在这里需要把模块添加到path中才能导入,否则找不到
    sys.path.insert(1, WSGI_PYTHON_DIR)
    if len(sys.argv) < 2:
        sys.exit("Format: python modulename:Appname")
    module_name, app_name = sys.argv[1].split(":")
    m = __import__(module_name)
    app = getattr(m, app_name)
    http_server = HTTPserver(app)
    http_server.bind(8080)
    http_server.start()


if __name__ == "__main__":
    main()
# webframework.py

import time
from webserver import HTTPserver

HTML_DIR = "./html"

class Application(object):
    def __init__(self, urls):
        self.urls = urls

    def __call__(self, env, start_response):
        path = env.get("PATH_INFO", "/")
        if "/" == path:
            path = "/static/index.html"
        if path.startswith("/static"):
            file_name = path[7:]
            try:
                f = open(HTML_DIR + file_name, "rb")
            except:
                status = "404 Not Found"
                headers = [
                    ("Content-Type", "Text/plain")

                ]
                start_response(status, headers)
                return "NOT FOUND"
            else:
                status = "200 OK"
                headers = [
                    ("Content-Type", "Text/html")

                ]
                start_response(status, headers)
                data = f.read().decode("utf-8")
                f.close()
                return data

        for url, app in urls:
            if path == url:
                return app(env, start_response)
        # 如果出现错误,返回错误信息
        status = "404 Not Found"
        headers = [
            ("Content-Type", "Text/plain")

        ]
        start_response(status, headers)
        return "Not Found"

def ctime(env, start_response):
    status = "200 OK"
    headers = [
        ("Content-Type", "Text/plain")
    ]
    start_response(status, headers)
    return time.ctime()

def say_hello(env, start_response):
    status = "200 OK"
    headers = [
        ("Content-Type", "Text/plain")
    ]
    start_response(status, headers)
    return "Hello World"


urls = [
    ("/ctime", ctime),
    ("/sayhello", say_hello)
]
app = Application(urls)

运行命令:

python3 webserver2.py webframework:app

运行结果同上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值