Администрирование
Гном в кедах - Администрирование
Web — сервер newLISP 10.2.x + cgi + MySQL
мой опыт создания и использования
NewLISP — на мой взгляд очень интересный язык программирования. Интерпретатор newLISP позволяет без напрягов запустить локальный web-сервер, организовав динамическое формирование страниц с запросами к базе данных mySQL. Эти записки будут отражением моего опыта, по формированию такого программного комплекса. Установка newLISP не должна вызвать проблем, равно как и установка базы данных mySQL, поэтому я пропускаю этот момент. Единственное замечание, я использую Calculate Linux который основан на базе Gentoo Linux. Пользователям Windows и MacOS придется немного подправить предложенное.
Запуск web-сервера
Запуск web-сервера происходит очень просто, от имени простого пользователя:
user $ newlisp httpd.conf -http -d 8080 -w /home/user/www &
Используемые параметры запуска:
1. httpd.conf -указание использовать конфигурационный файл;
2. -http -запуск интерпретатора в режиме web-сервера;
3. -d 8080 -указывает какой порт нужно слушать;
4. -w /home/www -указывает рабочий каталог web-сервера;
5. & -запускать интерпретатор newLISP в фоновом режиме.
Конфигурационный файл можно не указывать, хотя рабочий пример httpd.conf идет в поставке с newLISP по умолчанию.
Создание тестовой базы
Начать пожалуй стоит с создания, необходимой для опытов, базы данных. Создам простенькую телефонную книгу db_phonebook вот такой структуры:
База содержит две таблицы. Таблица tb_user содержит Ф.И.О. человека и необходимые примечания. Таблица tb_phone содержит номера телефонов, на однозначную принадлежность к конкретному человеку которых указывает поле user_id, а также содержит краткое описание телефонного номера.
SQL скрипт, предназначенный для создания необходимой базы данных, выглядит вот-так:
/**********************************************************************
Создание тестовой базы данных - скрипт phonebook.sql
Автор: kesha
**********************************************************************/
-- Создание базы db_phonebook
CREATE DATABASE IF NOT EXISTS db_phonebook;
-- Создание пользователя базы db_phonebook
GRANT SELECT,INSERT,UPDATE,DELETE ON db_phonebook.*
TO
IDENTIFIED BY 'secret';
-- Переход в базу db_phonebook
USE db_phonebook;
-- Создание таблицы tb_user
CREATE TABLE tb_user (
id INT(10) NOT NULL AUTO_INCREMENT, fio VARCHAR(100) NOT NULL,
example TEXT, PRIMARY KEY(id), UNIQUE(fio));
-- Создание таблицы tb_phone
CREATE TABLE tb_phone (
id INT(10) NOT NULL AUTO_INCREMENT, user_id INT(10) NOT NULL,
phone VARCHAR(20) NOT NULL, example VARCHAR(100), PRIMARY KEY(id), UNIQUE(phone));
/*********************************************************************/
Приведенный скрипт SQL достаточно сохранить в файле phonebook.sql и выполнить, находясь в MySQL мониторе от имени суперпользователя:
user $ mysql -u root -p
Enter password:
mysql> source /home/phonebook.sql; Query OK, 1 row affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) Database changed
Query OK, 0 rows affected (0.06 sec) Query OK, 0 rows affected (0.02 sec) mysql>
Ну и совсем было-бы не плохо добавить немного записей в базу данных. Для этого подойдет скрипт insert_ phonebook.sql
/*********************************************************************
Наполнение тестовой базы данных - скрипт insert_phonebook.sql
Автор: kesha
**********************************************************************/
-- Добавление в таблицу tb_user нескольких человек
INSERT INTO tb_user VALUES ( NULL,
'Иванов Иван Иванович',
'Хороший человек');
INSERT INTO tb_user VALUES ( NULL,
'Саяпин Василий Васильевич',
'Нужный человек');
INSERT INTO tb_user VALUES ( NULL,
'Косяков Лука Мудищев',
'На его звонки не отвечать, достанет');
-- Добавление в таблицу tb_phone нескольких телефонов
INSERT INTO tb_phone VALUES ( NULL,
(SELECT id FROM tb_user WHERE fio = 'Иванов Иван Иванович'),
'+7-999-555-55-55',
'Личный телефон');
INSERT INTO tb_phone VALUES ( NULL,
(SELECT id FROM tb_user WHERE fio = 'Иванов Иван Иванович'),
'+7-999-777-77-77',
'Корпоративный телефон');
INSERT INTO tb_phone VALUES ( NULL,
(SELECT id FROM tb_user WHERE fio = 'Саяпин Василий Васильевич'),
'+7-(9999)-999-99-99',
'Домашний телефон');
INSERT INTO tb_phone VALUES ( NULL,
(SELECT id FROM tb_user WHERE fio = 'Косяков Лука Мудищев'),
'+6-(6666)-666-66-66',
'Телефон и факс одновременно');
INSERT INTO tb_phone VALUES ( NULL,
(SELECT id FROM tb_user WHERE fio = 'Косяков Лука Мудищев'),
'+6-666-666-66-66',
'Не понятный телефон, но он иногда с него звонит');
/*********************************************************************/
Добавление записей в базу, и одновременное тестирование, нужно делать так:
user $ mysql db_phonebook -u wwwuser -p
Enter password:
mysql> source /home/insert_phonebook.sql; Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) mysql>
Ошибок нет, значить пора переходить к следующему этапу — доступ к данным с помощью newLISP. newLISP - доступ к базе mySQL
В стандартной поставке newLISP идут несколько модулей, сейчас нужен модуль под названием mysql.lsp, он как раз предназначен для легкого доступа к базе данных mysql. Свои эксперименты я буду проводить используя поставляемую вместе с newLISP среду разработки, которая вызывается командой newlisp-edit. В прочем тестировать получаемый скрипт лучше напрямую, из консоли.
#! /usr/bin/newlisp
;; Отработка навыков использования модуля mysql.lsp
;; Скрипт - mysql-test.lsp Автор: kesha; Подключение модуля mysql.lsp
(module "mysql.lsp"); Инициализация
(MySQL:init); Подключение к базе данных
(MySQL:connect "127.0.0.1" "wwwuser" "secret" "db_phonebook"); Стандартный SQL запрос к базе данных
(MySQL:query "select * from tb_user;"); Вывод на экран результатов SQL запроса
(println (MySQL:fetch-all)); Завершение соединения с базой данных
(MySQL:close-db); Конец скрипта
(exit)
Теперь стоит сохранить получившийся скрип в домашней папке под именем mysql- test.lsp и запускать консоль. Главное не забыть сделать скрипт исполняемым.
user $ chmod +x mysql-test.lsp
user $ ./mysql-test.lsp true
(("1" "Иванов Иван Иванович" "Хороший человек")
("2" "Саяпин Василий Васильевич" "Нужный человек")
("3" "Косяков Лука Мудищев" "На его звонки не отвечать, достанет"))
user $
Все замечательно работает. Смысл того, что делает скрипт ясно из примечаний. Пришла пора переходить к следующему этапу.
WEB — морда
Если web-сервер newLISP запущен на стандартный http порт (80), то при обращении к серверу из броузера порт можно не указывать. Если же используется не стандартный порт (например 8080), то обращаться к серверу из броузера нужно так http://localhost:8080. По умолчанию сервер ищет web-страничку index.html и если ее не находит, то возвращает ERR:404. Значить в первую очередь необходимо создать эту страничку. Моя страничка будет состоять из двух фреймов. В верхнем фрейме я размещу какой нибудь баннер, ну или типа того, в нижнем фрейме собственно и будет происходить взаимодействие с базой данных. Вот файл index.html:
<HTML>
<HEAD>
http-equiv="content-type" content="text/html; charset=UTF-8">Тестирование web-сервера newLISP
</HEAD><FRAMESET rows="15%,85%">
<FRAME frameborder="0" src="/head.html" name="head"><FRAME frameborder="0" src="/body.cgi?method=new" name="body">
</FRAMESET>
Ваш браузер пора уже обновить!!!</NOFRAMES>
</HTML>
Ниже дается пример баннера, странички head.html, которая будет загружаться в верхний фрейм. Конечно в реальности ее можно украсить и получше:
<HTML><HEAD>
http-equiv="content-type" content="text/html; charset=UTF-8"></HEAD>alight="left">Моя телефонная книга
alight="center" width="96%" size="5" color="silver"></BODY>
</HTML>
Наступает волнующий момент, пора занятся скриптами cgi, а конкретно скриптом body.cgi. Этот скрипт и будет отвечать за содержимое нижнего фрейма страницы index.html, а также, фактически будет нести всю нагрузку на телефонную книгу.
Тестирование CGI
Про модули, которые идут в стандартной поставке newLISP, я уже в курсе. Теперь мне нужен модуль cgi.lsp. Модуль простой как три рубля. Он позволяет получать и обрабатывать данные, переданные серверу в запросе как методом GET так и методом POST. Позволяет передавать клиенту web-страничку, в которую внедрен код newLISP обрамленный тегами <%...%>, обрабатывая этот код. В качестве тестового примера создам страничку body.html:
<HTML><HEAD>
http-equiv="content-type" content="text/html; charset=UTF-8"></HEAD>
<%(print html-body)%>
</BODY>
</HTML>В теле странички код newLISP печатает переменную html-body, осталось только подготовить эту переменную. Для первого опыта подойдет вот такой скрипт:
#! /usr/bin/newlisp
;; cgi - скрипт web-сервера: body.cgi
;; Автор: kesha; Подключаю модуль cgi.lsp
(module "cgi.lsp"); Формирование тела странички
(set 'html-body "Тест скрипта body.cgi
"); Обработка метода new
(when (= (CGI:get "method") "new") (CGI:put-page "body.html") (exit)); Завершение скрипта
(exit) Здесь все просто. Формирую переменную html-body, проверяю параметры запроса. Если параметры запроса удовлетворяют заданным — отправляю клиенту страничку body.html. Пора протестировать, да нужно не забыть сделать скрипт body.cgi исполняемым.
user $ chmod +x body.cgi
Копирую файлы index.html, head.html, body.html и body.cgi в папку, с которой работает newLISP web-сервер (/home/user/www/):
user $ cp ./index.html ./head.html ./body.html ./body.cgi /home/user/www/ Cтартую web-сервер:
user $ newlisp -http -d 8080 -w /home/user/www &
Вот теперь можно в браузере набирать http://localhost:8080
Что хочу получить?
Для удобства пользования «Телефонной книгой», нужно организовать поиск по базе как имен, так и телефонов. В самом простом случае на веб-страничке должно присуствовать:
1. Средство определения поиска (поиск по Ф.И.О., или по телефону);
2. Поле для ввода данных запроса;
3. Средство, например кнопка, посылки запроса в базу данных.
Результат поиска должен выдаваться в табличном виде, и там-же должна присутствовать кнопка возврата к начальной странице.
Поиск хочу получить достаточно универсальный, то-есть по буквам и (или) цифрам стоящим в любой позиции искомого слова (словосочетания). Для обозначения любых символов (в любом количестве) буду использовать звездочку - *:
1. * - все, что есть в базе;
2. *вич - все слова оканчивающиеся на вич;
3. лу* - все слова начинающиеся на лу;
4. *ви* - все слова содержащие слог ви;
5. л*а - все слова начинающиеся на л, и оканчивающиеся на а.
Код скрипта body.cgi получится перемешанным из кода html, sql и собственно кода newlisp, по этому я буду код выделять цветом:
1. вот так — буду выделять примечания;
2. вот так — код html;
3. вот так — код sql;
4. вот так — код newlisp;
5. вот так — вставки с необходимыми замечаниями.
Полное погружение.
Возьму уже существующий скрипт body.cgi и буду постепенно наполнять его функционалом. Вот заготовка скрипта:
#! /usr/bin/newlisp
;; cgi - скрипт web-сервера: body.cgi
;; Автор: kesha
; Подключаю модули
(module "cgi.lsp")
(module "mysql.lsp")
; Инициализация и соединение с базой данных
(MySQL:init)
(MySQL:connect "127.0.0.1" "wwwuser" "secret" "db_phonebook")
; Корректное завершение скрипта
(define (exit-body) (MySQL:close-db) (exit))
============== Здесь будет весь остальной текст программы ==============
; Завершение скрипта
(exit-body)
Тут все просто. Подключаю требуемые модули, получаю дескриптор базы mySQL (инициализирую базу), подключаюсь к базе и... Здесь мне приходится позаботится о корректном завершении скрипта. У алгоритма может быть несколько ветвей, приводящих к завершению программы. Правила newLISP требуют завершать программу вызовом функции exit, а ведь перед этим нужно еще завершить соединение с базой данных, поэтому я определяю функцию exit-body. Функция корректно закрывает соединение с базой данных, и завершает работу программы.
Теперь модернизирую обработчик метода new:
; Обработка метода new и нажатия кнопки Restore
(when (or (= (CGI:get "method") "new") (CGI:get "Restore"))(set 'html-body [text]
[/text])
(CGI:put-page "body.html") (exit-body))
Кнопка Restore — это будет кнопка возврата из просмотра результатов поиска по базе данных. Сначала я проверяю, вызывается метод new или нажата кнопка Restore. Дальше присваиваю переменной html-body сроку, представляющую собой код html.
Тут необходимы пояснения. В языке newLISP существует три способа обозначить строковую константу, это — двойные кавычки, фигурные скобки и теги [text]...[/text]. Здесь я использую теги.
Наконец отправляю клиенту страничку body.html (она ни как не изменилась), и
завершаю работу программы. Вот что получилось:
Следующий шаг, обработка поискового запроса:
;Обработка нажатия кнопки NewSearch
(when (CGI:get "NewSearch") (set 'html-body (append
(test-value (CGI:get "TypeSearch") (replace {*}(CGI:get "StrSearch"){%}))
[text]
[/text]))
(CGI:put-page "body.html") (exit-body))
В форме поиска кнопка «Искать» имеет имя NewSearch, вот здесь я как раз и обрабатываю нажатие этой кнопки. Опять идет присвоение строкового значения переменной html-body, но это значение уже формируется функцией append, в качестве параметров которой передается строка возвращаемая функцией test-value (кстати эту функцию еще предстоит написать), ну и соответственно код html. В свою очередь в функцию test-value в виде параметров передается значение выпадающего списка TypeSearch, из формы поиска, и обработанная функцией replace строка для поиска. Функция replace заменяет в строке, переданной ей вторым параметром, все символы переданные ей первым параметром, на символы переданные ей третьим параметром. Тоесть * на %, это нужно для организации поиска в базе данных, средствами самой базы данных.
Дальше опять посылаю клиенту страничку body.html и завершаю работу программы.
Пора проверить параметры на корректность:
; Проверка корректности параметров для поиска в базе
; и формирование требуемого подзапроса
(define (test-value table-search text-search) (if (empty? text-search)
(append
{})Пустая строка запроса...
(case table-search ("Человек" (query-base (append
"(SELECT id FROM tb_user WHERE fio LIKE '" text-search "')"))) ("Телефон" (query-base (append
"(SELECT user_id FROM tb_phone WHERE phone LIKE '" text-search "')")))
(true (append
{}})))))
{Не корректные параметры запроса...
В этой функции я в первую очередь проверяю пустая строка для поиска? Если пустая — сообщаю об этом клиенту и завершаю работу.
Если со строкой для поиска все нормально, то используя функцию case, формирую два sql подзапроса. Один для поиска по Ф.И.О., второй для поиска по телефонному номеру. Возможно по какой либо причине не удастся определить какой подзапрос нужно формировать, тогда отправляю клиенту сообщение об этой трагедии, и завершаю работу программы. Подзапросы формируются функцией append, которая объединяет текст sql запроса и строку, предназначенную для поиска. Полученный подзапрос передаю в функцию query-base:
; Поиск в базе данных
(define (query-base text-query) (MySQL:query (append [text]
SELECT tb_user.fio,tb_user.example,tb_phone.phone,tb_phone.example
FROM tb_user,tb_phone
WHERE tb_user.id = tb_phone.user_id
AND tb_user.id IN
[/text]
text-query
" LIMIT 0,10;"))
(if (= (MySQL:num-rows) 0) (append
{}) (format-result)))По Вашему запросу результатов нет...
Функцией append формирую полный sql запрос к базе данных, ну и отправляю его в базу. Проверяю количество найденных строк, если ноль — оповещаю об этом клиента и завершаю работу, еже-ли что-то нашли, тогда перехожу к функции формирования результатов поиска.
Форматирование результатов поиска осуществляет функция format-result:
; Форматирование результата поиска в базе данных (define (format-result)(append
{
"x" | ")) y))
"))
Результаты поиска в базе данных возвращаются MySQL:fetch-all в виде двумерного массива строк:
(("1" "Иванов Иван Иванович" "Хороший человек")
("2" "Саяпин Василий Васильевич" "Нужный человек")
("3" "Косяков Лука Мудищев" "На его звонки не отвечать, достанет"))
Функция map позволяет пройтись по одному измерению массива строк, и применить к каждому элементу массива действия, определяемые анонимной функцией fn (или канонически — lambda). Этим действием будет вторая функция map, которая посредством append, завернет каждую строчку в теги
Выводы
NewLISP — очень мощный скриптовый язык программирования, распространяемый по лицензии GPL, автора Lutz Mueller (от меня ему респект и уважуха). Маленький размер интерпретатора, большой функционал и использование только стандартных библиотек — вот несомненные достоинства newLISP
Библиотеки используемые newLISP:
user $ ldd /usr/bin/newlisp
linux-vdso.so.1 => (0x00007fffde771000) libm.so.6 => /lib/libm.so.6 (0x00000032a2800000) libdl.so.2 => /lib/libdl.so.2 (0x00000032a2400000)
libreadline.so.6 => /lib/libreadline.so.6 (0x00000032a3800000)
libc.so.6 => /lib/libc.so.6 (0x00000032a2000000)
/lib64/ld-linux-x86-64.so.2 (0x00000032a0a00000)
libncurses.so.5 => /lib/libncurses.so.5 (0x00000032a0e00000) И для примера библиотеки используемые bash:
user $ ldd /bin/bash
linux-vdso.so.1 => (0x00007fffbeb1f000)
libncurses.so.5 => /lib/libncurses.so.5 (0x00000032a0e00000)
libdl.so.2 => /lib/libdl.so.2 (0x00000032a2400000)
libc.so.6 => /lib/libc.so.6 (0x00000032a2000000)
/lib64/ld-linux-x86-64.so.2 (0x00000032a0a00000)
Заключение
Данный материал можно использовать как вам заблагорассудится, только пожалуйста автора не забывайте ;-)
P.S. Полный текст скрипта body.cgi
#! /usr/bin/newlisp
;; cgi - скрипт web-сервера: body.cgi
;; Автор: kesha
; Подключаю модули
(module "cgi.lsp")
(module "mysql.lsp")
; Инициализация и соединение с базой данных
(MySQL:init)
(MySQL:connect "127.0.0.1" "wwwuser" "secret" "db_phonebook")
; Корректное завершение скрипта
(define (exit-body) (MySQL:close-db) (exit))
; Форматирование результата поиска в базе данных
(define (format-result)(append
{}
(join (map (fn (y)(append "" ")) (MySQL:fetch-all)))
(join (map (fn (x)(append ""x" ")) y))
"
"
"))
; Поиск в базе данных
(define (query-base text-query) (MySQL:query (append [text]
SELECT tb_user.fio,tb_user.example,tb_phone.phone,tb_phone.example
FROM tb_user,tb_phone
WHERE tb_user.id = tb_phone.user_id
AND tb_user.id IN
[/text]
text-query
" LIMIT 0,10;"))
(if (= (MySQL:num-rows) 0) (append
{}) (format-result)))По Вашему запросу результатов нет...
; Проверка корректности параметров для поиска в базе
; и формирование требуемого подзапроса
(define (test-value table-search text-search) (if (empty? text-search)
(append
{}) (case table-searchПустая строка запроса...
("Человек" (query-base (append
"(SELECT id FROM tb_user WHERE fio LIKE '" text-search "')"))) ("Телефон" (query-base (append
"(SELECT user_id FROM tb_phone WHERE phone LIKE '" text-search "')")))
(true (append
{}})))))
{Не корректные параметры запроса...
; Обработка нажатия кнопки NewSearch
(when (CGI:get "NewSearch") (set 'html-body (append
(test-value (CGI:get "TypeSearch") (replace {*}(CGI:get "StrSearch"){%}))
[text]
[/text]))
(CGI:put-page "body.html") (exit-body))
; Обработка метода new и нажатия кнопки Restore
(when (or (= (CGI:get "method") "new") (CGI:get "Restore"))
(set 'html-body [text]
[/text])
(CGI:put-page "body.html") (exit-body))
; Завершение скрипта
(exit-body)
Следующая > |
---|
Кто онлайн?
Мы в twitter
Последние комментарии
-
Мало! Продолжить вплоть до KDE
By alexx -
средствами proftpd как понимаю нельзя. Однако сто...
By анонимус -
Я тоже решал эту задачу. Столкнулся с некоторыми п...
By Петр -
ага....ну я уже разобрался) сейчас задача стоит к...
By io -
Сори, написано в конце статьи. А вы точно DNS пров...
By Петр -
В том, что в этой статье ничего не описано про нас...
By Петр -
Можно ли привязать FTP к определённому интерфейсу,...
By remalex -
Блин, сделал как в статье. Но не джобит( инет я по...
By IO
Комментарии
Побольше бы таких статей о newlisp.
RSS лента комментариев этой записи