Web весна 2012 лекция 4

393 views

Published on

  • Be the first to comment

  • Be the first to like this

Web весна 2012 лекция 4

  1. 1. Backend разработка Смаль Дмитрий smal@corp.mail.ru
  2. 2. Что мы научимся делать?  Обрабатывать GET и POST запросы  Выводить HTML при помощи шаблонов  Хранить данные в СУБД Типичные задачи  Отображение списка объектов  Изменение (редактирование) объектов  Wizards: последовательности страниц
  3. 3. Языки и технологии Статические (+/-): СС++ модули к Web серверам. Java – Servlets, ApplicationServers Динамически (+/-): Perl – CGI, mod_perl, PSGI PHP – mod_php, FastCGI (FPM) Ruby – rack, свой сервер (mongrel) Python – WSGI, свой сервер (Tornado) JavaScript – свой сервер (NodeJS)
  4. 4. CGI скрипт #!/usr/bin/python2.7 print "Content-type: text/html" print "Status: 200" print "" print "<h1>Hello, world!</h1>" import os import sys for k, v in os.environ.items(): print "%s = %s<br>" % (k, v) print >> sys.stderr, "Nice to meet you"
  5. 5. NPH - CGI скрипт #!/usr/bin/python2.7 print "HTTP/1.0 301 Found" print "Location: http://go.mail.ru/" print "Set-Cookie: name=value" print ""
  6. 6. Передача GET параметров <a href=”/hello.cgi?name=me&greeting=hello”/>Say hello</a> QUERY_STRING=name=me&greeting=hello get_params = {} qs = os.environ['QUERY_STRING'] for pair in qs.split('&'): key, value = pair.split('=') get_params[key] = value
  7. 7. Передача POST параметров <form method=”post” action=”/hello.cgi”> <input name=”name” value=”me”/> <input name=”greeting” value=”hi”/> <input type=”submit”/> </form> STDIN=name=me&greeting=hello import sys qs = sys.stdin.read()
  8. 8. Передача файлов и не-ASCII <form method=”post” action=”/hello.cgi” enctype=”multipart/form-data”> <input name=”name” value=”me”/> <input name=”pic” type=”file”/> <input type=”submit”/> </form> <a href=”/hello.cgi? name=Boris&name=Ivan”>здорово</a> <a href=”/hello.cgi?name=%D0%B8%D0%BC %D1%8F”>привет</a>
  9. 9. CGI библиотеки import cgi import cgitb cgitb.enable() form = cgi.FieldStorage() if “greeting” not in form: raise BaseException(“can't work”) greeting = form[“greeting”] names = form.getlist(“name”) pic = forms[“pic”].file.read()
  10. 10. Обработка входных данных 1) Валиадция (regex, code) if not re.match('[a-z]+', form[“name”]): raise BaseException(“panic”) 2) Очистка story = re.sub('<[^>]+>', ' ', form[“story”]) 3) Экранирование story = form[“story”] re.sub('<', '&lt;', story) re.sub('>', '&gt', story)
  11. 11. Шаблонизаторы print “<html><body><h1>” “%s</h1></body></html>” % name VS context = { 'user' : get_user(form['name']), 'friends' : get_friends(form['name']) } print render('tpl/home.html', context)
  12. 12. Шаблоны <body> <h1>{{ user.name }}</h1> {% if user.sex == 'male' %} <h2>{{ user.age }}</h2> {% endif %} {% for f in friends %} <a href=”mailto:{{ f.email }}”>{{ f.name }}</a> <p>{{ f.about|linebreaks }}</p> {% endfor %} </body>
  13. 13. Структура страницы
  14. 14. Подшаблоны (includes) {% include 'inc/header.html' %} <table><tr> <td>{% include 'inc/left.html' %}</td> <td> CONTENT </td> <td>{% include 'inc/right.html' %}</td> </tr></table> {% include 'inc/footer.html' %}
  15. 15. Наследование (layouts) <!-- base.html --> <div>HEADER</div> <table><tr> <td>LEFT</td> <td> {% block content %} CONTENT {% endblock %}</td> <td>RIGHT</td> </tr></table> <div>FOOTER</div>
  16. 16. Наследование (layouts) <!-- page.html –-> {% extends 'base.html' %} {% block content %} <h1>{{ user.name }}</h1> <div>{{ block.super }}</div> {% endblock %}
  17. 17. СУБД, SQL INSERT UPDATE DELETE SELECT
  18. 18. SQL запросы INSERT INTO users (name, age) VALUES ('petr', 10), ('masha', 25); UPDATE users SET age = 10 WHERE name = 'petr'; DELETE FROM users WHERE name = 'masha'; SELECT * FROM users WHERE age > 10; SELECT * FROM users WHERE name = 'masha'; SELECT max(age) FROM users;
  19. 19. Использование SQL в python import MySQLdb db = MySQLdb.connect(**options) cursor = db.cursor() cursor.execute(“update users set age = age+1 ” ”where name = ?”, form[“name”]) context = {} cursor.execute(“select * from users”) context['friends'] = cursor.fetchall() cursor.execute(“select * from users where name = ?”, form[“name”]) context['user'] = cursor.fetchone() db.close()
  20. 20. Конфигурация приложений 1) hardcode. Настройки зашиты в код приложения 2) script. Настройки представляют собой скрипт на целевом ЯП 3) YAML, XML, ini, Config::Apache 4) Переменные, разделение на несколько файлов
  21. 21. Задача №1. Листинг объектов 1) Параметры: фильтрация, сортировка, номер страницы 2) /images/?order=created&page=3&limit=10 3) Результат работы скрипта: список объектов для данной страницы и paginator 4) paginator – представляет положение в списке страниц
  22. 22. import cgi import psycopg2 import settings db = psycopg2.connect(**settings.db) cursor = db.cursor() form = cgi.FieldStorage() order = form.getfirst('order', 'created') if order not in ('created', 'size'): raise BaseException('oops') page = form.getfirst('page', 1) limit = form.getfirst('limit', 10) sql = 'select * from images order by %s limit %d offset %d' % (order, limit, limit * (page – 1))
  23. 23. cursor.execute(sql) context = {} context['images'] = cursor.fetchall() cursor.execute('select count(*) as cnt from images') total = cursor.fetchone()[0]['cnt'] context['pager'] = calc_paginator(total, page, limit) db.close() print “Status: 200” print “Content-Type: text/html” print “” print render('/images.html', context)
  24. 24. Paginator Аргументы: total, page, limit Результат: { 'total': 100, 'page': 5, 'limit': 10, 'next_page': 6, 'prev_page': 4, 'next_page10': 10, 'prev_page10': 1, 'first_page':1, 'last_page': 10, 'pages' : [3, 4, 5, 6, 7] }
  25. 25. Задача № 2. Изменение объекта 1) Два режима работы: отображение формы и обновление объекта 2) Разделение по методу HTTP (GET | POST). Кеширование запросов 3) Использование спец. параметра (action) 4) Отображение ошибок и результата действия
  26. 26. import cgi; import psycopg2; import settings; import os db = psycopg2.connect(**settings.db) cursor = db.cursor() form = cgi.FieldStorage() if os.environ['HTTP_METHOD'] == 'POST”: try: cursor.execute('update users set name = ? where id = ?', form['name'], form['id']) redirect('/cgi-bin/object.cgiid=%s&res=updated' % form['id']) catch BaseException, e: redirect('/cgi-bin/object.cgi?id=%s&fail=fail' % form['id']) else: context = {} cursor.execute('select * from users where id = ?', form['id']) context['object'] = cursor.fetchone() render('object.html', context)
  27. 27. <form method=”POST” action=”/cgi-bin/object.cgi”> {% if res %} <p style=”color: green”>Объект обновлен: {{ res }}</p> {% endif %} {% if fail %} <p style=”color: red”>Ошибка: {{ fail }}</p> {% endif %} <input type=”hidden” name=”id” value=”{{ object.id }}”/> <input type=”text” name=”name” value=”{{ object.name }}”/> <input type=”submit”/> </form>
  28. 28. Best Practice 1) Разделять методы GET – получение, POST – обновление данных 2) Сообщать об ошибках и успехе 3) Проверять данные пользователя а) на сервере – безопасность программы б) на клиенте – удобство пользователя 4) Выделять неправильно введеные поля
  29. 29. Задача № 3. Wizard 1) Statefull vs Stateless. 2) Как передать данные между страницами? выбор товара → информация о клиенте → дата и место доставки → подтверждение 3) Варианты: - через URL - через скрытые поля - через Cookie - через сессии
  30. 30. Скрытые поля <!-- page2.html --> <form method=”POST” action=”/page3.html”> <input type=”hidden” name=”item_id” value=”1”/> <input type=”hidden” name=”ammount” value=”4”/> <input type=”text” name=”name” value=””/> <input type=”text” name=”phone” value=””/> <input type=”submit”/> </form>
  31. 31. Cookie 1) Установка Set-Cookie: name=val; path=/; domain=domain.ru; expires=Tue, 20 Mar 2012 11:52:54 GMT 2) Возврат Cookie: name=val;name2=val2;is_visited=2011-13-15 Сессии 1) Ключ сессии – в cookie 2) Данные – на сервере (memcached, database, files)
  32. 32. Cookie с помощью Python CGI 1) Установка import Cookie cookie = Cookie.SimpleCookie() cookie['name'] = 'val' cookie['name']['path'] = '/path' print cookie 2) Получение import Cookie cookie = Cookie.SimpleCookie() cookie.load('a=b;c=d') for name in cookie: print '%s => %s' % (name, cookie[name])
  33. 33. Достоинства и недостатки CGI Плюсы: 1) простая концепция “скриптов” 2) стандарт – есть поддержка на любом хостинге 3) последовательное исполнение Минусы и способы решения: 1) смешение кода и HTML → шаблонизаторы 2) повторение логики → вынесение кода в библиотеки 3) pretty urls → RewriteEngine 4) производительность (fork, exec, parse, db.connect) → кеширование кода (FastCGI, mod_perl etc)
  34. 34. Pretty URLs URL: /profile/mail/my Скрипт: /cgi-bin/profile.cgi?email=my@ mail.ru location ~* ^/profile/w+/w+/?$ { rewrite ^/profile/(w+)/(w+)/? /cgi-bin/profile.cgi?email=$2@$1.ru break; proxy_pass http://backend; }
  35. 35. Кеширование Что такое кеширование ? Виды кешей: 1) HTTP cache - сокращает загрузку статики 2) proxy cache – кеш страницы целиком (либо части, подключаемой через ssi) 3) buffer cache – кеширование данных в OS 4) memcached – распределенный кеш в памяти 5) генерация статических файлов 6) отложенное выполнение. crond.
  36. 36. Домашняя работа 1) реализовать calc_paginator 2) написать cgi скрипт для проверки. Список объектов – числа Фибоначчи до 10000 1) реализовать функцию redirect 2) написать cgi скрипт для проверки. Например аналог bit.ly со статическими настройками.

×