Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to write a simple Web framework with Python

2025-01-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

How to use Python to write a simple Web framework, many novices are not very clear about this, in order to help you solve this problem, the following editor will explain in detail for you, people with this need can come to learn, I hope you can get something.

I. Overview

In Python, WSGI (Web Server Gateway Interface) defines a standard interface between a Web server and a Web application (or Web framework). Under the WSGI specification, a variety of Web servers and Web frameworks can interact well.

Because of WSGI, it is also very easy to write a simple Web framework in Python. However, like many other powerful software, it is not easy to implement a feature-rich, robust and efficient Web framework; if you are going to do so, it may be more appropriate to use an off-the-shelf Web framework (such as Django, Tornado, web.py, etc.).

Try to write a Web framework similar to web.py. Well, I admit I'm exaggerating: first, web.py is not simple; second, it only focuses on implementing the URL scheduling (URL dispatch) part.

2. Start with demo_app

First of all, as a preliminary experience, we can use wsgiref.simple_server to build a trivial Web application:

#! / usr/bin/env python #-*-coding: utf-8-*-from wsgiref.simple_server import make_server, demo_app httpd = make_server (', 8086, demo_app) sa = httpd.socket.getsockname () print 'http://{0}:{1}/'.format(*sa) # Respond to requests until process is killed httpd.serve_forever ()

Run the script:

$python code.py http://0.0.0.0:8086/

Open the browser and type http://0.0.0.0:8086/ to see: a line of "Hello world!" And many environmental variables.

3. Application in WSGI

WSGI states that application is a callable object (callable object) that accepts two parameters environ and start_response and returns a string iteration object.

Where callable objects include functions, methods, classes, or instances with the _ _ call__ method; environ is a dictionary object, including CGI-style environment variables (CGI-style environment variables) and WSGI necessary variables (WSGI-required variables); and start_response is a callable object that accepts two regular parameters (status,response_headers) and one default parameter (exc_info) A string iteration object can be a list of strings, a generator function, or an iterable instance with a _ _ iter__ method. See Specification Details for more details.

A typical application implementation is given in The Application/Framework Side:

#! / usr/bin/env python #-*-coding: utf-8-*-"application.py" def simple_app (environ, start_response): "Simplest possible application object" status = '200 OK' response_headers = [(' Content-type', 'text/plain')] start_response (status, response_headers) return [' Hello world!\ n']

Now replace demo_app with simple_app:

#! / usr/bin/env python #-*-coding: utf-8-*-"code.py" from wsgiref.simple_server import make_server from application import simple_app as app if _ _ name__ = ='_ main__': httpd = make_server ('', 8086 " App) sa = httpd.socket.getsockname () print 'http://{0}:{1}/'.format(*sa) # Respond to requests until process is killed httpd.serve_forever ()

After running the script code.py, visit http://0.0.0.0:8086/ and you can see the familiar sentence on that line: Hello world!

Fourth, distinguish between URL

After tossing around for a while, you'll find that no matter how you change the path part of URL, you get the same response. Because simple_app only recognizes the host+port part.

In order to differentiate the path part of URL, you need to modify the implementation of application.py.

First, use classes to implement application:

#! / usr/bin/env python #-*-coding: utf-8-*-"application.py"class my_app: def _ _ init__ (self, environ, start_response): self.environ = environ self.start = start_response def _ iter__ (self): status = '200 OK' response_headers = [(' Content-type' 'text/plain')] self.start (status, response_headers) yield "Hello world!\ n"

Then, add differentiated processing for the path portion of the URL:

#! / usr/bin/env python #-*-coding: utf-8-*-"application.py" class my_app: def _ _ init__ (self, environ Start_response): self.environ = environ self.start = start_response def _ _ iter__ (self): path = self.environ ['PATH_INFO'] if path = "/": return self.GET_index () elif path = = "/ hello": return self.GET_hello () else: Return self.notfound () def GET_index (self): status = '200 OK' response_headers = [(' Content-type') 'text/plain')] self.start (status, response_headers) yield "Welcome!\ n" def GET_hello (self): status =' 200 OK' response_headers = [('Content-type',' text/plain')] self.start (status Response_headers) yield "Hello world!\ n" def notfound (self): status = '404 Not Found' response_headers = [(' Content-type', 'text/plain')] self.start (status, response_headers) yield "Not Found\ n"

Modify the from application import simple_app as app in code.py and replace simple_app with my_app to experience the effect.

V. Reconstruction

Although the above code works, there are many problems in coding style and flexibility, which will be refactored step by step.

1. Regular matching URL

Eliminate URL hard coding and increase the flexibility of URL scheduling:

#! / usr/bin/env python #-*-coding: utf-8-*-"application.py" import re # modify point class my_app: urls = ("/", "index"), ("/ hello/ (. *)", "hello") ) # modify point def _ _ init__ (self, environ, start_response): self.environ = environ self.start = start_response def _ _ iter__ (self): # modify point path = self.environ ['PATH_INFO'] method = self.environ [' REQUEST_METHOD'] for pattern Name in self.urls: M = re.match ('^'+ pattern +'$', path) if m: # pass the matched groups as arguments to the function args = m.groups () funcname = method.upper () +'_'+ name if hasattr (self, funcname): func = getattr (self Funcname) return func (* args) return self.notfound () def GET_index (self): status = '200 OK' response_headers = [(' Content-type', 'text/plain')] self.start (status, response_headers) yield "Welcome!\ n" def GET_hello (self Name): # modification point status = '200 OK' response_headers = [(' Content-type', 'text/plain')] self.start (status, response_headers) yield "Hello% s!\ n"% name def notfound (self): status =' 404 Not Found' response_headers = [('Content-type') 'text/plain')] self.start (status, response_headers) yield "Not Found\ n"

2 、 DRY

Eliminate duplicate code in the GET_* method and allow them to return strings:

#! / usr/bin/env python #-*-coding: utf-8-*-"application.py" import re class my_app: urls = ("/", "index"), ("/ hello/ (. *)", "hello"),) def _ _ init__ (self, environ Start_response): # modify point self.environ = environ self.start = start_response self.status = '200 OK' self._headers = [] def _ _ iter__ (self): # modify point result = self.delegate () self.start (self.status Self._headers) # converts the return value result (string or string list) to an iterative object if isinstance (result Basestring): return iter ([result]) else: return iter (result) def delegate (self): # Modification point path = self.environ ['PATH_INFO'] method = self.environ [' REQUEST_METHOD'] for pattern, name in self.urls: M = re.match ('^'+ pattern +'$') Path) if m: # pass the matched groups as arguments to the function args = m.groups () funcname = method.upper () +'_'+ name if hasattr (self, funcname): func = getattr (self) Funcname) return func (* args) return self.notfound () def header (self, name, value): # modify point self._headers.append ((name, value)) def GET_index (self): # modify point self.header ('Content-type' 'text/plain') return "Welcome!\ n" def GET_hello (self, name): # modify point self.header (' Content-type' 'text/plain') return "Hello% s!\ n"% name def notfound (self): # modification point self.status =' 404 Not Found' self.header ('Content-type',' text/plain') return "Not Found\ n"

3. Abstract the framework.

In order to abstract the class my_app into a separate framework, you need to make the following changes:

Stripping out the specific processing details: urls configuration and GET_* methods (instead of implementing the corresponding GET methods in multiple classes)

Implement the method header as a class method (classmethod) to facilitate external calls as functional functions

Use an instance with a _ _ call__ method to implement application instead

Modified application.py (final version):

#! / usr/bin/env python #-*-coding: utf-8-*-"" application.py "import re class my_app:" my simple web framework "headers = [] def _ init__ (self, urls= (), fvars= {}): self._urls = urls self._fvars = fvars def _ call__ (self, environ) Start_response): self._status = '200 OK' # default state OK del self.headers [:] # clear the previous headers result = self._delegate (environ) start_response (self._status, self.headers) # convert the return value result (string or string list) to an iterative object if isinstance (result Basestring): return iter ([result]) else: return iter (result) def _ delegate (self, environ): path = environ ['PATH_INFO'] method = environ [' REQUEST_METHOD'] for pattern, name in self._urls: M = re.match ('^'+ pattern +'$' Path) if m: # pass the matched groups as arguments to the function args = m.groups () funcname = method.upper () # method name uppercase (e.g. GET, POST) klass = self._fvars.get (name) # find the class object if hasattr (klass) based on the string name Funcname): func = getattr (klass, funcname) return func (klass (), * args) return self._notfound () def _ notfound (self): self._status = '404 Not Found' self.header (' Content-type', 'text/plain') return "Not Found\ n" @ classmethod def header (cls Name, value): cls.headers.append ((name, value))

Corresponding to the modified code.py (final version):

#! / usr/bin/env python #-*-coding: utf-8-*-"code.py" from application import my_app urls = (("/", "index"), ("/ hello/ (. *)", "hello"),) wsgiapp = my_app (urls, globals () class index: def GET (self): my_app.header ('Content-type') 'text/plain') return "Welcome!\ n" class hello: def GET (self, name): my_app.header (' Content-type', 'text/plain') return "Hello% s!\ n"% name if _ _ name__ = =' _ main__': from wsgiref.simple_server import make_server httpd = make_server ('', 8086) Wsgiapp) sa = httpd.socket.getsockname () print 'http://{0}:{1}/'.format(*sa) # Respond to requests until process is killed httpd.serve_forever ()

Of course, you can also configure more URL mappings in code.py and implement the corresponding classes to respond to requests.

Mainly referred to How to write a web framework in Python (the author anandology is one of the two maintainers of web.py code, the other is the famous but died young Aaron Swartz), on this basis made some adjustments and modifications, and mixed with some of their own ideas.

Is it helpful for you to read the above content? If you want to know more about the relevant knowledge or read more related articles, please follow the industry information channel, thank you for your support.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report