前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python web框架开发 - 实现动态页面返回

Python web框架开发 - 实现动态页面返回

作者头像
Devops海洋的渔夫
发布2019-06-02 13:46:26
7600
发布2019-06-02 13:46:26
举报
文章被收录于专栏:Devops专栏Devops专栏

仅供学习,转载请注明出处

前情回顾

其实我连着写的,就喝了杯水。所谓前情回顾就是我继续上一篇Python web框架开发 - WSGI协议 来继续代码编写。

有跳过的朋友可以根据链接先看看上一篇熟悉一下。

上一篇章,我已经完成了使用WSGI协议返回浏览器一个hello world,那么我能不能返回一个“嗨,胖子老板,来包芙蓉王”呢?

哦,是不是很调皮

这里来编写代码测试一下。

测试返回中文内容到浏览器中

运行测试如下:

其实这个乱码的原因就是返回的http头信息中的content-type没有指定使用utf-8编码。我们来看看百度页面的头信息。

从百度的返回header信息来看,我们需要设置Content-Type: charset=UTF-8

那么下面给返回的header信息添加内容如下:

好啦,写到这里就可以正常返回中文内容到浏览器了。

那么思考一下,下一步我就想要这样的需求。

如果浏览器发过来http://xxxx:7788/index.py的请求过来,那么我就返回一个index.html的内容到浏览器。

该怎么做呢?

大概思路可以如下:

  • 首先,framework.py中的application肯定要通过某个参数,获取到浏览器发送过来的url路径,从来判断打开哪个HTML资源
  • webserver部分,可以通过刚才没有用上的environ字典,传递路径以及其他所需的数据内容,这里只需要传递一个url路径即可。

另外,可以看看WSGI的传递字典示例,进行参考一下。

代码语言:javascript
复制
{
    'HTTP_ACCEPT_LANGUAGE': 'zh-cn',
    'wsgi.file_wrapper': <built-infunctionuwsgi_sendfile>,
    'HTTP_UPGRADE_INSECURE_REQUESTS': '1',
    'uwsgi.version': b'2.0.15',
    'REMOTE_ADDR': '172.16.7.1',
    'wsgi.errors': <_io.TextIOWrappername=2mode='w'encoding='UTF-8'>,
    'wsgi.version': (1,0),
    'REMOTE_PORT': '40432',
    'REQUEST_URI': '/',
    'SERVER_PORT': '8000',
    'wsgi.multithread': False,
    'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'HTTP_HOST': '172.16.7.152: 8000',
    'wsgi.run_once': False,
    'wsgi.input': <uwsgi._Inputobjectat0x7f7faecdc9c0>,
    'SERVER_PROTOCOL': 'HTTP/1.1',
    'REQUEST_METHOD': 'GET',
    'HTTP_ACCEPT_ENCODING': 'gzip,deflate',
    'HTTP_CONNECTION': 'keep-alive',
    'uwsgi.node': b'ubuntu',
    'HTTP_DNT': '1',
    'UWSGI_ROUTER': 'http',
    'SCRIPT_NAME': '',
    'wsgi.multiprocess': False,
    'QUERY_STRING': '',
    'PATH_INFO': '/index.html',
    'wsgi.url_scheme': 'http',
    'HTTP_USER_AGENT': 'Mozilla/5.0(Macintosh;IntelMacOSX10_12_5)AppleWebKit/603.2.4(KHTML,likeGecko)Version/10.1.1Safari/603.2.4',
    'SERVER_NAME': 'centos7'
}

这上面写得参数真是多呀,但是我这次要用到的就是这个参数'REQUEST_URI': '/'

我只要把webserver获取的文件路径,写入这个参数中,然后传递到framework,再进行读取文件,再返回HTML数据内容,就可以从浏览器中打开页面了。

说那么多,实操一波啦

url路径传递字典参数environ['REQUEST_URI']中,实现页面的返回

测试运行打印一下看看:

可以从上面看到已经获取到了需要打开的文件路径了,那么就用文件路径打开文件,返回数据到浏览器中看看。

但是可以从内容知道的是 ./html/index.py 并不是 ./html/index.html,这就比较尴尬了。不能够直接用于文件的打开,需要转化一下。

下面写个小测试,看看怎么转化。淡定试试正则匹配的sub方法。

代码语言:javascript
复制
In [1]: file_path = "./html/index.py"

In [2]: import re

# 在file_path中找到 .py 字符串,然后更改为 .html 字符串
In [3]: ret = re.sub(r".py",".html",file_path)

In [4]: print(ret)
./html/index.html

In [5]: 

好了,下面来继续。使用这个匹配后更换的规则,来打开文件并返回浏览器。

测试运行一下看看能否正确返回页面到浏览器中:

成功啦!因为可以返回动态页面到浏览器了。

下面肯定还会有很多可以扩展的地方,例如请求的是 login.py 、register.py 等等,只要在application进行匹配、判断、返回再返回就行啦。

这部分内容,我就不想写了。下面我想写的是能否通过命名行启动的时候,将启动服务端的端口号进行设定呢?

例如:我这里服务端写死的是7788的端口号,假设这个端口号被占用了!!!那我这个服务就无法启动了呀。

那该怎么办?

尴尬

sys.argv 的使用 - 输入参数至脚本之中

下面介绍使用一个 sys.argv 的参数列表,来解决这个问题。

首先编写一个test.py

代码语言:javascript
复制
import sys

print(sys.argv)

执行如下:

代码语言:javascript
复制
[root@server01 web]# python3 test.py 123 hahaha
['test.py', '123', 'hahaha']
[root@server01 web]# 

可以看出,在运行脚本的时候,后面的参数都会传入 sys.argv 的列表中,只要我后面通过列表获取这些参数,是不是就可以获取设置服务器启动的端口号呢?

下面来尝试一下:

修改test.py代码如下:

代码语言:javascript
复制
import sys

print(sys.argv)
print("设置服务端的端口号=",sys.argv[1])

执行测试如下:

代码语言:javascript
复制
[root@server01 web]# python3 test.py 8080
['test.py', '8080']
设置服务端的端口号= 8080
[root@server01 web]# 

好啦,那么下面我就可以使用这种方法,通过传入端口号来启动服务端。

编写设置服务端的端口号代码

好了,运行测试一下: 首先启动原来的7788端口号看看。

好了,使用7788的端口号成功之后,再改用8080的端口来测试一下:

成功了,可以根据启动的时候,填写参数来更改服务端启动端口号。

好了,那么就先到这里了。

因为太穷,买不起衣服,是时候钻被窝了,不能继续敲代码了。

最后贴上完整代码

webserver.py

代码语言:javascript
复制
#coding=utf-8
from socket import *
import re
import multiprocessing
import time
import framework
import sys

class WebServer:

   def __init__(self,server_port):

       # 创建套接字
       self.server_socket = socket(AF_INET, SOCK_STREAM)
       # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口
       self.server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
       # 设置服务端提供服务的端口号
       self.server_socket.bind(('', server_port))
       # 使用socket创建的套接字默认的属性是主动的,使用listen将其改为被动,用来监听连接
       self.server_socket.listen(128) #最多可以监听128个连接

   def start_http_service(self):
       # 开启while循环处理访问过来的请求
       while True:
           # 如果有新的客户端来链接服务端,那么就产生一个新的套接字专门为这个客户端服务
           # client_socket用来为这个客户端服务
           # self.server_socket就可以省下来专门等待其他新的客户端连接while True:
           client_socket, clientAddr = self.server_socket.accept()
           # handle_client(client_socket)
           # 设置子进程
           new_process = multiprocessing.Process(target=self.handle_client,args=(client_socket,))
           new_process.start() # 开启子进程
           # 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭的
           client_socket.close()

   def handle_client(self,client_socket):
       """为一个客户端服务"""
       # 接收对方发送的数据
       recv_data = client_socket.recv(1024).decode("utf-8") #  1024表示本次接收的最大字节数
       # 打印从客户端发送过来的数据内容
       #print("client_recv:",recv_data)
       request_header_lines = recv_data.splitlines()
       for line in request_header_lines:
           print(line)

       # 返回浏览器数据
       # 设置内容body
       # 使用正则匹配出文件路径
       print("------>",request_header_lines[0])
       print("file_path---->","./html/" + re.match(r"[^/]+/([^\s]*)",request_header_lines[0]).group(1))
       ret = re.match(r"[^/]+/([^\s]*)",request_header_lines[0])
       if ret:
          file_path = "./html/" + ret.group(1)
          if file_path == "./html/":
             file_path = "./html/index.html"
          print("file_path *******",file_path)

       # 判断file_path是否py文件后缀,如果是则请求动态资源,否则请求静态资源
       if file_path.endswith(".py"):

           # framework.application(client_socket)
           # 支撑WGSI协议的调用方式
           environ = {}
           environ['REQUEST_URI'] = file_path # 设置需要打开的文件路径
           response_body = framework.application(environ, self.start_response)
           # 设置返回的头信息header
           # 1.拼接第一行HTTP/1.1 200 OK + 换行符内容
           response_headers = "HTTP/1.1 " + self.application_header[0] + "\r\n"
           # 2.循环拼接第二行或者多行元组内容:Content-Type:text/html
           for var in self.application_header[1]:
               response_headers += var[0]+":"+var[1] + "\r\n"
           # 3.空一行与body隔开
           response_headers += "\r\n"
           # 4.打印看看header的内容信息
           print("response_header=")
           print(response_headers)

           # 设置返回的浏览器的内容
           client_socket.send(response_headers.encode("utf-8"))
           client_socket.send(response_body)

       else:
           # 请求静态资源
           try:
              # 设置返回的头信息 header
              response_headers = "HTTP/1.1 200 OK\r\n" # 200 表示找到这个资源
              response_headers += "\r\n" # 空一行与body隔开
              # 读取html文件内容
              file_name = file_path # 设置读取的文件路径
              f = open(file_name,"rb") # 以二进制读取文件内容
              response_body = f.read()
              f.close()
              # 返回数据给浏览器
              client_socket.send(response_headers.encode("utf-8"))   #转码utf-8并send数据到浏览器
              client_socket.send(response_body)   #转码utf-8并send数据到浏览器
           except:
              # 如果没有找到文件,那么就打印404 not found
              # 设置返回的头信息 header
              response_headers = "HTTP/1.1 404 not found\r\n" # 200 表示找到这个资源
              response_headers += "\r\n" # 空一行与body隔开
              response_body = "<h1>sorry,file not found</h1>"
              response = response_headers + response_body
              client_socket.send(response.encode("utf-8"))

   def start_response(self,status,header):
       self.application_header = [status,header]
       print("application_header=",self.application_header)

def main():
    # 通过sys.argv来获取服务端的端口号
    server_port = int(sys.argv[1])

    webserver = WebServer(server_port)
    webserver.start_http_service()

if __name__ == "__main__":
    main()

framework.py

代码语言:javascript
复制
import re

# 支撑WGSI协议
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=UTF-8')])
    # 接受需要打开的文件路径
    print("动态file_path= ", environ['REQUEST_URI'])

    file_path = re.sub(r".py", ".html", environ['REQUEST_URI'])

    with open(file_path,"rb") as f:
        response_body = f.read()

    return response_body
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.12.31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前情回顾
  • 测试返回中文内容到浏览器中
  • 将url路径传递字典参数environ['REQUEST_URI']中,实现页面的返回
  • sys.argv 的使用 - 输入参数至脚本之中
  • 编写设置服务端的端口号代码
  • 最后贴上完整代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档