Java cpu 1

Материал из Dom.

Перейти к: навигация, поиск

[править] Маленький магазинчик на java + mysql + ЧПУ

Сегодня я расскажу о формировании ЧПУ для java-основанных веб-проектов на примере небольшого веб-магазинчика. Это будет очень простой магазинчик, все данные в котором помещаются в одной таблице. Информация же будет выводиться на нескольких страницах, в виде иерархии разделов каталога и, собственно, товаров внутри раздела.


Очевидно, что “Настоящему” программисту нет никакого дела до того, как называется его страница:

index.jsp?sect=food&article=milk&date=fresh

Например, такой адрес я назову ППУ – программисто-понятные урлы (yes!, я сделал это первым, я ввел новый термин). или же

catalog/food/milk/fresh 

А этот стиль адресов “обозвали” еще до меня как ЧПУ (человеко-понятные урлы)

В любом случае, в адресе содержится вся необходимая информация, чтобы понять, что посетитель сайта хочет получить информацию о “молоке”, находящееся в “продуктовом” разделе каталога и, желательно, свежем.

Другое дело, что для посетителя сайта второй вариант адреса более понятен и легче запоминается. Его проще вспомнить спустя пару дней и набрать без ошибок. Интуитивно ясно, что если в адресе, наш посетитель удалит фрагмент “fresh”, то он получит сведения только о молоке, если же удалить и часть “milk”, так чтобы остался “catalog/food”, то для клиента ожидаемо попасть, именно, в раздел со всеми продовольственными товарами. В Интернете иногда встречаются страшные байки о том, что поисковые системы не умеют качественно индексировать адреса страниц с передаваемыми после “?” переменными и их значениями – не верьте, это такой же фольклор как “летающие тарелки” и “каждой советской семье по квартире в 2000 году”. Так что, кроме фактора “usability” других оснований для внедрения ЧПУ нет – а разве этого мало?

Что самое приятное – добавление средств ЧПУ, даже к уже существующему сайту, занимает не более десятка минут. Вам вовсе не нужно перестраивать структуру каталогов свого сайта, даже не нужно переписывать код вашего jsp-файла или сервлета. Виртуальный (не отображенный в физической структуре дерева каталогов сайта) адрес можно при поступлении запроса преобразовать (автоматически и абсолютно незаметно для, собственно, посетителя сайта) в старый стиль, с переменными и их значениями после знака “?”.

Как это сделать?

Для разработчиков сайтов использующих LAMP (linux&apache&mysql&php), известны две базовые методики обработки ЧПУ. Первая основана на том, что используется mod_rewrite. Этот модуль (расширение возможностей apache) срабатывает до вызова запрашиваемого файла. В файле .htaccess вы перечисляете шаблоны запрашиваемых адресов в стиле ЧПУ, каждому такому шаблону (при их записи используются регулярные выражения, чтобы придать шаблонам универсальность) ставится в соответствии новый адрес. Второй подход основан на возможности указать для сайта страницу, срабатывающую как обработчик ситуации “404 – страница не найдена”. Всякий раз, когда запрашивается ЧПУ (естественно, такой виртуальный адрес не находится) apache вызывает специальный файл 404. В этом файле пишется несложный код, который анализирует конфигурационные переменные сервера – в них хранится, в том числе, и запрошенный адрес ЧПУ – затем скрипт вычисляет, то какая страница (функция/метод класса) должна быть вызвана для показа запрошенной информации.

В мире java, если вы используете tomcat или иной java-сервер совместно с apache (надо сказать, что это довольно неплохая идея – использовать apache для отображения статических страниц, а tomcat для выполнения java-кода), то вы можете использовать mod_rewrite. Если же apache не доступен, то вы могли бы создать собственную функцию обработчик события 404, анализировать в ней запрошенный адрес и все как в прошлый раз. С другой стороны, в java есть интересная и полезная концепция – сервлеты-фильтры. Сервлеты-фильтры вызываются до того непосредственно вызова запрашиваемого файла и после этого. Сфера применения фильтров огромна – наиболее часто мы используем их для защиты страниц от несанкционированного доступа, работа с интернализацией и локализацией веб-страниц, создание хитрых хаков обеспечивающих корректную работу унаследованных решений. Важно, что даже если запрошенный адрес не существует, все равно сервлет-фильтр будет вызван, а значит, в нем можно выполнить подмену адреса. Написать кода такого сервлета не сложно и могло бы стать неплохой тренировкой для начинающего серверного java-разработчика. Но мы, естественно, пойдем другим путем: существует множество java-библиотек, решающих нужные нам задачи, так что остается выбрать одну из них. На странице wikipedia (http://en.wikipedia.org/ Rewrite_engine) в разделе Rewrite engines for Java 2 Platform, Enterprise Edition указаны следующие библиотеки для rewrite-инга.

- http://www.zlatkovic.com/httpredirectfilter.en.html - http://tuckey.org/urlrewrite/ - http://software.softeu.cz/rewriter/

Я расскажу о последней из них (http://software.softeu.cz/rewriter/). Предположим, вы скачали с сайта разрабочика jar-архив с библиотекой. Теперь я создам пустое веб-приложение и размещу его в папке tomcat (я использую tomcat 6.1.0 под jdk 1.6, работоспособность библиотеки в других ситуациях я не тестировал). В папке веб-приложения WEB-INF я создам подпапку lib, в которую скопирую архив библиотеки, а также в файле web.xml, подключу сервлет cz.softeu.rewriter.RewriterFilter:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd"
 version="2.5">
 
 <display-name>TestRewrite machine</display-name>
 <description>
 based on http://software.softeu.cz/rewriter/
 </description>
 
 <filter>
 <filter-name>RewriterFilter</filter-name>
 <filter-class>cz.softeu.rewriter.RewriterFilter</filter-class>
 </filter>
 <filter-mapping>
 <filter-name>RewriterFilter</filter-name>
 <url-pattern>/catalog/*</url-pattern>
 </filter-mapping>
 
</web-app>

В данном файле обратите внимание на регистрацию шаблона адресов, которые будет обрабатывать фильтр. Я говорю, что любые страницы, начинающиеся с “/catalog/”, должны быть обработаны на предмет преобразования адреса.

Теперь необходимо разработать прототип страницы магазина shop.jsp. В нем я буду принимать входные переменные: sect, article, date. Переменные будут служить для поиска информации в таблице базы данных mysql следующей структуры (см. рис. 1), затем я заполнил таблицу данными о товарах нашего магазинчика (см. рис. 2):

Изображение:bazar_1.png

Изображение:bazar_2.png

Для работы jsp-файла с информацией в mysql-базе мне понадобится jdbc-драйвер, который я загрузил с сайта mysql.com и поместил внутрь каталога lib моего веб-приложения. База создана в кодировке utf8 и, следовательно, я указываю параметры кодировки при соединении. В объект Properties помещаются такие параметры, как имя пользователя для входа на сервер mysql, его пароль и кодировки. Дальнейшие действия тривиальны: я проверяю, какое количество параметров было передано в jsp-страницу (совершенно не заботясь об будущих ЧПУ), затем делаю выборку нужной информации и отображаю ее в виде таблицы со ссылками. Ссылки же здесь в формате ЧПУ. В ходе работы сприпта выявилась проблема: когда в адресной строке браузера запрашивается адрес вида: catalog/food, а в возвращенной странице присутствуют ссылки вида: catalog/food/2007.1.14/milk, то браузер считает (и надо сказать вполне правильно, что итоговый адрес для запроса должен быть catalog/catalog/food/2007.1.14/milk). Это рушит нам схему именования страниц и самым простым способом задавать адреса будет добавление в секцию head страницы специального тега base. Его назначение задать “базу” – основу, от которой будет выполняться отсчет адресов всех страниц размещенных на сайте. Например, так:

<base href="http://localhost:8080/" />


Вот полный код примера jsp-страницы:

<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.sql.*" %>
<%@ page import="java.util.*" %>
<%@ page import="java.sql.Date" %>
<%--
 Created by IntelliJ IDEA.
 User: Programmer
 Date: 03.11.2007
 Time: 21:24:28
 To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
   <title>Simple jsp page</title>
   <base href="http://localhost:8080/" />
  </head>
 <body>
 
 <%
 Class.forName("com.mysql.jdbc.Driver").newInstance();
 Properties props = System.getProperties();
 props.setProperty("user" , "root");
 props.setProperty("password" , "");
 props.setProperty("useUnicode" , "true");
 props.setProperty("characterEncoding" , "utf8");
 props.setProperty("characterSetResults" , "utf8");
 Connection coco = DriverManager.getConnection("jdbc:mysql://center/rewriteshop", props);
 // создаем подключение к базе данных mysql с каталогом товаров
 String sql_sect = "select distinct sect from shop";
 String sql_dates = "select distinct article, date_of from shop where sect = ?";
 String sql_article = "select * from shop where sect = ? and date_of = ? and article = ?";
 // задали строки sql-запросов для различных ситуаций когда нашу страницу запросили
 if (request.getParameter("sect") == null){
  // первая ситуация когда не передано в страницу название секции, тогда следует
  // отобрать и вывести список всех секций товаров
  PreparedStatement pstmt_sects = coco.prepareStatement(sql_sect);
  ResultSet rs = pstmt_sects.executeQuery();
  out.print("<table border='1'>");
  while (rs.next()){
   String sect = rs.getString("sect");
   out.print("<tr><td><a href='catalog/"+sect+"'>"+sect+"</a></td></tr>");
   // печатаем строку таблицы с одной ячейкой и 
      размещаем в ней ссылку на раздел каталога
 }
  rs.close();
  out.print("</table>");
 }
 else{
  if (request.getParameter("date") == null){
  String sect = request.getParameter("sect");
  PreparedStatement pstmt_dates = coco.prepareStatement(sql_dates);
  pstmt_dates.setString(1, sect);
  ResultSet rs = pstmt_dates.executeQuery();
  out.print("<table border='1'>");
  while (rs.next()){
   String date_of = rs.getString("date_of");
   String article = rs.getString("article");
   out.print("<tr><td><a href='catalog/"+sect+"/"+date_of+"/"+article+"'>"+sect+"/"+
   date_of+"/"+article+"</a></td></tr>");
   // печатаем строку таблицы с одной ячейкой и размещаем 
      в ней ссылку на раздел каталога
  }
  rs.close();
  out.print("</table>");
 }
 else{
  if (request.getParameter("article") != null){
  String sect = request.getParameter("sect");
  String date = request.getParameter("date");
  String article = request.getParameter("article");
  PreparedStatement pstmt_article = coco.prepareStatement(sql_article);
  pstmt_article.setString(1, sect);
  pstmt_article.setString(2, date);
  pstmt_article.setString(3, article);
  ResultSet rs = pstmt_article.executeQuery();
  rs.next();
  String info = rs.getString("info");
  String image = rs.getString("img");
  out.print("Article: " + article + "<br />");
  out.print("Info: " + info + "<br />");
  out.print("Image: <img src='pics/" + image + "'/><br />");
  rs.close();
  out.print("</table>");
 }
 }
 
 }
 
 %>
 </body>
</html>

И вот результат ее работы (рис.4 и рис. 5)

Изображение:bazar_4.png

Изображение:bazar_5.png

Теперь последний шаг: для того чтобы запрошенные адреса вида:

http://localhost:8080/catalog/manufacture/2006.1.12/screwdriver

Преобразовались в следующие:

/index.jsp?sect=manufacture&date=2006.1.12&article=screwdriver

Необходимо в папке web-inf создать файл с настройками для сервлета-фильтра rewriter-а.

Имя файла должно быть: rewriter-config.xml. Его же внутреннее устройство очевидно: внутри корневого тега rewriter-config, мы задаем множество тегов b:regex, каждый из которых задает одно, отдельное, правило преобразования запрашиваемого адреса. Входной адрес соответствует регулярному выражению внутри тега “from”, а целевой адрес должен быть задан в теге “to”. Очевидно, что вы можете группировать части исходного адреса с помощью круглых скобок, а затем ссылаться на них внутри шаблона “to” используя запись “$цифра”. Цифра должна быть равна порядковому номеру части regexp-шаблона заданного внутри тега “from”. И вот пример кода файла rewriter-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<rewriter-config xmlns:b="http://rewriter.softeu.cz/basic/">
    <b:regex>
        <from>^/catalog/([^/]+)$</from>
        <to>/index.jsp?sect=$1</to>
    </b:regex>
    <b:regex>
        <from>^/catalog/([^/]+)/([^/]+)$</from>
        <to>/index.jsp?sect=$1&amp;date=$2</to>
    </b:regex>
    <b:regex>
        <from>^/catalog/([^/]+)/([^/]+)/([^/]+)$</from>
        <to>/index.jsp?sect=$1&amp;date=$2&amp;article=$3</to>
    </b:regex>
</rewriter-config>

Весь процесс обработки-преобразования адресов журналируется - это позволит быстрее отлаживать код регулярных выражений.

Изображение:bazar_3.png

Subscribe Now!

...

ObMachine projects & articles (java, flash, flex, php, ...)  -- black-zorro.com


Личные инструменты
Навигация