Webservice Python utilisant un serveur Apache existant

a marqué ce sujet comme résolu.

Hello,

J'ai réalisé un petit webservice qui, en fonction des paramètre de requête, renvoie un fichier à l'utilisateur.

Il ressemble à ça (j'ai laissé que les parties intéressantes) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/env python
# -*- coding: utf-8 -*

from http.server import BaseHTTPRequestHandler, HTTPServer
import os
import os.path as op
from urllib.parse import parse_qs, urlparse

PORT_NUMBER = 8080

class RequestHandler(BaseHTTPRequestHandler):

  def display_msg(self, text):
      'Display a ``text`` on the web page.'

      self.send_header('Content-type', 'text/plain; charset=utf-8')
      self.send_header('Content-Disposition', 'inline')
      self.end_headers()
      self.wfile.write(bytes(text, 'UTF-8'))

  def send_file(self, file_path, file_name):
      '''Send a file to the user (open the *Download File dialog box*).

- ``file_path``: The path of the file to send to the user.
- ``file_name``: The name of the file.
'''

      self.send_header('Content-type', 'Application/xml')
      self.send_header('Content-Disposition', 'attachment; filename="%s"' % file_name)
      self.end_headers()
      with open(file_path, 'rb') as f:
          self.wfile.write(f.read())

  def do_GET(self):
      'On GET events, get the arguments and generate a file.'

      self.send_response(200)

      # Get URL arguments
      url_args = parse_qs(urlparse(self.path).query, keep_blank_values=True)

      # Generate the file from URL GET arguments
      file_path, file_name, error = build_file(url_args)

      if error is not None:
          self.display_msg('ARGUMENT ERROR: %s.' % error)

      # Check if the converted file is present on the disk
      if not op.isfile(file_path):
          self.display_msg('CONVERSION ERROR: The file has not been generated.')
          return

      # Returns the converted file then remove it
      self.send_file(file_path, file_name)
      os.remove(file_path)

if __name__ == '__main__':
  # Launch the web server
  try:
      server = HTTPServer(('', PORT_NUMBER), RequestHandler)
      print('Web server started. Please go to 127.0.0.1:%i.' % PORT_NUMBER)
      server.serve_forever()
  except KeyboardInterrupt:
      print('Interrupted by the user - shutting down the web server.')
      server.socket.close()

Comme vous pouvez le voir, ce script monte un serveur web sur le port 8080.

Je dois maintenant installer mon script sur un serveur de prod, sur lequel est déjà installé un serveur web Apache. Je dois donc modifier mon script afin qu'il utilise Apache plutôt que de créer un nouveau webservice.

J'ai correctement configuré le serveur de manière à ce qu'il puisse exécuter du Python. Ainsi ce bout de code :

1
2
3
4
5
6
7
8
import cgitb
cgitb.enable()
# https://docs.python.org/2/library/cgitb.html

print ("Content-Type: text/plain; charset=utf-8")
print ("")

print("Hello World!")

Affiche correctement Hello World! sur la page web.

Comment puis-je écouter les requêtes GET et envoyer des données à l'utilisateur (j'imagine qu'il y a une méthode pour ne pas avoir à écrire les en-têtes HTTP à la main) en utilisant le serveur Apache existant ? La doc de cgitb est très peu fournie sur ce sujet.

Merci !

+0 -0

Le standard pour faire communiquer un script python et un serveur web s'appel WSGI. Tu trouvera facilement des infos dessus. Mais je te conseille fortement de pas le faire à la main.

Pour des petits webservices utiliser flask voir bottle (si tu veux réduire les dépendances) sont probablement des bien meilleurs solutions. Ce sont des solutions simple et fiable plutôt que de vouloir ré-inventer la roue.

Attention avec flask cependant : il faut l'utiliser impérativement derrière un serveur style wsgi, aussi tentant que cela puisse paraître de le laisser se servir tout seul.

Son serveur builtin est vraiment pas taillé pour la prod ; il suffit de s'endormir sur F5 pour le faire se vautrer.

+0 -0

Yop, merci pour la suggestion ! Du coup je suis parti sur Flask, je connaissais pas ce petit framework, ça a l'air vraiment chouette.

Alors je n'ai aucun soucis avec ce dernier quand je l'utilise avec son serveur builtin. Par contre j'ai un soucis quand j'essaie de passer sur le serveur Apache, je me permet de l'évoquer sur ce topic :

J'ai créé un fichier /var/www/mon_app/mon_app.py dans lequel se trouve le Hello World de la doc :

1
2
3
4
5
6
7
8
9
#!/usr/bin/python3
# -*- coding: UTF-8 -*-

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

et, toujours selon la doc, un fichier /var/www/mon_app/mon_app.wsgi :

1
from mon_app import app as application

Pour finir, voici mon fichier /etc/apache2/sites-available/mon_app.conf :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<VirtualHost *:8080>
    DocumentRoot /var/www/mon_app

        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>

        <Directory /var/www/mon_app>
                Options Indexes FollowSymLinks MultiViews ExecCGI
                AllowOverride None
                Order allow,deny
                allow from all
        AddHandler cgi-script .py
        </Directory>

    WSGIScriptAlias /mon_app /var/www/mon_app/mon_app.wsgi
    #WSGIDaemonProcess nc2cdf user=user1 group=group1 threads=5

        ErrorLog ${APACHE_LOG_DIR}/error_mon_app.log
        CustomLog ${APACHE_LOG_DIR}/access_mon_app.log combined

        # Possible values: debug, info, notice, warn, error, crit, alert, emerg.
        LogLevel warn
</VirtualHost>

Quand je me rend sur http://127.0.0.1:8080/, j'ai le contenu du dossier /var/www/mon_app au lieu du Hello, World!. Par ailleurs si je me rend sur http://127.0.0.1:8080/mon_app.py, j'ai une Internal Server Error associée à ce log :

1
2
3
4
5
[Mon Nov 28 10:52:36.176365 2016] [cgi:error] [pid 6298] [client 127.0.0.1:39192] AH01215: Traceback (most recent call last):: /var/www/mon_app/mon_app.py
[Mon Nov 28 10:52:36.176462 2016] [cgi:error] [pid 6298] [client 127.0.0.1:39192] AH01215:   File "/var/www/mon_app/mon_app.py", line 8, in <module>: /var/www/mon_app/mon_app.py
[Mon Nov 28 10:52:36.176483 2016] [cgi:error] [pid 6298] [client 127.0.0.1:39192] AH01215:     from flask import Flask: /var/www/mon_app/mon_app.py
[Mon Nov 28 10:52:36.176506 2016] [cgi:error] [pid 6298] [client 127.0.0.1:39192] AH01215: ImportError: No module named flask: /var/www/mon_app/mon_app.py
[Mon Nov 28 10:52:36.178773 2016] [cgi:error] [pid 6298] [client 127.0.0.1:39192] End of script output before headers: mon_app.py

(et oui bien sur Flask est installé avec Pip, vu que j'arrive a l'utiliser avec le serveur builtin.)

Savez-vous d'où cela peut venir ?

Merci ! :-)

+0 -0

Hello !

Bon, j'ai abandonné Flask car ce dernier nécessitait des dépendances qui posaient problème car le serveur de prod sur lequel je bosse est assez vieux.

Mais j'ai tout de même pu répondre à ma question initiale ici !

Avec la même config que mon premier post, il suffit d'avoir un script du genre :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/usr/bin/python

import cgi, cgitb # Import modules for CGI handling

form = cgi.FieldStorage() # Create instance of FieldStorage
pseudo = form.getvalue('pseudo') # Get data from fields

print "Content-type:text/html\r\n\r\n"
print "<html><body>"
print "<p>Hello %s !</p>" % pseudo
print "</body></html>"

et du coup, http://127.0.1.1:8080/cgi_test?pseudo=Roipoussiere m'affiche bien Hello Roipoussiere !.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte