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

411 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
411
On SlideShare
0
From Embeds
0
Number of Embeds
33
Actions
Shares
0
Downloads
12
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

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

  1. 1. Backend разработка Дмитрий Смаль
  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
  5. 5. CGI Запрос: ● Параметры запроса – environ ● Тело запроса – stdin ● URI запроса – QUERY_STRING или argv[1] Ответ: ● Тело ответа (с заголовками) - stdout ● Ошибки выполнения - stderr ● HTTP код – через псевдозаголовок Status
  6. 6. CGI – окружение Переменные окружения REQUEST_METHOD – метод (GET, POST, …) REQUEST_URI – строка запроса QUERY_STRING - строка параметров REMOTE_ADDR – ip адрес клиента SCRIPT_NAME – имя текущего скрипта HTTP_COOKIE – заголовок Cookie: HTTP_REFERER – заголовок Referer:
  7. 7. CGI скрипт #!/usr/bin/python2.7 import os import sys print print print print "Content-type: text/html" "Status: 200" "" "<h1>Hello, world!</h1>" for k, v in os.environ.items(): print "%s = %s<br>" % (k, v) print >> sys.stderr, "Nice to meet you"
  8. 8. nph - CGI скрипт #!/usr/bin/python2.7 print print print print "HTTP/1.0 301 Found" "Location: http://go.mail.ru/" "Set-Cookie: name=value" "" Как сервер определяет nph скрипт ? ● По имени файла nph- ● По первой строчке вывода скрипта В чем отличие от обычных CGI ?
  9. 9. Обработка HTTP запросов
  10. 10. GET параметры Гиперссылка <a href=”/hello.cgi?name=me&greeting=hello”/>Hello!</a> Переменная окружения QUERY_STRING=name=me&greeting=hello CGI скрипт get_params = {} qs = os.environ['QUERY_STRING'] for pair in qs.split('&'): key, value = pair.split('=') get_params[key] = value
  11. 11. 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 CGI скрипт qs = sys.stdin.read() ...
  12. 12. Файлы и не-ASCII multipart/form-data <form method=”post” action=”/hello.cgi” enctype=”multipart/form-data”> <input name=”name” value=”me”/> <input name=”pic” type=”file”/> <input type=”submit”/> </form> URI percent encoding <a href=”/hello.cgi?name=%D0%B8%D0%BC%D1%8F”>привет</a>
  13. 13. Библиотеки CGI #!/usr/bin/python2.7 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()
  14. 14. Обработка форм 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)
  15. 15. Работа с базой данных
  16. 16. СУБД: таблицы
  17. 17. СУБД: 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;
  18. 18. 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()
  19. 19. Конфигурация
  20. 20. Конфигурация 1) hardcode. Настройки зашиты в код приложения 2) script. Настройки представляют собой скрипт на целевом ЯП 3) YAML, XML, ini, Config::Apache 4) Переменные, разделение на несколько файлов
  21. 21. Генерация HTML страниц
  22. 22. Шаблонизаторы 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)
  23. 23. Шаблоны <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>
  24. 24. Структура страницы
  25. 25. Подшаблоны {% 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' %}
  26. 26. Наследование (layouts) <!-- base.html --> <div>{% block header %} HEADER {% endblock %}</div> <table><tr> <td>LEFT</td> <td>{% block content %} CONTENT {% endblock %}</td> <td>RIGHT</td> </tr></table> <div>{% block footer %} FOOTER {% endblock %}</div>
  27. 27. Наследование (layouts) <!-- page.html –-> {% extends 'base.html' %} {% block header %} Custom header {% endblock %} {% block content %} <h1>{{ user.name }}</h1> <div>{{ block.super }}</div> {% endblock %}
  28. 28. Задача 1. Листинг объектов
  29. 29. Листинг объектов 1) Параметры: фильтрация, сортировка, номер страницы 2) /images/?order=created&page=3&limit=10 3) Результат работы скрипта: список объектов для данной страницы и paginator 4) paginator – представляет положение в списке страниц
  30. 30. Листинг объектов #!/usr/bin/python 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 = int(form.getfirst('page', 1)) limit = int(form.getfirst('limit', 10)) sql = 'select * from img order by %s limit %d offset %d' % (order, limit, limit * (page – 1)) cursor.execute(sql)
  31. 31. Листинг объектов 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 print print print “Status: 200” “Content-Type: text/html” “” render('/images.html', context)
  32. 32. 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] }
  33. 33. Задача 2. Изменение объекта
  34. 34. Изменение объекта 1) Два режима работы: отображение формы и обновление объекта 2) Разделение по методу HTTP (GET | POST). Кеширование запросов 3) Использование спец. параметра (action) 4) Отображение ошибок и результата действия
  35. 35. Изменение объекта #!/usr/bin/python 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?id=%s&res=updated' % form['id']) catch BaseException, e: redirect('/cgi-bin/object?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)
  36. 36. Изменение объекта <form method=”POST” action=”/cgi-bin/object”> {% 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>
  37. 37. Best Practice 1) Разделять методы GET – получение, POST – обновление данных 2) Сообщать об ошибках и успехе 3) Коды возврата при ошибке и успехе 4) Проверять данные пользователя а) на сервере – безопасность программы б) на клиенте – удобство пользователя 5) Выделять неправильно введеные поля
  38. 38. Задача 3. Wizard
  39. 39. Wizard 1) Statefull vs Stateless. 2) Как передать данные между страницами? выбор товара → информация о клиенте → место доставки → подтверждение 3) Варианты: - через URL - через скрытые поля - через Cookie - через сессии
  40. 40. Скрытые поля <!-- 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>
  41. 41. Cookie и Сессии Cookie: Set-Cookie: name=val; path=/; domain=domain.ru; expires=Tue, 20 Mar 2012 11:52:54 GMT Cookie: name=val;name2=val2;is_visited=2011-13-15 Сессии: 1) Ключ сессии – в cookie 2) Данные – на сервере (memcached, database, files)
  42. 42. Cookie в Python Установка: import Cookie cookie = Cookie.SimpleCookie() cookie['name'] = 'val' cookie['name']['path'] = '/path' Получение: import Cookie cookie = Cookie.SimpleCookie() cookie.load('a=b;c=d') for name in cookie: print '%s => %s' % (name, cookie[name])
  43. 43. Достоинства CGI 1) простая концепция “скриптов” 2) стандарт – поддержка на любом хостинге 3) последовательное исполнение
  44. 44. Недостатки CGI 1) смешение кода и HTML → шаблонизаторы 2) повторение логики → вынесение кода в библиотеки 3) pretty urls → RewriteEngine 4) производительность (fork, exec, parse, db.connect) → кеширование кода (FastCGI, mod_perl etc)
  45. 45. WSGI – pep 3333 def application(environ, start_response): start_response('200 OK', [ ('Content-Type', 'text/plain') ]) yield 'Hello Worldn' def application(environ, start_response): start_response('200 OK', [ ('Content-Type', 'text/plain')]) return [ 'Hello Worldn' ]
  46. 46. Спасибо за внимание Дмитрий Смаль, smal@corp.mail.ru

×