Руководство Java Servlet Filter

1- Введение

Статья основана на:
  • Eclipse 4.6 NEON

  • Tomcat 8.x

Вы должны иметь знание о Servlet перед тем, как прочитать статью про  Servlet- Filter, если вы начинающий, вы можете посмотреть  Java Servlet по ссылке:

2- Почему нужен Server-Filter?

Ситуация 1:

Обычно, когда пользователь запрашивает веб-страницу, запрос будет отправлен на server, он должен проходить через фильтры (Filter) до того как дойти до запрошенной страницы, как в изображении ниже.

Ситуация 2:

Однако существуют ситуации запроса пользователя, которые не проходят все уровни  Filter.

Ситуация 3:

Ситуация когда пользователь отправляет запрос одной страницы (page1), этот запрос проходит через Filter, у определенного filter запрос будет перенаправлен на другую страницу (page2).
Пример ситуации:
  1. Пользователь посылает запрос, чтобы посмотреть страницу с личной информацией.
  2. Запрос будет отправлен на Server.
  3. Она проходит Filter, который записывает информацию log.
  4. Идет к Filter и проверяет вошел ли пользователь в систему, filter проверил, и увидел, что пользователь не вошел в систему, то перенаправляет запрос на страницу входа в систему пользователя.

3- Что может сделать Servlet-Filter?

Иногда у вас есть только одно понятие это Filter используется чтобы перенаправить запрос пользователя на другую страницу, или блокировать доступ к определенному сайту, если пользователь не имеет прав. Или используется для записи информации Log.
На самом деле Filter может быть использован для кодирования вебсайта (encoding). Например, установить кодировку UTF-8 для страницы. Открыть и закрыть соединение  к Database и подготовить транзакцию JDBC (JDBC Transaction).

4- Создать Project для начала с Servlet-Filter

Сначала мы создаем WebApp проект для работы с Servlet-Filter.
  • File/New/Other...
Ввести:
  • Project Name: ServletFilterTutorial
Проект создан:
Создать файл index.html:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Home Page</title>
</head>
<body>

  <h2>Servlet-Filter Tutorial</h2>

</body>
</html>

5- Конфигурация среды запуска

Щелкните правой кнопкой мыши на проект и выберите Properties
Щелкните правой кнопкой мыши на проект и выберите:
  • Run As/Run on Server
ОК!, Все готово, чтобы начать изучать  Servlet- Filter.

6- Первый пример Servlet-Filter 

Kласс Servlet Filter, который выполняет interface  javax.servlet.Filter. Класс LogFilter ниже записывает время и путь запроса, отправленного к WebApp.
LogFilter.java
package org.o7planning.tutorial.servletfilter;

import java.io.IOException;
import java.util.Date;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class LogFilter implements Filter {

	public LogFilter() {
	}

	@Override
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("LogFilter init!");
	}

	@Override
	public void destroy() {
		System.out.println("LogFilter destroy!");
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {

		HttpServletRequest req = (HttpServletRequest) request;

		String servletPath = req.getServletPath();

		System.out.println("#INFO " + new Date() + " - ServletPath :" + servletPath //
				+ ", URL =" + req.getRequestURL());

		// Разрешить request продвигаться дальше. (Перейти данный Filter).
		chain.doFilter(request, response);
	}

}
Configure filter in web.xml:
Добавить в фрагмент конфигурации  в web.xml:
<!--
  Declaring a filter named logFilter
-->
<filter>
  <filter-name>logFilter</filter-name>
  <filter-class>org.o7planning.tutorial.servletfilter.LogFilter</filter-class>
</filter>

<!--
  Declare the path (of the page) will have the effect of logFilter
 /* for all paths
-->
<filter-mapping>
  <filter-name>logFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://java.sun.com/xml/ns/javaee"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                               http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id="WebApp_ID" version="3.0">

<display-name>ServletFilterTutorial</display-name>

<filter>
 <filter-name>logFilter</filter-name>
 <filter-class>org.o7planning.tutorial.servletfilter.LogFilter</filter-class>
</filter>

<filter-mapping>
 <filter-name>logFilter</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>



<welcome-file-list>
 <welcome-file>index.html</welcome-file>
 <welcome-file>index.htm</welcome-file>
 <welcome-file>index.jsp</welcome-file>
 <welcome-file>default.html</welcome-file>
 <welcome-file>default.htm</welcome-file>
 <welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
Перезапустите приложение:
Вы можете запустить следующие ссылки в браузере, здесь есть ссылки на источники, которые не существуют на вашем WebApp, но они все находятся под влиянием LogFilter.
Информация Log записывается на экране Console:
Следующий код позволяет запросу пройти фильтр (Filter) чтобы продолжить до цели (желаемой страницы).
// Разрешить request продвигаться дальше.
// Может прийти к следующему Filter или к цели.
chain.doFilter(request, response);

7- Модель работы Filter

Когда пользователь посылает запрос, цель (target) может быть источником данных (resource) или servlet. Запрос должен пройти через Filter и, наконец дойти до цели. Filter и цели сцеплены (chained) вместе как в изображении ниже:
Используйте chain.doFilter(request,response), чтобы переместить запрос к следующей ссылке. Если в filter chain.doFilter (request, response) не вызывается, запрос пользователя не достигнет цели, он останавливается в этом фильтре.

8- Инициализированные параметры Servlet-Filter

Как и с  Servlet, вы можете инициализировать параметры для Filter. В примере ниже, Filter имеет обязанность записи log в файл, вы можете настроить конфигурацию в web.xml имя файла для записи.
Log2Filter.java
package org.o7planning.tutorial.servletfilter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Log2Filter implements Filter {

	private String logFile;

	public Log2Filter() {
	}

	@Override
	public void init(FilterConfig fConfig) throws ServletException {
		this.logFile = fConfig.getInitParameter("logFile");

		System.out.println("Log File " + logFile);
	}

	@Override
	public void destroy() {
		System.out.println("Log2Filter destroy!");
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {

		if (this.logFile != null) {
			// Записать информацию Log в File.
			this.logToFile(this.logFile);
		}

		// Разрешить данный request пройти дальше. (Пройти этот Filter).
		chain.doFilter(request, response);
	}

	private void logToFile(String fileName) {
		// Записать log в file..
		System.out.println("Write log to file " + fileName);
	}

}
Добавьте конфигурацию в web.xml:
<filter>
   <filter-name>log2Filter</filter-name>
   <filter-class>org.o7planning.tutorial.servletfilter.Log2Filter</filter-class>
   <init-param>
       <param-name>logFile</param-name>
       <param-value>AppLog.log</param-value>
   </init-param>
</filter>

<filter-mapping>
   <filter-name>log2Filter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>ServletFilterTutorial</display-name>

<filter>
  <filter-name>logFilter</filter-name>
  <filter-class>org.o7planning.tutorial.servletfilter.LogFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>logFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>


<filter>
  <filter-name>log2Filter</filter-name>
  <filter-class>org.o7planning.tutorial.servletfilter.Log2Filter</filter-class>
  <init-param>
      <param-name>logFile</param-name>
      <param-value>AppLog.log</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>log2Filter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
 


<welcome-file-list>
  <welcome-file>index.html</welcome-file>
  <welcome-file>index.htm</welcome-file>
  <welcome-file>index.jsp</welcome-file>
  <welcome-file>default.html</welcome-file>
  <welcome-file>default.htm</welcome-file>
  <welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>

9- Servlet-Filter url-pattern

Существуют 3 способа как вы можете конфигурировать url-pattern для  Filter:
URL Pattern Example
/* http://example.com/contextPath
http://example.com/contextPath/status/abc
/status/abc/* http://example.com/contextPath/status/abc
http://example.com/contextPath/status/abc/mnp
http://example.com/contextPath/status/abc/mnp?date=today
http://example.com/contextPath/test/abc/mnp
*.map http://example.com/contextPath/status/abc.map
http://example.com/contextPath/status.map?date=today
http://example.com/contextPath/status/abc.MAP

10- Servlet-Filter используя Annotation

В примерах выше Filter конфигурируется в  web.xlm, однако с WebApp версии 3 и далее, вы можете использовать Annotation для конфигурации Filter.

Этот пример иллюстрирует, когда пользователь запрашивает просмотр файла изображения (JPG, PNG или GIF), filter будет проверять, существует изображение или нет, при отсутствии изображения, Filter перенаправит запрос на файл изображения по умолчанию.
Сначала скопируйте 2 изображения flower.png & image-not-found.png в папках images на вашем WebApp.
 
ImageFilter.java
package org.o7planning.tutorial.servletfilter;

import java.io.File;
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebFilter(urlPatterns = { "*.png", "*.jpg", "*.gif" }, initParams = {
		@WebInitParam(name = "notFoundImage", value = "/images/image-not-found.png") })
public class ImageFilter implements Filter {

	private String notFoundImage;

	public ImageFilter() {
	}

	@Override
	public void init(FilterConfig fConfig) throws ServletException {

		// ==> /images/image-not-found.png
		notFoundImage = fConfig.getInitParameter("notFoundImage");
	}

	@Override
	public void destroy() {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {

		HttpServletRequest req = (HttpServletRequest) request;

		// ==> /images/path/my-image.png
		// ==> /path1/path2/image.pngs
		String servletPath = req.getServletPath();

		// Абсолютная ссылка исходной папки WebApp (WebContent).
		String realRootPath = request.getServletContext().getRealPath("");

		// Абсолютная ссылка к файлу изображения.
		String imageRealPath = realRootPath + servletPath;

		System.out.println("imageRealPath = " + imageRealPath);

		File file = new File(imageRealPath);

		// Проверить, существуют ли изображения.
		if (file.exists()) {

			// Разрешить request продвинуться далее. (Пройти этот Filter).
			// (Чтобы пройти дальше к запрошенному файлу изображений).
			chain.doFilter(request, response);

		} else if (!servletPath.equals(this.notFoundImage)) {

			// Redirect (Перенаправить) к файлу изображения 'image not found'.
			HttpServletResponse resp = (HttpServletResponse) response;

			// ==> /ServletFilterTutorial + /images/image-not-found.png
			resp.sendRedirect(req.getContextPath() + this.notFoundImage);

		}

	}

}
Перезапустите ваше приложение и запустите следующие URL:
С ссылкой запроса изображения, а изображение не существует, то она будет перенаправлена к изображению по умолчанию.
В приведенном выше примере вы можете также использовать пересылка (Forward) вместо того, чтобы перенаправить (Redirect) запрос на изображение по умолчанию в случае, если запрошенное изображение, не существует.
// Redirect:

// ==> /ServletFilterTutorial + /images/image-not-found.png
response.sendRedirect(request.getContextPath()+ this.notFoundImage);

// Forward:

request.getServletContext().getRequestDispatcher(this.notFoundImage).forward(request, response);

11- Установка соединения JDBC в Filter

Вы можете создать объект Connection соединить с   JDBC  в Servlet  для обработки с Database. Но вы также можете создать объект Connection соединить с  JDBC в Filter, и он будет работать с разными Servlet. И вы можете использовать этот  Connection на протяжении всего пути запроса. Чтобы легче было понять, вы можете посмотреть на иллюсстрацию ниже:
ConnectionUtils это класс, который создает объект  Connection, соединяя к database, в данной статье я буду детально знакомить со способом, как вы получаете объект Connection.
Вы можете просмотреть статью  JDBC по ссылке:
ConnectionUtils.java
package org.o7planning.tutorial.servletfilter.conn;

import java.sql.Connection;

public class ConnectionUtils {

	public static Connection getConnection() {

		// Создать Connection (соединение) к Database.
		Connection conn = null;

		// .....
		return conn;
	}

	public static void closeQuietly(Connection conn) {
		try {
			conn.close();
		} catch (Exception e) {
		}
	}

	public static void rollbackQuietly(Connection conn) {
		try {
			conn.rollback();
		} catch (Exception e) {
		}
	}
}
MyUtils.java
package org.o7planning.tutorial.servletfilter.conn;

import java.sql.Connection;

import javax.servlet.ServletRequest;

public class MyUtils {

	public static final String ATT_NAME = "MY_CONNECTION_ATTRIBUTE";

	// Сохранить объект Connection в атрибут (attribute) в request.
	// Сохраненная информация существует только во время запроса (request)
	// до тех пор, когда данные возвращаются приложению пользователя.
	public static void storeConnection(ServletRequest request, Connection conn) {
		request.setAttribute(ATT_NAME, conn);
	}

	// Получить объект Connection сохраненный в атрибуте request.
	public static Connection getStoredConnection(ServletRequest request) {
		Connection conn = (Connection) request.getAttribute(ATT_NAME);
		return conn;
	}
}
Я объявляю  URL-pattern для JDBCFilter это  /*, этот filter будет работать со всеми запросами пользователей.
JDBCFilter.java
package org.o7planning.tutorial.servletfilter.conn;

import java.io.IOException;
import java.sql.Connection;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter(urlPatterns = { "/*" })
public class JDBCFilter implements Filter {

	public JDBCFilter() {
	}

	@Override
	public void init(FilterConfig fConfig) throws ServletException {

	}

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {

		HttpServletRequest req = (HttpServletRequest) request;

		// 
		String servletPath = req.getServletPath();

		// Открыть Connection (соединение) только для request со специальной ссылкой
		// (Например ссылка к servlet, jsp, ..)
		// Избегать открытие Connection для обычноых запросов
		// (Например image, css, javascript,... )
		if (servletPath.contains("/specialPath1") || servletPath.contains("/specialPath2")) {
			Connection conn = null;
			try {
				// Создать объект Connection подключенный к database.
				conn = ConnectionUtils.getConnection();
				// Настроить автоматически commit = false, чтобы активно контролировать.
				conn.setAutoCommit(false);

				// Сохранить в атрибут (attribute) в request.
				MyUtils.storeConnection(request, conn);

				// Разрешить request продвигаться дальше (Пройти этот Filter).
				chain.doFilter(request, response);

				// Вызвать commit() чтобы завершить транзакцию (transaction) с DB.
				conn.commit();
			} catch (Exception e) {
				ConnectionUtils.rollbackQuietly(conn);
				throw new ServletException();
			} finally {
				ConnectionUtils.closeQuietly(conn);
			}
		}
		// Для обычных request.
		else {
			// Разрешить request продвигаться дальше (Пройти этот Filter).
			chain.doFilter(request, response);
		}

	}

}
В следующем Filter или в servlet или JSP-странице (в одном запросе), вы можете получить объект Connection, который сохранился в атрибуте (attribute) запроса:
Connection conn = (Connection) request.getAttribute(ATT_NAME);

// Или
Connection conn = MyUtils.getStoredConnection();

12- Руководство программирования JSP

Далее вы можете изучить про JSP:

View more categories: