CGI: пишем простой сайт на Python. Часть 2: Обработка форм, cookies
В первой части мы написали Hello world. Сегодня мы рассмотрим несколько более сложные вещи: обработку данных форм и cookies.
Получение данных из форм
Итак, во-первых разберёмся с формами. В модуле CGI есть полезный класс: FieldStorage, который содержит в себе переданную в форме информацию. По сути дела этот класс представляет из себя словарь, обладающий теми же свойствами, что и обычный словарь в python.
У класса FieldStorage есть 2 метода получения значений данных формы:
FieldStorage.getfirst(name, default=None) - всегда возвращает только одно значение, связанное с именем поля формы. Метод возвращает только первое значение в том случае, если нехороший пользователь послал более одного значения. Обратите внимание, что порядок, в котором будут получены значения, могут отличаться от браузера к браузеру. Если нет такого поля формы или значение не существует, то метод возвращает default.
FieldStorage.getlist(name) - возвращает список значений, связанных с именем поля формы.
Разберём на примере: создадим в нашей папке файл index.html со следующим содержимым (это будет наша форма, данные из которой мы будем обрабатывать):
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Обработка данных форм</title> </head> <body> <form action="/cgi-bin/form.py"> <input type="text" name="TEXT_1"> <input type="text" name="TEXT_2"> <input type="submit"> </form> </body> </html>
А в папке cgi-bin/ - файл form.py (обработчик формы)
#!/usr/bin/env python3 import cgi form = cgi.FieldStorage() text1 = form.getfirst("TEXT_1", "не задано") text2 = form.getfirst("TEXT_2", "не задано") print("Content-type: text/html\n") print("""<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Обработка данных форм</title> </head> <body>""") print("<h1>Обработка данных форм!</h1>") print("<p>TEXT_1: {}</p>".format(text1)) print("<p>TEXT_2: {}</p>".format(text2)) print("""</body> </html>""")
Попробуем это в действии (кто сидит на linux, не забудьте поставить права на выполнение).
Запускаем локальный сервер, и переходим на localhost:8000:
Но есть нюанс...
А если попробовать так?
Это серьёзная уязвимость, поэтому от неё нужно избавляться. Для этого нужно (в самом простом случае) экранировать все опасные символы. Это можно сделать с помощью функции escape из модуля html.
Перепишем form.py:
#!/usr/bin/env python3 import cgi import html form = cgi.FieldStorage() text1 = form.getfirst("TEXT_1", "не задано") text2 = form.getfirst("TEXT_2", "не задано") text1 = html.escape(text1) text2 = html.escape(text2) print("Content-type: text/html\n") print("""<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Обработка данных форм</title> </head> <body>""") print("<h1>Обработка данных форм!</h1>") print("<p>TEXT_1: {}</p>".format(text1)) print("<p>TEXT_2: {}</p>".format(text2)) print("""</body> </html>""")
Результат можете проверить сами.
Вообще говоря, экранирование нежелательных символов везде, где нужно - очень большая проблема безопасности веб-приложений. Помните об этом.
Cookies
Cookies (печеньки) — небольшой фрагмент данных, отправленный веб-сервером и сохраняемый на компьютере пользователя. Браузер всякий раз при попытке открыть страницу соответствующего сайта пересылает этот фрагмент данных веб-серверу в составе HTTP-запроса.
Собственно, cookies - хороший способ сохранить некоторые данные о пользователях.
Отправка печенек осуществляется заголовком Set-cookie:
#!/usr/bin/env python3 print("Set-cookie: name=value; expires=Wed May 18 03:33:20 2033; path=/cgi-bin/; httponly") print("Content-type: text/html\n") print("Cookies!!!")
Например, если сохранить этот скрипт в /cgi-bin/cookie.py и зайти на localhost:8000/cgi-bin/cookie.py, то вам поставится печенька с именем name и значением value. Срок её хранения до мая 2033 года, отправляется повторно на сервер только к скриптам, которые расположены в /cgi-bin/, и передается только http-запросами (её нельзя получить из браузера пользователя с помощью javascript).
Все эти параметры не являются обязательными. Можно написать так:
#!/usr/bin/env python3 print("Set-cookie: name=value") print("Content-type: text/html\n") print("Cookies!!!")
Тогда храниться она будет до того момента, когда закроется браузер, будет отправляться на сервер для любых документов (и для /index.html тоже, в отличие от предыдущего случая). Также её можно будет получить средствами javascript (поскольку не был установлен флаг httponly).
Обработка Cookies
Теперь научимся получать cookies. Они передаются на сервер и доступны в переменной os.environ (словарь, cookies хранятся по ключу HTTP_COOKIE). Они передаются в виде пар ключ=значение, что не очень удобно при обработке. Для упрощения работы можно использовать модуль http.cookies.
Напишем простой скрипт (/cgi-bin/cookie.py), проверяющий, установлена ли кука, и если нет, устанавливает:
#!/usr/bin/env python3 import os import http.cookies cookie = http.cookies.SimpleCookie(os.environ.get("HTTP_COOKIE")) name = cookie.get("name") if name is None: print("Set-cookie: name=value") print("Content-type: text/html\n") print("Cookies!!!") else: print("Content-type: text/html\n") print("Cookies:") print(name.value)
Так страница выглядит после первого запроса:
И после обновления страницы:
Не следует хранить в cookies важные данные, и не полагайтесь на выставленный вами срок хранения. Cookies можно удалить или изменить вручную в браузере.