JSP/Servletアプリでvalidatorの機能を使う

Servlet/JSPの環境で、入力値のチェックを行ってみます。
ここでは、バリデーションという仕組みを利用します。

この記事では、バリデーションの他に、DispatcherServletパターン、setAttributeでインスタンスを設定して、JSPで表示する方法のサンプルとしても使えます。

さて、今回の例では、名前、年齢、身長を入力して、その入力値をチェックします。

フォームはこんなイメージです。

f:id:sho322:20140517173628j:plain

ここにチェックした入力値が空だったり、数値を入れるべきところに変な文字列が入っていたりすると、以下のようにエラーメッセージを表示します。

f:id:sho322:20140517173442j:plain

サンプルのフォルダ構成は以下のようになっています。
こちらがソースの構成です。
f:id:sho322:20140517173538j:plain

こちらが、WEB-INF周りの構成。
f:id:sho322:20140517173429j:plain

それでは、ソースを見てきましょう。
まずはコントローラーです。
■Controller(インターフェース)

package controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Controller {
String handleRequest(HttpServletRequest request, HttpServletResponse response);
}

■InputFriendController

package controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class InputFriendController implements Controller {
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
return "/WEB-INF/jsp/FriendForm.jsp";
}
}

■SaveFriendController

package controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import domain.Friend;
import validator.FriendValidator;
import form.FriendForm;
public class SaveFriendController implements Controller {
public String handleRequest(HttpServletRequest request,
HttpServletResponse response) {
FriendForm friendForm = new FriendForm();
friendForm.setName(request.getParameter("name"));
friendForm.setAge(request.getParameter("age"));
friendForm.setHeight(request.getParameter("height"));
FriendValidator friendValidator = new FriendValidator();
List<String> errors = friendValidator.validate(friendForm);
if(errors.isEmpty()) {
Friend friend = new Friend();
friend.setName(friendForm.getName());
friend.setAge(Integer.parseInt(friendForm.getAge()));
friend.setHeight(Float.parseFloat(friendForm.getHeight()));
request.setAttribute("friend", friend);
return "/WEB-INF/jsp/FriendDetails.jsp";
} else {
request.setAttribute("errors", errors);
request.setAttribute("form", friendForm);
return "/WEB-INF/jsp/FriendForm.jsp";
}
}
}

これらのコントローラーは、ブラウザからリクエストされたHTTPを処理する役割を果たします。
各コントローラーに、入力値の保存や、入力フォームの表示などの役割を割り当てて、分離しています。

そうすることで、以下のようなDispatcherServletによって、各コントローラーに振り分けることができます。
DispatcherControllerパターンは、JavaEEの古くからのデザインパターンで、リクエストを最初に受け付けるコントローラーの役割を果たします。

最初に受け付けて、担当者に振り分けるわけです。

■DispatcherServlet

package servlet;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import controller.InputFriendController;
import controller.SaveFriendController;
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 38749L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
public void process(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
String uri = request.getRequestURI();
int lastIndex = uri.lastIndexOf("/");
String action = uri.substring(lastIndex + 1);
String dispatchUrl = null;
if (action.equals("friend_input.action")) {
InputFriendController controller = new InputFriendController();
dispatchUrl = controller.handleRequest(request, response);
} else if (action.equals("friend_save.action")) {
SaveFriendController controller = new SaveFriendController();
dispatchUrl = controller.handleRequest(request, response);
}
if (dispatchUrl != null) {
RequestDispatcher rd = request.getRequestDispatcher(dispatchUrl);
rd.forward(request, response);
}
}
}

このDispatcherServletでは、リクエストを受けたURLを解析して、そのアクション(どんな処理をしたいのか)を取り出します。
そのアクションに応じたコントローラーに処理を振り分けるわけです。

DispatcherServletに振り分けるためのweb.xmlの設定は以下のとおりです。

■web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<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_2_5.xsd"
         id="WebApp_ID" version="2.5">
<display-name>springweb</display-name>
<!--
		- Location of the XML file that defines the root application context.
		- Applied by ContextLoaderListener.
	-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/application-config.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
		- Servlet that dispatches request to registered handlers (Controller implementations).
	-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>servlet.DispatcherServlet</servlet-class>
<!--  
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/mvc-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        -->
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>

STS(Spring Tool Suite)のSpring Web Mavenプロジェクトを使っているので、余計な記述も含まれています。

次に、今回のメインである、Validatorについて見てみます。

■FriendValidator

package validator;
import java.util.ArrayList;
import java.util.List;
import form.FriendForm;
public class FriendValidator {
public List<String> validate(FriendForm friendForm) {
List<String> errors = new ArrayList<String>();
String name = friendForm.getName();
if (name == null || name.trim().isEmpty()) {
errors.add("名前は必ず入力してください");
}
String age = friendForm.getAge();
if (age == null || age.trim().isEmpty()) {
errors.add("年齢は必ず入力してください");
} else {
try {
Integer.parseInt(age);
} catch (NumberFormatException e) {
errors.add("年齢に変な値を入れてませんか?");
}
}
String height = friendForm.getHeight();
if (height == null || height.trim().isEmpty()) {
errors.add("身長を入れてね");
} else {
try {
Float.parseFloat(height);
} catch (NumberFormatException e) {
errors.add("身長におかしな値を入れてませんか?");
}
}
return errors;
}
}

ここで出てくるFriendFormとはなんでしょうか。
■FriendForm

package form;
public class FriendForm {
private String name;
private String age;
private String height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
}

■Friend

package domain;
public class Friend {
private String name;
private int age;
private float height;
public Friend() {
}
public Friend(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
}

最後に、これらを表示するJSPを見てみます。

■FriendForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<title>友達入力</title>
<style type="text/css">@import url(css/main.css);</style>
</head>
<body>
<div id="global">
<c:if test="${requestScope.errors != null}">
<p id="errors">
おねがい
<ul>
<c:forEach var="error" items="${requestScope.errors}">
<li>${error}</li>
</c:forEach>
</ul>
</p>
</c:if>
<form action="friend_save.action" method="post">
<fieldset>
<legend>友達を追加します</legend>
<p>
<label for="name">おなまえ:</label>
<input type="text" id="name" name="name" tabindex="1">
</p>
<p>
<label for="age">年齢:</label>
<input type="text" id="age" name="age" tabindex="2">
</p>
<p>
<label for="height">身長:</label>
<input type="text" id="height" name="height" tabindex="3">
</p>
<p id="buttons">
<input id="reset" type="reset" tabindex="4">
<input id="submit" type="submit" tabindex="5" 
                    value="友達を追加">
</p>
</fieldset>
</form>
</div>
</body>
</html>

■FriendDetails.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>Save Product</title>
<style type="text/css">@import url(css/main.css);</style>
</head>
<body>
<div id="global">
<h4>友達を登録しました</h4>
<p>
<h5>詳細は以下のとおりです:</h5>
名前: ${friend.name}<br/>
年齢: ${friend.age}<br/>
身長: $${friend.height}
</p>
</div>
</body>
</html>

スッキリわかるサーブレット&JSP入門

スッキリわかるサーブレット&JSP入門