Web Service explaining – Build a Web server (ii)

Source: Internet
Author: User
Tags install django pip install django virtual environment virtualenv

Guide Once upon a while, the Python Web framework you chose restricts the Web servers you can choose from, and vice versa. If a framework and server are designed to work together, then everything is fine.

In the first part, I raised the question: "How do you fit a Django, Flask or Pyramid application on a Web server that you just built, instead of having to make changes to the Web server to accommodate a variety of different web frameworks?" "We can find the answer in this article.

But you may be facing (or have faced) the problem of trying to match a pair of frameworks and servers that are not adaptable:

Basically, you need to choose the frameworks and servers that work together, not the ones you want to use.

So, how do you make sure that your Web server and many different Web frameworks work together without making any changes to the code of your Web server or framework? The answer to this question is the Python Web Server gateway Interface (Web server gateways Interface) (abbreviated as WSGI, read "Wizgy").

WSGI allows developers to choose between Web frames and Web server types independently of each other. Now you can really match the WEB server with the framework and then choose your favorite pair. For example, you can use Django,Flask or Pyramid, with Gunicorn,nginx/uwsgi , or waitress to combine. Thanks to WSGI's support for both the server and the framework, we can really choose their collocation.

So, WSGI is the answer to the question I raised in the first part and repeated at the beginning of this article. Your Web server must implement the server portion of the WSGI interface, and the modern Python WEB framework has implemented the framework of the WSGI interface, allowing you to use any framework directly in the Web server without changing any server code to be compatible with a particular web framework.

Now that you know that Web server and web framework support for WSGI allows you to choose the most appropriate pair to use, and it also facilitates the developers of servers and frameworks, so they can focus on what they are good at, without having to touch another part of the code. Other languages also have similar interfaces, such as Java, which has a Servlet API, and Ruby has Rack.

These theories are good, but I bet you're saying, "Show me the code!," Okay, let's take a look at this very small WSGI server implementation:

# # # Use Python 2.7.9, test under Linux and Mac OS X via import socketimport stringioimport sysclass wsgiserver (object): Address_fam ily = socket.af_inet Socket_type = socket. Sock_stream request_queue_size = 1 def __init__ (self, server_address): # # # Create a listener socket Self.listen_sock ET = Listen_socket = Socket.socket (self.address_family, Self.socket_type) # # # allows multiplexing of the same Address listen_socket.setsockopt (socket. Sol_socket, SOCKET. SO_REUSEADDR, 1) # # # Bind address Listen_socket.bind (server_address) # # Activate socket Listen_socket.listen (SE lf.request_queue_size) # # # Gets the name of the host and port host, port = Self.listen_socket.getsockname () [: 2] Self.server_n    AME = Socket.getfqdn (host) Self.server_port = port # # # Returns the response header field set by the WEB framework/App Self.headers_set = []  def set_app (self, application): self.application = Application def serve_forever (self): Listen_socket =  Self.listen_socket while True:          # # # Gets the new client connection self.client_connection, client_address = Listen_socket.accept () # # # Processing a request after closing Close the connection, and then loop to wait for another connection to establish Self.handle_one_request () def handle_one_request (self): Self.request_data = Request _data = SELF.CLIENT_CONNECTION.RECV (1024) # # # in ' curl-v ' style output format request data print ('. '. Join (' < {line        }/n '. Format (line=line) for line in Request_data.splitlines ())) Self.parse_request (Request_data)        # # # Build an environment variable dictionary from request data env = Self.get_environ () # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Retrieve results  result = Self.application (env, Self.start_response) # # # Constructs a response, loopback to client Self.finish_response (result) def        Parse_request (self, text): Request_line = Text.splitlines () [0] request_line = Request_line.rstrip ('/r/n ') # # # divides the request line into several parts (Self.request_method, # GET Self.path, #/hello Self.request_versi On # http/1.1) = Request_line.split () def get_environ (self): env = {} # # # # # # # # # # The following code snippet does not follow the PEP8 rule, but this layout is intended to be done by emphasizing # #        Variables and their values in order to achieve their display purpose. # # # # # WSGI required variable env[' wsgi.version ' = (1, 0) env[' wsgi.url_scheme '] = ' http ' env[' WSG I.input '] = Stringio.stringio (self.request_data) env[' wsgi.errors '] = Sys.stderr env[' Wsgi.mult        Ithread '] = False env[' wsgi.multiprocess '] = False env[' wsgi.run_once '] = false # # # CGI Required Variable env[' Request_method ' = self.request_method # GET env[' path_info '] = self.path #/he Llo env[' server_name '] = self.server_name # localhost env[' server_port '] = str (self.server _port) # 8888 return env def start_response (self, status, Response_headers, Exc_info=none): # # # Add the necessary servers The header field server_headers = [(' Date ', ' Tue, ' 12:54:48 GMT '), (' Server ', ' Wsgiserver 0.2 '),] self.headers_set = [status, Response_headers + server_headers] # # # In order to follow the WSGI protocol, Start_response The function must return a ' write ' # # # Callable Object (return value. Write can be called as a function).        For simplicity, we are here to ignore this detail. # # # return Self.finish_response def finish_response (self, result): try:status, response_headers = SE Lf.headers_set response = ' http/1.1 {status}/r/n '. Format (status=status) for header in Response_heade                 Rs:response + = ' {0}: {1}/r/n '. Format (*header) response + = '/r/n ' for data in result: Response + = data # # # in the ' curl-v ' style output format request data print ('. Join (' > {L ine}/n '. Format (line=line) for line in Response.splitlines ())) self.client_connection . Sendall (response) finally:self.client_connection.close () server_address = (HOST, PORT) = ' ', 8888def ma Ke_server (server_address, application): Server = WSGiserver (server_address) Server.set_app (application) return Serverif __name__ = ' __main__ ': If Len (SYS.ARGV) &lt ; 2:sys.exit (' Provide a WSGI Application object as Module:callable ') App_path = sys.argv[1] module, applicatio n = app_path.split (': ') module = __import__ (module) application = GetAttr (module, application) httpd = Make_serve R (server_address, application) print (' wsgiserver:serving HTTP on port {port} .../n '. Format (port=port)) httpd.serve_ Forever ()

Of course, this code is a lot longer than the first part of the server code, but it is still very short (less than 150 lines), you can easily understand it, without needing to delve into the details. The server code above can also do more-it can be used to run some of the WEB applications that your favorite framework writes out, which can be Pyramid,flask,django or other Python WSGI frameworks.

Don't you believe it? Try it yourself. Save the above code as


, or download it directly from Github. If you plan to run it without any parameters, it will complain and then exit.

$ python webserver2.pyprovide a WSGI Application object as Module:callable

What it wants to do is actually serve your Web app, and that's the highlight. In order to run this server, all you need is to install Python. However, if you want to run Pyramid,flask or Django apps, you'll need to install those frameworks first. Let's put all three of them on the wall. The installation method I recommend is installed through virtualenv . By following these steps, you can create and activate a virtual environment and install the three Web frameworks above.

$ [sudo] pip install virtualenv$ mkdir ~/envs$ virtualenv ~/envs/lsbaws/$ cd ~/envs/lsbaws/$ lsbin  include  lib$ so Urce bin/activate (lsbaws) $ pip install Pyramid (lsbaws) $ pip install flask (lsbaws) $ pip Install Django

Now you need to create a Web app. Let's start with the Pyramid first. Save the following code as


, and with just the


Put it in the same directory, or download the file directly from Github:

From pyramid.config import configuratorfrom pyramid.response import responsedef hello_world (Request):    return Response (        ' Hello World from pyramid!/n ',        content_type= ' Text/plain ',    ) config = Configurator () config.add_ Route (' Hello ', '/hello ') Config.add_view (hello_world, route_name= ' hello ') app = Config.make_wsgi_app ()

Now you can use your own WEB server to run your Pyramid app:

(lsbaws) $ python webserver2.py pyramidapp:appWSGIServer:Serving HTTP on port 8888 ...

You just let your server load the executable object appin the Python module pyramidapp . Now your server can receive requests and forward them to your Pyramid app. Enter Http://localhost:8888/hello in the browser, hit enter, and then look at the results:

You can also use the command line tool Curl to test the server:

$ curl-v Http://localhost:8888/hello ...

See what the server and Curl print to the standard output stream.

Now let's try Flask. Run the same steps as above.

From flask import flaskfrom flask Import Responseflask_app = Flask (' Flaskapp ') @flask_app. Route ('/hello ') def Hello_world ():    return Response (        ' Hello World from flask!/n ',        mimetype= ' Text/plain '    ) app = Flask_app.wsgi_app

Save the above code as


, or download it directly from Github and enter the following command to run the server:

(lsbaws) $ python webserver2.py flaskapp:appWSGIServer:Serving HTTP on port 8888 ...

Now enter Http://localhost:8888/hello in the browser and hit ENTER:

Again, try curl, and you'll see that the server returned a message generated by the Flask app:

$ curl-v Http://localhost:8888/hello ...

Can this server handle Django applications? Try it! But this task can be a bit complicated, so I suggest you clone the entire repository and then use the djangoapp.py in the Github repository to do the experiment. The source code here is primarily the Django HelloWorld project (already using Django's

django-admin.py Startproject

Command was created) added to the current Python path, and then imported the project's WSGI application. LCTT: In addition to the code shown here, you need a matching HelloWorld project to work, and the code can be found in the Github repository. )

Import syssys.path.insert (0, './helloworld ') from HelloWorld import Wsgiapp = wsgi.application

Save the above code as


, and then run the Django app with your WEB server:

(lsbaws) $ python webserver2.py djangoapp:appWSGIServer:Serving HTTP on port 8888 ...

Enter the following link and hit enter:

You can also test at the command line this time-you should have done it twice before-to confirm that the Django app handled your request:

$ curl-v Http://localhost:8888/hello ...

Have you tried it? Are you sure this server can work with those three frameworks? If you haven't tried, please go and try it. Reading is important, but the content of this series is re-built , which means you need to do some work yourself. Go and try it. Don't worry, I'll be waiting for you. No kidding, you really need to try each step yourself and make sure it works as expected.

Well, you've experienced the power of WSGI: it allows Web servers and web frameworks to be paired at will. WSGI provides a miniature interface between the Python WEB server and the framework. It is very simple and can be easily implemented on both the server and the framework side. The following code snippet shows the server and framework-side implementations of the WSGI interface:

def run_application (application): ""    "server-side code. "" "    # # # WEB application/Framework store HTTP status codes and HTTP response headers here,    # # # Server will pass this information to the client    Headers_set = []    # # # # # # # # # # of words to store wsgi/cgi environment variables Code    environ = {}    def start_response (status, Response_headers, Exc_info=none):        headers_set[:] = [Status, Response_headers] #    # # Server Wake executable variable "application", get response header    result = Application (environ, start_response)    # # # Server assembles an HTTP response and transmits it to the client    ... def app (environ, start_response): "" "    an empty WSGI app" ""    start_response (' 200 OK ', [(' Content-type ', ' Text/plain ')])    return [' Hello world! '] Run_application (APP)

This is how it works:

    1. The WEB framework provides a callable object application (the WSGI specification does not specify how it is implemented).
    2. Each time the WEB server receives an HTTP request from the client, it wakes up the callable object applition. It passes an environment variable dictionary environthat contains the WSGI/CGI variable to the object, and a callable object start_response.
    3. The WEB framework or app generates an HTTP status code and an HTTP response header, and then passes it to the start_response function, which the server stores. Also, the WEB framework or app returns the HTTP response body.
    4. The server assembles the status code, response header, and response body into an HTTP response and then transmits it to the client (this step is not in the WSGI specification, but logically this step should be included in the workflow.) So in order to clear this process, I wrote it out)

This is the graphical representation of this interface specification:

So far, you've seen the code for Web apps written with Pyramid, Flask, and Django, and you've seen how a Web server can implement the other half (server-side) WSGI specification with code. You've even seen how we can use a piece of code to implement one of the simplest WSGI Web applications without using any framework.

In fact, when you use the framework above to write a WEB application, you only work at a higher level, without having to deal directly with WSGI. But I know you must also be interested in the frame part of WSGI interface, because you are reading this article. So instead of Pyramid, Flask, or Django, we do it ourselves to create the simplest WSGI Web app (or web framework) and then run it with your server:

def app (environ, start_response): "" "    one of the simplest WSGI applications.    This is the beginning of your own WEB framework ^_^ ""    status = ' OK '    response_headers = [(' Content-type ', ' Text/plain ')]    Start_response (status, Response_headers)    return [' Hello World from a simple WSGI application!/n ']

Similarly, save the above code as


or download the file directly from Github, and then run the app on the WEB server, like this:

(lsbaws) $ python webserver2.py wsgiapp:appWSGIServer:Serving HTTP on port 8888 ...

Enter the following address in the browser, and then press ENTER. This is the result you should see:

You've just been learning how to create a Web server and write your own WSGI web framework! It's awesome!

Now, let's go back and see what the server is sending to the client. This is what the server generates when you use the HTTP client to invoke your Pyramid app:

This response is part of the HTTP response you see in the first part of this series, but it also comes with a few more things. For example, it has four HTTP headers that you've never seen before:content-type, content-length, Date , and Server. These header content is basically present in the response returned by each WEB server. However, they are not strictly required to appear. The purpose of these HTTP request/Response header fields is that it can pass you some extra information about the HTTP request/response.

Now that you know a little bit more about the WSGI interface, let me show you the source of the various parts of the HTTP response above:

I do not have any explanation for the environ dictionary above, but basically this dictionary must contain the WSGI and CGI variable values that are defined beforehand by the WSGI specification. When the server resolves an HTTP request, it gets the values of those variables from the request. This is what the environ dictionary should look like:

The WEB framework uses the information contained in the above dictionary to determine which view to use to handle the response, where to read the request body, and where to output the error message, if any, through the request path in the dictionary, the request action, and so on.

Now that you've created your own WSGI Web server, you've also made several Web applications using different Web frameworks. And you've created a simple WEB application and framework for yourself in the process. The process was tiring. Now let's review what your WSGI Web server needs to do with the WSGI app when it comes to service requests:

    • First, the server starts to work and then loads a callable object application, which is provided by your WEB framework or app
    • The server then reads a request
    • The server then resolves the request
    • The server then uses the request data to build a environ dictionary
    • It then invokes applicationwith the environ dictionary and a callable object Start_response as an argument, and gets the response body content.
    • The server then constructs an HTTP response using the response body returned by application , and the status code and response header content set by the start_response function.
    • Eventually, the server sends the HTTP response back to the client.

This is basically what the server is going to do. You now have a working WSGI server that can build Web App services for any web framework that follows the WSGI specification, such as Django, Flask, Pyramid, and the framework you just wrote yourself. The best part is that it works with a number of different Web frameworks without changing any server code. That's good.

Web Service explaining – Build a Web server (ii)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.