WEBAPP开发教程Web框架
凌雪 2018-10-25 来源 :网络 阅读 810 评论 0

摘要:本文将带你了解WEBAPP开发教程Web框架,希望本文对大家学WEBAPP有所帮助。

本文将带你了解WEBAPP开发教程Web框架,希望本文对大家学WEBAPP有所帮助。


基础知识
    file-like Object
    像open()函数返回的这种有个read()方法的对象,在Python中统称为file-like   Object。除了file外,还可以是内存的字节流,网络流,自定义流等等。
    file-like Object不要求从特定类继承,只要写个read()方法就行。
    StringIO就是在内存中创建的file-like Object,常用作临时缓冲。
    在web 框架中,wsgi.input变量就是一个file-like Object的输入流,因此可以使用read()方法。
   
    import导入的类(或函数)要通过前缀的方式访问
    例如import httpCore后,可以通过 httpCore.getFileInfo   来访问httpCore.py中定义的getFileInfo()函数,变量同样要通过此种方式访问。编写web服务器
    当然,像直接引用getFileInfo()函数,可以通过from httpCore import getFileInfo的方式(这样就可以直接通过getFileInfo   来访问httpCore.py中定义的getFileInfo()函数)
   
   
    __name__和__file__的作用
   
    #文件名为 " ok7.py " import os,os.path print __name__ print   __file__print os.path.abspath(__file__)print os.path.dirname(os.path.abspath(__file__))print   os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    结果:
   
   
   
   
    编码问题
      最早的Python只支持ASCII编码,普通的字符串'ABC'在Python内部默认是ASCII编码的。Python在后来添加了对Unicode的支持,以Unicode表示的字符串用u'xxx'表示,u'xxx'只能是Unicode编码。Python   2.x版本虽然支持Unicode,但在语法上需要 'xxx' 和 u'xxx' 两种字符串表示方式。
   
    >>> u'中'u'\u4e2d'
    所以 u '中' = u ' \u4e2d ',只不过一个是计算机的存储形式,一个是计算机的显示形式。Unicode和 utf - 8   之间用encode和decode方法相互转化。
   
    URL编码
   
    python的url编码函数是在类urllib库中,使用方法是:
    编码:urllib.quote(string[, safe]),除了三个符号“ _ . -   ”外,将所有符号编码,后面的参数safe是不编码的字符,
    使用的时候如果不设置的话,会将斜杠,冒号,等号,问号都给编码了。
    如下:>>> import urllib >>> print   urllib.quote("//neeao.com/index.php?id=1")   http%3A//neeao.com/index.php%3Fid%3D1   这样在使用urllib.urlopen打开编码后的网址的时候,就会报错了。
    设置不编码的符号:>>> print   urllib.quote("//neeao.com/index.php?id=1",":?=/")   //neeao.com/index.php?id=1 这下就好了。
    CGI环境变量
   
    所有的CGI程序都接收以下的环境变量,这些变量在CGI程序中发挥了重要的作用:
   
    变量名
   
    描述
   
    CONTENT_TYPE
   
      这个环境变量的值指示所传递来的信息的MIME类型。目前,环境变量CONTENT_TYPE一般都是:application/x-www-form-urlencoded,他表示数据来自于HTML表单。
   
    CONTENT_LENGTH
   
      如果服务器与CGI程序信息的传递方式是POST,这个环境变量即使从标准输入STDIN中可以读到的有效数据的字节数。这个环境变量在读取所输入的数据时必须使用。
   
    HTTP_COOKIE
   
    客户机内的 COOKIE 内容。
   
    HTTP_USER_AGENT
   
    提供包含了版本数或其他专有数据的客户浏览器信息。
   
    PATH_INFO
   
    这个环境变量的值表示紧接在CGI程序名之后的其他路径信息。它常常作为CGI程序的参数出现。
   
    QUERY_STRING
   
      如果服务器与CGI程序信息的传递方式是GET,这个环境变量的值即使所传递的信息。这个信息经跟在CGI程序名的后面,两者中间用一个问号'?'分隔。
   
    REMOTE_ADDR
   
    这个环境变量的值是发送请求的客户机的IP地址,例如上面的192.168.1.67。这个值总是存在的。而且它是Web客户机需要提供给Web服务器的唯一标识,可以在CGI程序中用它来区分不同的Web客户机。
   
    REMOTE_HOST
   
    这个环境变量的值包含发送CGI请求的客户机的主机名。如果不支持你想查询,则无需定义此环境变量。
   
    REQUEST_METHOD
   
    提供脚本被调用的方法。对于使用 HTTP/1.0 协议的脚本,仅 GET 和 POST 有意义。
   
    SCRIPT_FILENAME
   
    CGI脚本的完整路径
   
    SCRIPT_NAME
   
    CGI脚本的的名称
   
    SERVER_NAME
   
    这是你的 WEB 服务器的主机名、别名或IP地址。
   
    SERVER_SOFTWARE
   
    这个环境变量的值包含了调用CGI程序的HTTP服务器的名称和版本号。例如,上面的值为Apache/2.2.14(Unix)
   
   
    cookie
    所谓cookie就是在客户访问服务器端脚本的同时,通过客户的浏览器,在客户硬盘上写入纪录数据   ,当下次客户再次访问脚本时取回数据信息,从而达到身份判别的功能,cookie常用在密码判断中 。
   
    HTML设置上传文件的表单需要设置enctype 属性为multipart/form-data:Form content types
    <html><body>     <form    enctype="multipart/form-data"  action="save_file.py"   method="post">     <p>File: <input type="file"   name="filename" /></p>     <p><input type="submit" value="Upload"   /></p>     </form></body></html>
   
   
    enctype语法
    <form enctype="value">    #value指属性值
   
    属性值
   
    描述
   
    application/x-www-form-urlencoded
   
    在发送前编码表单的所有字符(默认)
   
    multipart/form-data
   
   
    不对字符编码。
   
    在使用包含文件上传控件的表单时,必须使用该值。
   
   
    text/plain
   
    空格转换为 "+" 加号,但不对特殊字符编码。
   
    结果:
   
   
    save_file.py脚本文件代码如下:
    #coding=utf-8#!/usr/bin/python import cgi, osimport cgitb; cgitb.enable()   form = cgi.FieldStorage() # 获取文件名fileitem = form['filename'] # 检测文件是否上传if   fileitem.filename:   # 设置文件路径    fn =   os.path.basename(fileitem.filename)     open('/tmp/' + fn, 'wb').write(fileitem.file.read())        message = 'The file "' + fn +   '" was uploaded successfully'     else:   message = 'No file was   uploaded'   print   """\Content-Type: text/html\n<html><body>     <p>%s</p></body></html>""" %   (message,)
    datetime strftime函数:
   
   
    @decorator原理分析:所谓返回值就是指 函数的执行结果
    # -*- coding:utf-8 -*- import functools def log(path): def   wrapper(func): func.__bbbb__ = path return func            #wrapper的返回值(执行结果) return   wrapper                   #log函数的返回值(执行结果) @log('x')def now(): return 'hello' print   now.__bbbb__
    得到:x
   
    如果是:
    # -*- coding:utf-8 -*- import functools def log(path): def   wrapper(func): func.__bbbb__ = path return func() return wrapper   @log('x')def now(): return 'hello' print now.__bbbb__得到:
   
    结果说‘str’对象没有属性‘ __bbbb__ ’,并且什么都没有输出,为什么会这样?
   
    观察包装函数,
    @log('x')def now(): return 'hello'相当于执行log( ' x ' )( now ),首先log( ' x '   )返回wrapper闭包(函数),同时' x ' 作为参数传递给wrapper,得到的是wrapper( now   ),这是直接执行wrapper函数啊!
    凡是函数名加括号的必然就是执行函数啊!!不加括号那就是函数名。
    wrapper函数也是有返回值的,返回值是执行now( ) (return   fun()),这样一来执行wrapper函数就是执行了now()函数,返回 ‘hello’。至于 “ print   now.__bbbb__ ”,now现在变成了指代字符串‘ hello ’——即now==log('x')(now)=wrapper(now)
     , 也就是 wrapper()   函数的返回值,wrapper() 函数的返回值就是字符串‘ hello ’。
   
   
    我们总结:
    某函数调用了包装函数,那么“某函数”的名字就不再指代某函数,而是指代包装函数的执行结果。
    如下例:
   
    def log(text):    def   decorator(func):        def   wrapper(*args, **kw):            print   '%s %s():' % (text, func.__name__)              return func(*args, **kw)          return wrapper    return   decorator @log('x')def now():    return   'hello'用now()函数调用包装函数log后,now就不再指代原函数,而是指代now==log('x')(now)=decorator(now),即执行decorator()函数的结果——返回的wrapper函数,也就是说now现在指代wrapper函数。现在验证一下:
   
   
    解决这一问题的办法可以用functools的functoos.wraps(func)函数——让“某函数”的函数名还指代某函数,不因调用了@decorator而指向改变!
   
    再回到之前的例子,
    我们想做的是为now()函数增加一个属性__bbbb__,但是:
    1)" print now.__bbbb__  "  的 now已经不再指代 now()   函数,而是wapper的返回值‘hello’字符串,所以无法正确输出 now.__bbbb__  ;
    2)我们是已经为原本的 now()   函数函数增加了属性,但是那是局部性的行为,离开函数即失效,即就算用functools的functoos.wraps(func)函数——让now()函数的函数名now还指代now()函数,也依然无法正确输出 now.__bbbb__ 。
      还用我们总结的规律来解决这个问题,now==log('x')(now)=wrapper(now),执行wrapper(now),如果wrapper()函数的返回值是func,那么上面的两个问题都迎刃而解了:返回值是func名,即传进来的now,则不会改变now的指向(now==now),而now增加的属性也得以保留(作为func的附属品一并返回),在最初的例子中就是这么做的,所以可以正确输出。
   
    看Web框架中的例子:
    def view(path):    def   decorator(func):          @functools.wraps(func)          def wrapper(*args, **kw):               r= func(*args, **kw)              if isinstance(r, dict):                  logging.info('return Template')                return   Template(path,**r)  #wrapper函数的返回值            raise ValueError('Expect return a   dict when using @view() decorator.')          return wrapper      #decorator函数的返回值    return decorator      #view函数的返回值 def post(path): def   _decorator(func): func.__web_route__ = path func.__web_method__ =   'POST' return func return _decorator   @view('test/view.html') @post('/post/:id')def testpost(): return dict(a=1)   print testpost.__web_route__print testpost.__web_method__
      两个装饰器叠加使用,默认先执行里面的装饰器,即@post('/post/:id'),可以立即获得 testpost.__web_route__ 和   testpost.__web_method__;再调用外面的装饰器@view('test/view.html')得到一个wrapper()函数,但并未执行(未激活,返回值是Template的一个实例),最后需要激活wrapper函数,才能得到那个Template实例。
   
    dir()函数和getattr()的使用
    我有一个urls.py文件,内容如下:
    # -*- coding: utf-8 -*- import logging from transwarp.web import get, view   from models import User, Blog, Comment @view('test_users.html')@get('/')def   test_users():    users =   User.find_all()    return   dict(users=users)
    执行下列命令:
   
   
   
    python的Web框架 bottle
   
   
    Web各部分之间的逻辑关系 (WSGI,很好的资料!)
   
     environ:
     it is a dict data structure   including all http request information.
    Nginx/Apache/lighttpd:相当于一个request   proxy,根据配置,把不同的请求转发给不同的server处理,例如静态的文件请求自己处理,这个时候它就像一个web   server,对于fastcgi/python这样的请求转发给flup这样的Server/Gateway进行处理;它们处理的是网络中最底下的两层—网络通信层和协议层。网络通信层:包括TCP网络通信的若干工作,比如bind,listen,connect,receive,send,disconnect;协议层:Apache可以处理http协议中的request和response,并且可以解释header和body;flup:一个用python写的web   server(实际是wsgi   server),也就是cgi中所谓的Server/Gateway,它负责接受apache/lighttpd转发的请求,并调用你写的程序(application),并将application处理的结果返回到apache/lighttpd;
      WSGI:它是Python应用程序或框架和Web服务器之间交互的接口规范,它规范了Python程序和服务器之间的通信。对于Apache/Nginx等服务器来说,只要支持WSGI规范,就可以保证所有(兼容WSGI的)Python程序能运行;对于Python程序或者框架来说,只要兼容WSGI,就可以保证能在所有(支持WSGI的)服务器上运行。fastcgi:fastcgi则是Python程序和服务器间底层的通信协议的规范。apache/lighttpd的一个模块,虽然flup可以作为一个独立的web   server使用,但是对于浏览器请求处理一般都交给apache/lighttpd处理,然后由apache/lighttpd转发给flup处理,这样就需要一个东西来把apache/lighttpd跟flup联系起来,这个东西就是fastcgi,它通过环境变量以及socket将客户端请求的信息传送给flup并接收flup返回的结果;框架framework:应该说有了上面的东西你就可以开始编写你的web程序了,但是问题是你就要自己处理浏览器的输入输出,还有cookie、session、模板等各种各样的问题了,框架的作用就是帮你把这些工作都做好了,它就是所谓的web   framework。
    在WSGI规范下,Web组件被分成三类:client, server, and middleware.  WSGI   apps(服从WSGI规范的应用)能够被连接起来处理一个request,这也就引发了中间件这个概念,中间件同时实现c端和s端的接口,c看它是上游s,s看它是下游的c。WSGI的s端所做的工作仅仅是接收请求,传给application(做处理),然后将结果response给middleware或client.   除此以外的工作都交给中间件或者application来做。对一个WSGI程序(callable的对象,可以是函数也可以是实现了__call__的对象),将request作为参数传入(不再是纯文本,而是经过包装),同样将经过包装的response作为响应返回。request/response的包装由Python标准库提供。
    wsgi是将web   server参数python化,封装为request对象传递给apllication命名的func对象并接受其传出的response参数,由于其处理了参数封装和结果解析,wsgi从外观上很接近cgi的调用方式,和unix环境中调用一个程序也很类似:给你一堆环境变量,加上参数。如果把wsgi   application看作一个脚本,其实就是外界通过wsgi调用python的脚本而已。 
      CGI是HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工具,是一种接口标准,根据其标准编写的程序须运行在网络服务器上,叫做cgi程序。
    利用程序的标准输入输出流stdin, stdout,完成 HTTP 通信。HTTP 是文本协议,每次请求的文本以标准输入流的形式进入服务器端 CGI   程序,创建进程;然后进程的标准输出流作为响应。CGI是比较原始的开发动态网站的方式。
    一个网站的动态内容是由程序生成的,这个程序接受客户端的请求,然后进行相应处理,再返回给客户端,客户端和服务端的通信是通过HTTP协议。然后我们会发现,这个程序在处理客户端请求的时候,大部分时候会进行很多重复的工作,比如说HTTP请求的解析。也就是说,你的程序需要解析HTTP请求,我的程序也需要解析。于是Web服务器如apache诞生了,apache解析这个HTTP请求,然后把这个请求的各种参数写进进程的环境变量,比如REQUEST_METHOD,PATH_INFO之类的。之后服务器会调用相应的程序来处理这个请求,这个程序也就是我们所要写的CGI程序了。它会负责生成动态内容,然后返回给apache,再由服务器转交给客户端。服务器和CGI程序之间通信,一般是通过进程的环境变量和管道。
    相比于cgi每一个请求都产生一个进程   ,fastcgi是启动一个常驻进程。每次当apache接到request的时候,通过IPC或者socket将请求内容发给fastcgi进程,然后fastcgi再转发给上层应用程序。
    后来又有人针对Python语言做了好多优化工作,产生了wsgi。经过wsgi之后,就全是Python程序了。request header、request   body和response被wsgi server整理成了Python对象。例如字典、FileIO等。
    framework拿到这些对象之后,就可以从request   header中找到URI,然后根据url映射关系找到对应的函数开始执行。之后将函数的返回值整理成wsgi可以接受的response对象(实际上就是一个str或者generator)返回给wsgi   server。
    Flask
    在使用Flask框架时,除了导入flask模块以外,第一句就是:
   
    app = Flask(__name__)
    传入的参数__name__表示这句脚本所在文件的名字,即 使用Flask框架的 “ .py ”   文件的名字,这样flask就能根据这个文件的名字找到其所在的目录,也就能找到这个目录下其他的包、模块、模板的根目录(HTML文件所在目录),这样才能完成模板调用和模块导入等工作,如:
   
    wsgiapp.py就是调用Flask框架的PY文件(也就是app = Flask(__name__)脚本语句所在的文件),那么__name__等于wsgiapp,其所在的目录就是web应用的根目录。
      而模板文件的目录就是templates,很容易根据__name__就找到这一目录以及其下的HTML文件共模板使用。其他的模块也容易找到并导入。
   
    之后,Flask类创建了一个实例(object)——app,这样就能够通过这个实例来使用Flask框架了,
    实际上,Web服务器使用WSGI协议,把接收自浏览器客户端的所有请求都交给这个程序实例( object)处理,实际上使用的是这个实例拥有的方法(route()就是app实例的URL   mapping方法)。
   
    @app.route('/', methods=['GET', 'POST'])def home():    return '<h1>Home</h1>'
    服务器并不知道什么请求要用什么函数处理,它只是把所有的请求通过WSGI interface 后都交给framework或WSGI   application来处理。
   
    那么怎样才能把从浏览器来的HTTP请求都正确的处理呢,这正是我们编写处理函数(URL映射函数)的初衷。
    如上例,就是“GET  /    HTTP/1.1”的HTTP请求的处理函数。你可能也发现了,每一个HTTP请求都务必有一个对应的处理函数,否则浏览器端的请求就会显示“ 404   not found ” . ——对请求的处理是遍历的,这是我们使用服务器端脚本语言如PHP,python等编写脚本的目的!
    那么Web框架要做的工作是什么呢?
    1. 将请求的 URL 映射到处理它的代码上;
    2. 动态地构造请求的 HTML 返回给客户端——HTML 中带有计算得到的值或者从数据库中取出来的信息。
   
    试想如果不使用Web框架我们要怎么做? 
    答:编写庞杂的wsgi application传递给名为 wsgiref  的WSGI   server(符合WSGI协议标准的服务器)调用。
   
    def application(environ, start_response):      method = environ['REQUEST_METHOD']      path = environ['PATH_INFO']      if method=='GET' and path=='/':          return handle_home(environ, start_response)    if method=='POST' and   path='/signin':        return   handle_signin(environ, start_response)      ...
    很显然我们手动拆分HTTP请求参数environ(WSGI server给的),提取出我们想要的信息,然后根据这些信息与很多的“ if ”匹配,“   定位 ”到精确的处理函数对HTTP请求进行具体的处理事务。
   
    使用了框架之后呢?省略了这个拆分参数和精确“定位”的步骤,我们要做的仅仅是编写URL处理函数即可——什么样的URL请求用什么样的处理函数,
    编写处理函数的唯一目的是“注册”——   即让浏览器客户端URL请求有处可寻处理函数(因为已注册的URL和相应的处理函数是绑定的——通过@decorator)。浏览器客户端URL请求匹配到了注册的URL,就是找到了相应的处理函数。
    注册能处理的所有URL,未注册的自然就是非法URL——浏览器请求这样的URL时,服务器将返回错误!
    框架是怎么拆分参数的:框架同样接收environ参数作为request线程级变量,通过request获取精确的参数信息。
    既然拆分参数的活交给框架来做,用户再用拆分的参数来定位处理函数就毫无意义了,那么怎样办呢?
      框架是如何做到精确定位的:框架内部消化拆分后的参数(比如浏览器客户端URL请求路径参数)。Flask在此之前已经通过Python的装饰器@decorator在内部自动地把注册的URL和处理函数给关联起来——根据URL来调用相应的处理函数。
    至于匹配方法,一般就是遍历,直到在注册的URL中找到第一个匹配的(正则匹配),然后调用这个注册的URL对应的处理函数(用@decorator绑定的)。
    pylons
    WSGI Middleware
   
   
    Within config/middleware.py a Pylons application is wrapped in successive   layers which add functionality.
    WSGI middleware is used extensively in Pylons to add functionality to the   base WSGI application. In Pylons, the 'base' WSGI Application is the   PylonsApp. It's responsible for looking in the environ dict that was
     passed in (from the Routes   Middleware).
    Some of the layers, like the Session, Routes, and Cache middleware, only   add objects to the environ dict, or add HTTP headers to the response (the   Session middleware for example adds the session cookie
     header). Others, such as the Status   Code Redirect, and the Error Handler may fully intercept the request   entirely, and change how it’s responded to.
    URL Dispacher:
   
    When the request passes down the middleware, the incoming URL gets parsed   in the RoutesMiddleware, and if it matches a URL (See URL Configuration),   the
     information about the controller   that should be called is put into the environ dict for use by   PylonsApp.
    The PylonsApp then
     attempts to find a controller in   the controllers directory that matches the name of the controller,   and searches for a class inside it by a similar scheme (controller
 name + ‘Controller’, ie,   HelloController). Upon finding a controller, its then called like any other   WSGI application using the same WSGI interface   that PylonsApp was
 called with.
   
    New in version 1.0: Controller name can also be a dotted path to the   module / callable that should be imported and called.
     For example, to use a controller   named ‘Foo’ that is in the ‘bar.controllers’ package, the controller name   would be bar.controllers:Foo.
   
    This is why the BaseController that resides in a   project’s lib/base.py module
     inherits   from WSGIController and
 has a __call__ method that   takes the environ and start_response.   The WSGIController locates
 a method in the class that   corresponds to the action that Routes found, calls it, and returns   the response completing the request.
    Response时最先调用的是PylonsApp,Request 阶段的PylonsApp和Response阶段的PylonsApp不是一回事,前者接受来自Request   阶段的中间层RoutesMiddleware解析得到的URL信息(存储在environ   dict中),根据URL信息得到相应的处理函数的信息存储在environ dict中,后者就要根据匹配好的处理函数的信息具体去controllers   directory中找处理函数了(同时搜寻该处理函数中的一个类,类的名字是controller name
     + ‘Controller’)。找到之后就执行该处理函数WSGI   application(按照WSGI接口标准调用),执行结果是an iterable (valid PEP 333 WSGI response),   which is then sent back as the response.。
    所有的处理函数(controller)都继承自BaseController,而lib/base.py模块里面的BaseController类继承自   WSGIController,且有一个__call__方法(参数是environ and start_response),WSGIController   找到一个和Routes匹配的动作相一致的方法(在__call__( )方法里面找),然后调用这个方法完成响应请求的工作。并且,In the event   that an action is not found
     to handle the request, the   Controller will raise an “Action Not Found” error if in debug mode, otherwise   a 404 Not Found error will be returned.    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之WebApp频道!

本文由 @凌雪 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程