42
Backend разработка Дмитрий Смаль

Web осень 2012 лекция 4

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Web осень 2012 лекция 4

Backend разработка

Дмитрий Смаль

Page 2: Web осень 2012 лекция 4

Что мы научимся делать?

Типичные задачи

●Обрабатывать GET и POST запросы

●Выводить HTML при помощи шаблонов

●Хранить данные в СУБД

●Отображение списка объектов

●Изменение (редактирование) объектов

●Wizards: последовательности страниц

Page 3: Web осень 2012 лекция 4

Языки и технологии

Статические (+/-):

● С\С++ модули к Web серверам.

● Java – Servlets, ApplicationServers

Динамически (+/-):

● Perl – CGI, mod_perl, PSGI

● PHP – mod_php, FastCGI (FPM)

● Ruby – rack, свой сервер (mongrel)

● Python – WSGI, свой сервер (Tornado)

● JavaScript – свой сервер (NodeJS)

Page 4: Web осень 2012 лекция 4

CGI скрипт

#!/usr/bin/python2.7

import osimport sys

print "Content-type: text/html"print "Status: 200"print ""print "<h1>Hello, world!</h1>"

for k, v in os.environ.items(): print "%s = %s<br>" % (k, v)

print >> sys.stderr, "Nice to meet you"

Page 5: Web осень 2012 лекция 4

nph - CGI скрипт

#!/usr/bin/python2.7

print "HTTP/1.0 301 Found"print "Location: http://go.mail.ru/"print "Set-Cookie: name=value"print ""

Как сервер определяет nph скрипт ?

●По имени файла nph-

●По первой строчке вывода скрипта

Page 6: Web осень 2012 лекция 4

Обработка HTTP запросов

Page 7: Web осень 2012 лекция 4

GET параметры

<a href=”/hello.cgi?name=me&greeting=hello”/>Hello!</a>

Гиперссылка

Переменная окружения

CGI скрипт

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

Page 8: Web осень 2012 лекция 4

POST параметры

<form method=”post” action=”/hello.cgi”> <input name=”name” value=”me”/> <input name=”greeting” value=”hi”/> <input type=”submit”/></form>

Форма

Стандартный поток ввода STDIN

CGI скрипт

name=me&greeting=hello

qs = sys.stdin.read()...

Page 9: Web осень 2012 лекция 4

Файлы и не-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>

multipart/form-data

URI percent encoding<a href=”/hello.cgi?name=%D0%B8%D0%BC%D1%8F”>привет</a>

Page 10: Web осень 2012 лекция 4

Библиотеки CGI

#!/usr/bin/python2.7

import cgiimport 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()

Page 11: Web осень 2012 лекция 4

Обработка форм

if not re.match('[a-z]+', form[“name”]): raise BaseException(“panic”)

1) Валидация (regex, code)

2) Очистка

3) Экранирование

story = re.sub('<[^>]+>', ' ', form[“story”])

story = form[“story”]re.sub('<', '&lt;', story)re.sub('>', '&gt', story)

Page 12: Web осень 2012 лекция 4

Работа с базой данных

Page 13: Web осень 2012 лекция 4

СУБД: таблицы

Page 14: Web осень 2012 лекция 4

СУБД: 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;

Page 15: Web осень 2012 лекция 4

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()

Page 16: Web осень 2012 лекция 4

Конфигурация

Page 17: Web осень 2012 лекция 4

Конфигурация

1) hardcode. Настройки зашиты в код приложения

2) script. Настройки представляют собой скрипт на целевом ЯП

3) YAML, XML, ini, Config::Apache

4) Переменные, разделение на несколько файлов

Page 18: Web осень 2012 лекция 4

Генерация HTML страниц

Page 19: Web осень 2012 лекция 4

Шаблонизаторы

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)

Page 20: Web осень 2012 лекция 4

Шаблоны

<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>

Page 21: Web осень 2012 лекция 4

Структура страницы

Page 22: Web осень 2012 лекция 4

Подшаблоны

{% 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' %}

Page 23: Web осень 2012 лекция 4

Наследование (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>

Page 24: Web осень 2012 лекция 4

Наследование (layouts)

<!-- page.html –->

{% extends 'base.html' %}

{% block header %} Custom header{% endblock %}

{% block content %} <h1>{{ user.name }}</h1> <div>{{ block.super }}</div>{% endblock %}

Page 25: Web осень 2012 лекция 4

Задача 1. Листинг объектов

Page 26: Web осень 2012 лекция 4

Листинг объектов

1) Параметры: фильтрация, сортировка, номер страницы

2) /images/?order=created&page=3&limit=10

3) Результат работы скрипта: список объектов для данной страницы и paginator

4) paginator – представляет положение в списке страниц

Page 27: Web осень 2012 лекция 4

Листинг объектов

#!/usr/bin/pythonimport cgiimport psycopg2import 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)

Page 28: Web осень 2012 лекция 4

Листинг объектов

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)

Page 29: Web осень 2012 лекция 4

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]}

Page 30: Web осень 2012 лекция 4

Задача 2. Изменение объекта

Page 31: Web осень 2012 лекция 4

Изменение объекта

1) Два режима работы: отображение формы и обновление объекта

2) Разделение по методу HTTP (GET | POST). Кеширование запросов

3) Использование спец. параметра (action)

4) Отображение ошибок и результата действия

Page 32: Web осень 2012 лекция 4

Изменение объекта

#!/usr/bin/pythonimport 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)

Page 33: Web осень 2012 лекция 4

Изменение объекта

<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>

Page 34: Web осень 2012 лекция 4

Best Practice

1) Разделять методы GET – получение, POST – обновление данных

2) Сообщать об ошибках и успехе

3) Проверять данные пользователя

а) на сервере – безопасность программы

б) на клиенте – удобство пользователя

4) Выделять неправильно введеные поля

Page 35: Web осень 2012 лекция 4

Задача 3. Wizard

Page 36: Web осень 2012 лекция 4

Wizard

1) Statefull vs Stateless.

2) Как передать данные между страницами?

выбор товара → информация о клиенте

→ место доставки → подтверждение

3) Варианты:

- через URL

- через скрытые поля

- через Cookie

- через сессии

Page 37: Web осень 2012 лекция 4

Скрытые поля

<!-- 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>

Page 38: Web осень 2012 лекция 4

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

Cookie:

Сессии:

1) Ключ сессии – в cookie

2) Данные – на сервере (memcached, database, files)

Page 39: Web осень 2012 лекция 4

Cookie в Python

import Cookiecookie = Cookie.SimpleCookie()cookie['name'] = 'val'cookie['name']['path'] = '/path'

Установка:

Получение:

Установка:

import Cookiecookie = Cookie.SimpleCookie()cookie.load('a=b;c=d')for name in cookie: print '%s => %s' % (name, cookie[name])

Page 40: Web осень 2012 лекция 4

Достоинства CGI

1) простая концепция “скриптов”

2) стандарт – поддержка на любом хостинге

3) последовательное исполнение

Page 41: Web осень 2012 лекция 4

Недостатки CGI

Плюсы:

1) смешение кода и HTML → шаблонизаторы

2) повторение логики → вынесение кода в библиотеки

3) pretty urls → RewriteEngine

4) производительность (fork, exec, parse, db.connect) → кеширование кода (FastCGI, mod_perl etc)

Page 42: Web осень 2012 лекция 4

Спасибо за вниманиеДмитрий Смаль, [email protected]