Technologies avancées
Transcription
Technologies avancées
Technologies avancées Olivier Pons / 2016 Technologies avancées Objectif Connaître les bases Django Olivier Pons / 2015 Technologies avancées 2 – Site basique 1 – Django – Installation Etapes qui vont suivre 1. Installation de l'environnement 2. Installation de Django 3. Création d'un projet vide 4. Création d'une application ”monblog” Olivier Pons / 2016 3 / 71 Technologies avancées 2 – Site basique 1 – Django – Installation Création de l'environnement virtuel mkdir monenv cd monenv python3 -m venv myvenv myvenv/bin/activate ←→ source bin/activate myvenv/bin/pip install --upgrade pip myvenv/bin/pip install django Olivier Pons / 2016 4 / 71 Technologies avancées 2 – Site basique 1 – Django – Installation Création de l'environnement virtuel – Python 2.x mkdir monenv cd monenv virtualenv myvenv source myvenv/bin/activate myvenv/bin/pip install requests myvenv/bin/pip install --upgrade pip myvenv/bin/pip install django Olivier Pons / 2016 5 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Création du projet > myvenv/bin/django-admin.py startproject monprojet . > tree monprojet monprojet/ ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py Editez monprojet/settings.py Et mettez le en français, avec TIMEZONE à ”Europe/Paris” Olivier Pons / 2016 6 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Les fichiers de base monprojet/ → Dossier racine ├── __init__.py → monprojet est un module ├── settings.py → configuration du site Web ├── urls.py → résolution des routes (urlresolver) └── wsgi.py → Web Server Gateway Interface = serveur Olivier Pons / 2016 7 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Création du projet Le fichier manage.py est une sorte d'enveloppe de ”django-admin” L'interface d'administration n'existe pas car… elle est générée automatiquement ! En attendant il faut demander à générer la base de données dont se servent les modules de base… Puis générer une application. En général : un projet, qui contient plusieurs applications Olivier Pons / 2016 8 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Création de la base de données myvenv/bin/python3 manage.py migrate Olivier Pons / 2016 9 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Lancement du serveur myvenv/bin/python3 manage.py runserver Olivier Pons / 2016 10 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Nouvelle application >> myvenv/bin/python3 manage.py startapp monblog >> tree monblog/ monblog/ ├── __init__.py ├── admin.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py └── views.py Olivier Pons / 2016 11 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Nouvelle application Ajouter dans monprojet/settings.py l'application 'monblog' INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'monblog', ) Olivier Pons / 2016 12 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Modèle exemple Mettre dans monblog/models.py : from django.db import models from django.utils import timezone class Post(models.Model): author = models.ForeignKey('auth.User') title = models.CharField(max_length=200) text = models.TextField() created_date = models.DateTimeField( default=timezone.now) published_date = models.DateTimeField( blank=True, null=True) def publish(self): self.published_date = timezone.now() self.save() def __str__(self): return self.title Olivier Pons / 2016 13 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Modèle exemple >> myvenv/bin/python3 manage.py makemigrations monblog >> myvenv/bin/python3 manage.py migrate monblog Olivier Pons / 2016 14 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog QuerySet >> >> >> >> >> >> >> >> >> myvenv/bin/python3 manage.py shell from monblog.models import Post from django.contrib.auth.models import User User.objects.all() User.objects.create(username='olivier') User.objects.all() User.objects.get(username='olivier') moi = User.objects.get(username='olivier') Post.objects.create( author = moi, title = 'Mon titre', text = 'Test') >> Post.objects.filter(author=moi) Olivier Pons / 2016 15 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog QuerySet >> post = Post.objects.get(id=1) >> post.publish() >> Post.objects.filter(published_date__isnull=False) >> Post.objects.order_by('created_date') >> Post.objects.order_by('-created_date') >> exit Olivier Pons / 2016 16 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Administration Créer un superadmin : myvenv/bin/python3 manage.py createsuperuser Mettre dans monblog/admin.py : from django.contrib import admin from .models import Post admin.site.register(Post) Relancer le serveur myvenv/bin/python3 manage.py runserver Aller sur : http://127.0.0.1:8000/admin/ Olivier Pons / 2016 17 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Olivier Pons / 2016 18 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Urls Ajouter l'URL dans monprojet/urls.py : urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'', include('monblog.urls')), ] Créer le fichier en conséquence : monblog/urls.py from django.conf.urls import patterns, include, url from . import views urlpatterns = patterns('', url(r'^$', views.post_list), ) Olivier Pons / 2016 19 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Templating Ajouter l'URL dans monblog/views.py : from django.shortcuts import render def post_list(request): return render(request, 'blog/post_list.html', {}) Créer les dossiers puis le fichier en conséquence : >>> mkdir monblog/templates >>> mkdir monblog/templates/blog >>> vim monblog/templates/blog/post_list.html Olivier Pons / 2016 20 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Comparaison Python / Php Olivier Pons / 2016 21 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Comparaison Python / Php Symfony : générer l'interface d'administration / Sonata - Installer le bundle - Rajouter / modifier le routing - Publier les assets - Décommenter le traducteur (multilangue) - Supprimer les routes inutiles - Générer toutes les classes d'admin. - Eventuellement, activer cmf_tree (→ 3 actions en plus) http://symfony.com/doc/current/cmf/tutorial/sonata-admin.html Django : rien à faire. Si, si. Rien. Olivier Pons / 2016 22 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Templates / Données dynamiques Lancer python ”local” : myvenv/bin/python3 Puis import os os.environ['DJANGO_SETTINGS_MODULE']='monprojet.settings' from django.contrib.auth.models import User import monblog.models posts = monblog.models.Post.objects\ .filter(published_date__isnull=False)\ .order_by('published_date') posts Olivier Pons / 2016 23 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Templates / Données dynamiques Toutes les conditions possibles sont après ”__” : xxx.filter(nomchamp__lte = xx) xxx.filter(nomchamp__gt = xx) xxx.filter(nomchamp__startwith = xx) xxx.filter(nomchamp__endswith = xx) xxx.filter(nomchamp__exact = xx) xxx.filter(nomchamp__iexact = xx) xxx.filter(nomchamp__contains = xx) xxx.get(id = xx) xxx.objects.all()[:5] Olivier Pons / 2016 24 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Templates / Données dynamiques Ajouter dans monblog/views.py : from django.shortcuts import render from .models import Post def post_list(request): posts = Post.objects\ .filter(published_date__isnull=False)\ .order_by('published_date') return render( request, 'blog/post_list.html', {'posts': posts} ) Relancer le serveur Web Olivier Pons / 2016 25 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Templates / Données dynamiques Ajouter dans monblog/templates/blog/post_list.html : {% for post in posts %} <div> <p>Publié le : {{ post.published_date }}</p> <h1><a href="">{{ post.title }}</a></h1> <p>{{ post.text|linebreaks }}</p> </div> {% endfor %} Relancer le serveur Web Olivier Pons / 2016 26 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Templates / Données statiques Créez le dossier ”static” à la racine puis ”css” : mkdir static mkdir static/css Modifiez ”monprojet/settings.py” : STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), ) Relancer le serveur Web Olivier Pons / 2016 27 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Templates / Données statiques Modifier première ligne de ”monblog/templates/blog/post_list.html” : {% load staticfiles %} Puis dans le <head></head> ajouter : <link rel="stylesheet" href="{% static "css/blog.css" %}"> Relancer le serveur Web → Intégrer totalement un boilerplate au choix (voir après) Olivier Pons / 2016 28 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Boilerplates Initializr 99lime Alsacreation http://www.initializr.com/ http://www.99lime.com/elements/ http://schnaps.it/ http://fortawesome.github.io/Font-Awesome/cheatsheet/ Olivier Pons / 2016 29 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Templates / Héritage Fichier base.html contient toute la base avec des blocs vides : {% block content %} {% endblock %} Les fichiers qui héritent ”remplissent” ces blocs : ”monblog/templates/blog/post_list.html” : {% block content %} {% for post in posts %} … {% endfor %} {% endblock content %} Olivier Pons / 2016 30 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Url + Template + QuerySet Editer monblog/urls.py y ajouter dans les patterns : url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail), Editer ”monblog/views.py” : from django.shortcuts import render, get_object_or_404 def post_detail(request, pk): post = get_object_or_404(post, pk=pk) return render( request, 'blog/post_detail.html', {'post': post} ) Olivier Pons / 2016 31 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Url + Template + QuerySet Editer ”monblog/templates/blog/post_detail.html” : {% extends "blog/base.html" %} {% block content %} <div class="date"> {% if post.published_date %} Publié le : {{ post.published_date }} {% endif %} </div> <h1>{{ post.title }}</h1> <p>{{ post.text|linebreaks }}</p> {% endblock %} Olivier Pons / 2016 32 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Formulaires – Simples (pas liés à un modèle) Principe → Déclarer une classe dérivée de ”forms.Form” dans forms.py → Déclarer une vue de type ”FormView” dans views.py → Mettre la forme en visuel dans les templates (xxx.html) Olivier Pons / 2016 33 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Formulaires – Simples (pas liés à un modèle) forms.py class RegisterForm(forms.Form): username = forms.CharField() prenom = forms.CharField() etc. prenom = forms.CharField( label=u'Entrez votre prénom', max_length=100) Ce ne sont que des définitions de champs = ”Fields” Olivier Pons / 2016 34 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Formulaires – Simples (pas liés à un modèle) views.py class RegisterView(FormView): template_name = 'applancement/index.html' form_class = RegisterForm def form_valid(self, form): username = form.cleaned_data['username'] prenom = form.cleaned_data['prenom'] ... return HttpResponseRedirect(u'{0}{1}'.format( site_web, self.request.META['PATH_INFO'] )) Olivier Pons / 2016 35 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Formulaires – Simples (pas liés à un modèle) templates/index.html <form action="{% url 'register' %}" method="post"> {% csrf_token %} {% for field in form %} {{ field.label_tag }}<br />{{ field }}<br /> {% if field.errors %} {{ field.errors }} {% endif %} {{ field.help_text }} {% endfor %} {{ form.non_field_errors }} <input type="submit" value="Register!"> </form> Olivier Pons / 2016 36 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Formulaires Editer ”monblog/forms.py” : from django import forms from .models import Post ← formulaires Django ← notre modèle class PostForm(forms.ModelForm): ← Classe dérivée ← classe où définir : class Meta: ← - la table model = Post fields = ('title', 'text',)← - les champs Olivier Pons / 2016 37 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Formulaires Ajouter le lien vers le formulaire : <a href="{% url "blog.views.post_new" %}" class="top-menu"> <span class="glyphicon glyphicon-plus"></span> </a> Olivier Pons / 2016 38 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Droits Authentification Vérifie que l’utilisateur est bien celui qu’il prétend être Autorisation Détermine ce qu’un utilisateur authentifié est autorisé à faire Installation Tout est déjà fait (settings.py) django.contrib.auth Authentification django.contrib.contenttypes Association modèles ↔ permissions Olivier Pons / 2016 39 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Décorateurs from django.views.decorators.http import require_http_methods @require_http_methods(["GET", "POST"]) def my_view(request): # arrivé ici = que GET ou POST ! pass → require_GET(), require_POST(), require_safe() from django.contrib.auth.decorators import login_required @login_required(login_url='/login/') def my_view(request): ... Olivier Pons / 2016 40 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Décorateurs from django.contrib.auth.decorators import permission_required @permission_required('polls.can_vote', login_url='/login/') def my_view(request): ... Dans l'application polls : class Poll(models.Model): def can_vote(self, user): return not self.vote_set.filter(user=user).exists() Olivier Pons / 2016 41 / 71 Technologies avancées 2 – Site basique 1 – Django – Blog Décorateurs from django.contrib.auth.decorators import user_passes_test def email_ok(user): return user.email.endswith('@gmail.com') @user_passes_test(email_ok) def my_view(request): ... Olivier Pons / 2016 42 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments URLs paramétrées Dans urls.py urlpatterns = [ .... url(_(r'^vente-groupee/(?P<slug>[a-zA-Z0-9-_]+)/$'), p_views.DetailView.as_view(), name='vente_groupee'), .... Dans un fichier de template : <a href="{% url 'vente_groupee' monparam %}">Ici<a/> Olivier Pons / 2016 43 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Sessions Dans settings.py vérifier que MIDDLEWARE_CLASSES contient django.contrib.sessions.middleware.SessionMiddlewarer Puis dans les vues : → Le premier paramètre d'une vue est un objet HttpRequest → Via cet objet, on accède à la propriété session requestion.session.['id']=654654 exemple = requestion.session.get('id') del request.session['id'] Fonctions les plus utilisées de session : keys(), items(), setdefault(), clear(), flush() https://docs.djangoproject.com/fr/1.8/topics/http/sessions/ 44 Olivier Pons / 2016 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Namespace Définition du namespace dans la route ”principale” Ajouter dans urls.py urlpatterns = [ url(_(r'^monapp/'), include('monapp.urls', namespace="produits")), url(r'^admin/', include(admin.site.urls)), ] Olivier Pons / 2016 45 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Namespace Définition des routes dans l'application même : blog/urls.py from django.conf.urls import include, url from django.contrib import admin from . import views urlpatterns = [ url(r'^$', views.IndexView.as_view(), name='index'), ] Olivier Pons / 2016 46 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Modèles : choix multiple from django.db import models from django.utils.translation import gettext_lazy as _ DIFFICULTY = (('easy', _('Easy')), ('medium', _('Medium')), ('hard', _('Hard'))) class Exercise(models.Model): name = models.CharField(max_length=300, unique=True) level = models.CharField( max_length=15, choices=DIFFICULTY) Olivier Pons / 2016 47 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Modèles : base abstraite class BaseModel(models.Model): date_creation = models.DateTimeField( auto_now_add=True ) date_last_modif = models.DateTimeField( auto_now=True ) class Meta: abstract = True Olivier Pons / 2016 48 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Modèles : exemple de filtre https://docs.djangoproject.com/fr/1.8/topics/db/queries/ Dans le modèle : class Produit(BaseModel): tags = models.ManyToManyField( Tag, related_name='produits' ) Dans la vue : Tag.objects.filter( produits__in=Produit.objects.all() ).distinct() Olivier Pons / 2016 49 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Administration : ManyToMany class ProduitTagsInline(admin.TabularInline): model = Produit.tags.through extra = 0 class ProduitDescriptionsInline(admin.TabularInline): model = Produit.descriptions.through extra = 0 class ProduitAdmin(admin.ModelAdmin): inlines = [ProduitTagsInline, ProduitDescriptionsInline] exclude = ('descriptions', 'tags') Olivier Pons / 2016 50 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Administration : ManyToMany Olivier Pons / 2016 51 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Administration : même clé étrangère → models.py class LangueTraduction(BaseModel): src = models.ForeignKey('Langue', related_name='langue_src') dst = models.ForeignKey('Langue', related_name='langue_dst') nom = models.CharField(max_length=50) → admin.py class LangueTraductionsInline(admin.TabularInline): # ! fk_name = obligatoire sinon pas visible : fk_name = 'src' model = LangueTraduction extra = 0 class LangueAdmin(admin.ModelAdmin): inlines = [LangueTraductionsInline] Olivier Pons / 2016 52 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Administration : même clé étrangère Olivier Pons / 2016 53 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Traduction Au moment où la fonction est appelée from django.utils.translation import ugettext as _ Au moment où la chaîne est réellement utilisée from django.utils.translation import ugettext_lazy as _ Créer un dosser ”locale” dans le projet Avec manage.py shell : makemessages -l fr makemessages -l en compilemessages Olivier Pons / 2016 54 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Traduction Pour qu'il charge ces chaines : Dans settings.py, ajouter : LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale'), ) Olivier Pons / 2016 55 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Champs de type ImageField Répertoire des fichiers ”téléversés” : Dans settings.py, ajouter : MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads') Puis dans urls.py, ajouter : urlpatterns = [ ... url(r'^public/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}), ] Olivier Pons / 2016 56 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Lancer des commandes SQL en direct Exemple ici pour simuler le ”DESC [table]” de mysql : Lancer python en ligne de commande from django.db import connection cursor = connection.cursor() cursor.execute("PRAGMA table_info(ventegroupee)") for row in cursor: print(row) Pour la note : cursor est une classe ”iterable”, donc on peut faire un ”for” dessus Olivier Pons / 2016 57 / 71 Technologies avancées 2 – Site basique 2 – Django – Suppléments Outils Django déjà en production Allez ici : https://github.com/divio/django-cms Installez le en suivant les instructions. Olivier Pons / 2016 58 / 71 Technologies avancées 2 – Site basique 3 – Django – Jeu de rôle Modèle exemple Autre exemple models.py : from django.db import models from django.utils import timezone from django.utils.translation import ugettext_lazy as _ class CharacterClass(models.Model): name = models.CharField(_(u'Nom de la Classe'),max_length=80) playable = models.BooleanField(_(u'Le joueur peut le choisir')) def __str__(self): return self.name Olivier Pons / 2016 59 / 71 Technologies avancées 2 – Site basique 3 – Django – Jeu de rôle Modèle exemple Types existants : CharField BooleanField EmailField PositiveIntegerField TextField ForeignKey OneToOneField ManyToManyField Olivier Pons / 2016 60 / 71 Technologies avancées 2 – Site basique 3 – Django – Jeu de rôle Modèle exemple class CharacterManager(models.Manager): def get_query_set(self): return super(CharacterManager, self)\ .get_query_set()\ .filter(playable = True) class CharacterClass(models.Model): ... objects = models.Manager() # Manager par défaut playable_character = CharacterManager() # Playable Manager playable = CharacterClass.objects.filter(Playable = True) playable = CharacterClass.playable_character.all() Olivier Pons / 2016 61 / 71 Technologies avancées 2 – Site basique 3 – Django – Jeu de rôle Vues génériques monblog/urls.py → on peut y ajouter directement : from django.conf.urls import url from django.views.generic import TemplateView urlpatterns = [ url(r'^apropos/$', TemplateView.as_view( template_name='apropos.html' )) ] Olivier Pons / 2016 62 / 71 Technologies avancées 2 – Site basique 3 – Django – Jeu de rôle Vues génériques : dérivation # monapp/views.py from django.views.generic import TemplateView class AproposView(TemplateView): template_name = "apropos.html" # urls.py from django.conf.urls import url from monapp.views import AproposView urlpatterns = [ url(r'^apropos/', AproposView.as_view()), ] Olivier Pons / 2016 63 / 71 Technologies avancées 2 – Site basique 3 – Django – Jeu de rôle Vues génériques : listes class IndexView(generic.ListView): template_name = 'produits/index.html' context_object_name = 'liste_produits' def get_queryset(self): """ Dites ce que cela renvoie : """ return Produit.objects.order_by( '-date_v_fin', '-date_v_debut' )[:5] Olivier Pons / 2016 64 / 71 Technologies avancées 2 – Site basique 3 – Django – Jeu de rôle Vues génériques : détail from django.views.generic.detail import DetailView from django.utils import timezone from .models import Produit class MaDetailView(DetailView): template_name = 'produits/detail.html' model = Produit def get_context_data(self, **kwargs): context = super(MaDetailView, self) .get_context_data(**kwargs) context['now'] = timezone.now() return context Olivier Pons / 2016 65 / 71 Technologies avancées 2 – Site basique 3 – Django – Sujet à faire Choisissez un site à faire parmi les suivants Réservation de places de concert Médiathèque Blog multi-utilisateurs multi rôles Vente de services (développement, formation) Rencontre entre professionnels (”à la LinkedIn”) SAAS de comptabilité basique Création communautaire de livres Présentation et statistiques détaillées d'un MMORPG Crowdfunding (”à la kickstarter / indiegogo”) Site d'apprentissage en ligne Réservation de tables d'un restaurant Olivier Pons / 2016 66 / 71 Technologies avancées 2 – Site basique 4 – Structure classique Varnish Configuration de varnish : /etc/varnish/default.vcl : backend apache { .host = "127.0.0.1"; .port = "8080"; } backend nodejs { .host = "127.0.0.1"; .port = "3000"; } ... Olivier Pons / 2016 67 / 71 Technologies avancées 2 – Site basique 4 – Structure classique Varnish ... sub vcl_recv { if (req.url ~ "(?i)\.(jpeg|jpg|...|html|htm)$") { unset req.http.Cookie; } if ( (req.http.host ~ "olivierpons\.(.*)") || (req.http.host ~ "krystallopolis\.(.*)") ) { set req.backend_hint = apache; set req.http.X-Server = req.http.host; } ... Olivier Pons / 2016 68 / 71 Technologies avancées 2 – Site basique 4 – Structure classique Varnish ... # Surcharge pour NodeJS if ( (req.http.host ~ "node\.wogwog\.(.*)") ) { set req.backend_hint = nodejs; set req.http.X-Server = req.http.host; # "pass" = passer direct sans cache possible return(pass); } } ... Olivier Pons / 2016 69 / 71 Technologies avancées 2 – Site basique 4 – Structure classique Varnish ... sub vcl_deliver { set resp.http.Server = "WTF Server"; # Supprimer toutes les signatures : unset resp.http.Via; unset resp.http.X-Powered-By; unset resp.http.X-Varnish; } Olivier Pons / 2016 70 / 71 Technologies avancées 2 – Site basique 4 – Structure classique Apache / NodeJS / Python Apache httpd.conf Listen 8080 NodeJS bin/www var port = normalizePort(process.env.PORT || '3000'); Python / Django python manage.py runserver 8080 Olivier Pons / 2016 71 / 71