Web 開発再入門 #10 ― コントローラー処理（Web API もどき）

# Web 開発再入門 #10 ― コントローラー処理（Web API もどき）

fmockup

## はじめに

Web アプリケーションのサーバー・サイドを開発します。
MVC の Controller 部分となります。

MVC：Model View Controller

## 画面ショット

ログイン画面で認証 Web API を呼び出したとき、Web API の Controller のバリデーションでエラーを出しています。

![Java-fmockup_14-loginValidationError.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3529613/e1c7d43a-be0e-1b88-46b6-33ce90313f0e.jpeg)

## フォルダー・ファイル構成

D:\\
└ Development\\
&nbsp;&nbsp;&nbsp;&nbsp;└ Workspace\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ fmockup\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ build\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ sql\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ src\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;└ main\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ java\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;└ cn\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ com\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ xxxx\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ fmockup\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ action\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;└ SessionAuthAction.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ controller\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ customizer\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ entity\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ mapper\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ response\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;└ StatusResponse.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ service\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ util\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ validator\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;└ SessionAuthValidator.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ validator_order\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ ValidatorOrder.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ ValidatorOrder1.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ ValidatorOrder2.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ ValidatorOrder3.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ resources\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ vue-vite\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ WinSW.NET-nnn\\

## Web API もどき

GET リクエストと POST リクエストのみを実装します。なので、ここでは “Web API もどき” と呼んでいます。
簡単にするために、以下を実装していません。
* PUT、DELETE リクエストは使用していません。
* リクエストにおいて JSON 渡しを使用していません。

## ファイルの文字コード

基本的に Eclipse でファイルを作成するので、あまり意識したことがありません。
多分、Unix 改行（LF）なのだと思います。

## ファイルの作成

1. Eclipse で、それぞれのソース・コード・フォルダーにて、[ファイル (F)] - [新規 (N)] - [クラス] で、“SessionAuthAction.java”、“SessionAuthValidator.java”、“GroupOrder.java”、“GroupOrder1.java”、“GroupOrder2.java”、“GroupOrder3.java”、“StatusResponse.java” を作成する。
    ```java:SessionAuthAction.java
    ・・・
    
    package cn.com.xxxx.fmockup.action;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.ObjectError;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    
    import cn.com.xxxx.fmockup.entity.SessionAuthEntity;
    import cn.com.xxxx.fmockup.response.StatusResponse;
    import cn.com.xxxx.fmockup.service.SessionAuthService;
    import cn.com.xxxx.fmockup.util.LogUtil;
    import cn.com.xxxx.fmockup.util.MessageUtil;
    import cn.com.xxxx.fmockup.util.PropertyUtil;
    import cn.com.xxxx.fmockup.util.SessionUtil;
    import cn.com.xxxx.fmockup.validator.SessionAuthValidator;
    import cn.com.xxxx.fmockup.validator_order.GroupOrder;
    import jakarta.servlet.http.HttpSession;
    
    /**
     * Session Auth Action
     *   It processes the Web-API POST request from browser.
     */
    
    @RestController
    public class SessionAuthAction {
    
    	@Autowired
    	private HttpSession session;
    
    	@Autowired
    	private SessionAuthService service;
    
    	/**
    	 * Session Auth Action
    	 *   It processes the Web-API POST request from browser.
    	 */
    	@PostMapping("/session/auth")
    	@ResponseBody
    	public StatusResponse response(@Validated(GroupOrder.class) @ModelAttribute SessionAuthValidator validator, BindingResult result) throws Exception {
        // ★★★ ↑ SessionAuthValidator.java ★★★
    
    		MessageUtil.setLocale();
    
    		LogUtil.access("Login started (" + Thread.currentThread().getStackTrace()[1].getClassName() + ")");
    		StatusResponse response = new StatusResponse();
    
    		// Drop Session
    		session.invalidate();
    
    		// Check Bean Validator
    		if (result.hasErrors()) {
    			LogUtil.access("Login faild : '" + validator.getUserName() + "'");
    			response.setStatus("action-ng");
    			response.setMessage(MessageUtil.getMessage(MessageUtil.COMMON_ERROR_TITLE_PLEASE_MODIFY_VALUE));
    			for (ObjectError error : result.getAllErrors()) {
    				String[] keyValue = error.getDefaultMessage().split(":", 2);
    				if (keyValue[0].equals("ambiguous")) {
    					response.setMessage(keyValue[1]);
    				} else {
    					response.addMessageMap(keyValue[0], keyValue[1]);
    				}
    			}
    			return response;
                // ★★★ ↑ StatusResponse.java ★★★
    		}
    
    		// Check User Exists or Not
    		SessionAuthEntity entity;
    		try {
    			entity = service.searchOneUser(validator);
                // ★★★ ↑ SessionAuthService ★★★
    		} catch (Exception ex) {
    			LogUtil.access("Login failed (Service) : '" + validator.getUserName() + "', '" + ex.getMessage() + "'");
    			response.setStatus("action-ng");
    			response.setMessage(ex.getMessage());
    			return response;
                // ★★★ ↑ StatusResponse.java ★★★
    		}
    
    		// Set Session Values
    		SessionUtil sessionUtil = new SessionUtil(session, entity.getUserId(), validator.getUserName());
    
    		// Check Account Password Is Expired
    		if (entity.getDurationDays() > PropertyUtil.getLoginExpireDay()) {
    			LogUtil.access("Login succeed (expired) : '" + sessionUtil.getUserName() + "'");
    			response.setStatus("action-ok-expired");    // Go to Change Password Screen
    			response.setMessage("");
    			return response;
                // ★★★ ↑ StatusResponse.java ★★★
    		}
    
    		// Check Account Is Locked or Not
    		if (entity.getUserLockedF().equals("Y")) {
    			LogUtil.access("Login succeed (locked) : '" + sessionUtil.getUserName() + "'");
    			response.setStatus("action-ok-expired");    // Go to Change Password Screen
    			response.setMessage("");
    			return response;
                // ★★★ ↑ StatusResponse.java ★★★
    		}
    
    		// Set Response Values
    		sessionUtil.setLoggedIn();
    		LogUtil.access("Login succeed : '" + sessionUtil.getUserName() + "'");
    		response.setStatus("action-ok");
    		response.setMessage("");
    		return response;
            // ★★★ ↑ StatusResponse.java ★★★
    	}
    }
    ```
    説明：
    POST リクエスト（ POST http://localhost:8080/session/auth ）を受け付けて、リクエストデータをチェックして、メイン処理を行って、レスポンスデータ（JSON）を返却するまでの処理を行います。
    ```java:SessionAuthValidator.java
    ・・・
    
    package cn.com.xxxx.fmockup.validator;
    
    import cn.com.xxxx.fmockup.validator_order.GroupOrder1;
    import cn.com.xxxx.fmockup.validator_order.GroupOrder2;
    import cn.com.xxxx.fmockup.validator_order.GroupOrder3;
    import jakarta.validation.constraints.NotEmpty;
    import jakarta.validation.constraints.Pattern;
    import jakarta.validation.constraints.Size;
    import lombok.Data;
    
    /*
     * Notice:
     * Do not write detail message in Login Screen.
     * (Need to write ambiguous message in Login Screen)
     */
    
    /**
     * Session Authorization Validator
     *   It checks values from browser.
     */
    
    @Data
    public class SessionAuthValidator {
    
    	// Screen Input Values
    
    	@NotEmpty(message = "userName:{LOGIN_ERROR_FORM_NO_USER_ID_IS_SPECIFIED}", groups = GroupOrder1.class)
    	@Size(max = 32, message = "ambiguous:{LOGIN_ERROR_TITLE_USER_ID_OR_PASSWORD_IS_NOT_CORRECT}", groups = GroupOrder2.class)
    	@Pattern(regexp = "[a-zA-Z0-9\\_\\-]*", message = "ambiguous:{LOGIN_ERROR_TITLE_USER_ID_OR_PASSWORD_IS_NOT_CORRECT}", groups = GroupOrder3.class)
    	private String userName;
    
    	// Password is checked at SessionAuthAction
    	@NotEmpty(message = "password:{LOGIN_ERROR_FORM_NO_PASSWORD_IS_SPECIFIED}", groups = GroupOrder1.class)
    //	@Size(min = 8, max = 32, message = "ambiguous:{LOGIN_ERROR_TITLE_USER_ID_OR_PASSWORD_IS_NOT_CORRECT}", groups = GroupOrder2.class)
    //	@Pattern(regexp = "[a-zA-Z0-9\\!\\\"\\#\\$\\%\\&\\'\\(\\)\\-\\=\\^\\~\\\\\\|\\@\\`\\[\\{\\;\\+\\:\\*\\]\\}\\,\\<\\.\\>\\/\\?\\_]*", message = "ambiguous:{LOGIN_ERROR_TITLE_USER_ID_OR_PASSWORD_IS_NOT_CORRECT}", groups = GroupOrder3.class)
    	private String password;
    }
    ```
    説明：
    リクエストデータの値チェックを行います。またデータベースに検索条件を指定する値にもなります。
    ```java:GroupOrder.java
    ・・・
    
    package cn.com.xxxx.fmockup.validator_order;
    
    import jakarta.validation.GroupSequence;
    
    @GroupSequence({ GroupOrder1.class, GroupOrder2.class, GroupOrder3.class })
    public interface GroupOrder {
    }
    ```
    ```java:GroupOrder1.java
    ・・・
    
    package cn.com.xxxx.fmockup.validator_order;
    
    public interface GroupOrder1 {
    }
    ```
    ```java:GroupOrder2.java
    ・・・
    
    package cn.com.xxxx.fmockup.validator_order;
    
    public interface GroupOrder2 {
    }
    ```
    ```java:GroupOrder3.java
    ・・・
    
    package cn.com.xxxx.fmockup.validator_order;
    
    public interface GroupOrder3 {
    }
    ```
    説明：
    上記の 4つは、リクエストデータのチェック順を定義するためのものです。    
    ```java:SessionAuthService.java
    ・・・
    
    package cn.com.xxxx.fmockup.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import cn.com.xxxx.fmockup.entity.SessionAuthEntity;
    import cn.com.xxxx.fmockup.mapper.SessionAuthMapper;
    import cn.com.xxxx.fmockup.util.CryptoUtil;
    import cn.com.xxxx.fmockup.util.LogUtil;
    import cn.com.xxxx.fmockup.util.MessageUtil;
    import cn.com.xxxx.fmockup.util.PropertyUtil;
    import cn.com.xxxx.fmockup.validator.SessionAuthValidator;
    
    /**
     * Session Auth Service
     *   It processes login data.
     */
    
    @Service
    public class SessionAuthService {
    
    	@Autowired
    	private SessionAuthMapper mapper;
    
    	/**
    	 * Session Auth Service
    	 *   It processes login data.
    	 */
    	public SessionAuthEntity searchOneUser(SessionAuthValidator validator) throws Exception {
    
    		//
    		// If an error occurs in this process, it must output a blurred explanation in error message.
    		//
    
    		// Get Values from Database
    		SessionAuthEntity entity;
    		try {
    			entity = mapper.searchOneUser(validator);
    		} catch (Exception ex) {
    			LogUtil.info("searchOneUser : '" + ex.getMessage() + "'");
    			throw new Exception(MessageUtil.COMMON_ERROR_STRING_UNKNOWN_TROUBLE_HAS_OCCURRED);
    		}
    		if (entity == null) {
    			LogUtil.info("searchOneUser : 'User does not exists.'");															// A true problem explanation
    			throw new Exception(MessageUtil.getMessage(MessageUtil.LOGIN_ERROR_TITLE_USER_ID_OR_PASSWORD_IS_NOT_CORRECT));		// A blurred explanation
    		}
    
    		// Check Retry Count
    		if (entity.getFailedCount() >= PropertyUtil.getLoginRetryCount()) {
    			LogUtil.info("searchOneUser : 'Retry Count is overed by maximum count.'");											// A true problem explanation
    			throw new Exception(MessageUtil.getMessage(MessageUtil.LOGIN_ERROR_TITLE_USER_ID_OR_PASSWORD_IS_NOT_CORRECT));		// A blurred explanation
    		}
    
    		// Check Match Password
    		String password;
    		try {
    			password = CryptoUtil.decrypt(entity.getPasswordAes());
                // ★★★ ↑ CryptoUtil.java ★★★
    		} catch (Exception ex) {
    			LogUtil.info("searchOneUser : '" + ex.getMessage() + "'");		// A true problem explanation
    			throw new Exception(MessageUtil.getMessage(MessageUtil.LOGIN_ERROR_TITLE_USER_ID_OR_PASSWORD_IS_NOT_CORRECT));	// A blurred explanation
    		}
    		if (! password.equals(validator.getPassword())) {
    
    			try {
    				mapper.updateUserFailedCount(entity.getUserId());
    			} catch (Exception ex) {
    				LogUtil.info("searchOneUser : '" + ex.getMessage() + "'");
    				throw new Exception(MessageUtil.COMMON_ERROR_STRING_UNKNOWN_TROUBLE_HAS_OCCURRED);
    			}
    
    			LogUtil.info("searchOneUser : 'Password is not correct.'");														// A true problem explanation
    			throw new Exception(MessageUtil.getMessage(MessageUtil.LOGIN_ERROR_TITLE_USER_ID_OR_PASSWORD_IS_NOT_CORRECT));	// A blurred explanation
    		}
    
    		//
    		// Because authorization process is already ended,
    		// so if an error occurs in this process, it can output a true problem explanation in error message.
    		//
    
    		// Set Values to Database
    		int count;
    		try {
    			count = mapper.resetUserFailedCount(entity.getUserId());
    		} catch (Exception ex) {
    			LogUtil.info("searchOneUser : '" + ex.getMessage() + "'");
    			throw new Exception(MessageUtil.COMMON_ERROR_STRING_UNKNOWN_TROUBLE_HAS_OCCURRED);
    		}
    		if (count == 0) {
    			LogUtil.info("searchOneUser : 'This Account does not exist.'");
    			throw new Exception(MessageUtil.getMessage(MessageUtil.LOGIN_ERROR_TITLE_THIS_ACCOUNT_DOES_NOT_EXIST)); 
    		} else if (count > 1) {
    			LogUtil.info("searchOneUser : 'Assertion : Invalid count of manipulate data.'");
    			throw new Exception(MessageUtil.COMMON_ERROR_STRING_UNKNOWN_TROUBLE_HAS_OCCURRED);
    		}
    
    		return entity;
    	}
    
    	public void searchOneUserForSession(String userId, String tranName) throws Exception {
    
    		//
    		// Because authorization process is already ended,
    		// so if an error occurs in this process, it can output a true problem explanation in error message.
    		//
    
    		// Get Values from Database
    		SessionAuthEntity entity;
    		try {
    			entity = mapper.searchOneUserForSession(userId);
    		} catch (Exception ex) {
    			LogUtil.info("searchOneUserForSession : '" + ex.getMessage() + "'");
    			throw new Exception(MessageUtil.COMMON_ERROR_STRING_UNKNOWN_TROUBLE_HAS_OCCURRED);
    		}
    		if (entity == null) {
    			LogUtil.info("searchOneUserForSession : 'Yout Account does not exist.'");
    			throw new Exception(MessageUtil.getMessage(MessageUtil.USER_ERROR_TITLE_THIS_USER_DOES_NOT_EXIST));
    		}
    
    		// Check Account Is Locked or Not
    		if (entity.getUserLockedF().equals("Y")) {
    			LogUtil.info("searchOneUserForSession : 'Your Account was locked. Please logout, and try to login again.'");
    			throw new Exception(MessageUtil.getMessage(MessageUtil.ACCOUNT_ERROR_TITLE_YOUR_ACCOUNT_WAS_LOCKED_PLEASE_LOGOUT_AND_TRY_TO_LOGIN_AGAIN));
    		}
    
    		// Check Access Control
    		int count;
    		try {
    			count = mapper.searchOneUserTranCount(userId, tranName);
    		} catch (Exception ex) {
    			LogUtil.info("searchOneUserForSession : '" + ex.getMessage() + "'");
    			throw new Exception(MessageUtil.COMMON_ERROR_STRING_UNKNOWN_TROUBLE_HAS_OCCURRED);
    		}
    		if (count == 0) {
    			LogUtil.info("searchOneUserForSession : 'Your Account access is restricted.'");
    			throw new Exception(MessageUtil.getMessage(MessageUtil.ACCOUNT_ERROR_TITLE_YOUR_ACCOUNT_IS_RESTRICTED));
    		}
    
    		return;
    	}
    }
    ```
    説明：
    メインの処理を行います。データベースをアクセスしたり、エラーを発行したり。
    ```java:CryptoUtil.java
    ・・・
    
    package cn.com.xxxx.fmockup.util;
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;
    
    /**
     * Cripto Utility
     *   It encrypts or decrypts data.
     */
    
    public class CryptoUtil {
    
    	private static String charset = "utf-8";
    
    	/**
    	 * Cripto Utility
    	 *   It encrypts data.
    	 */
    	public static String encrypt(String inString, int charLength) throws Exception {
    
    		// Feature TODO : Change from "AES" to "AES/GCM/NoPadding".
    
    		String outString;
    		try {
    			SecretKeySpec key = new SecretKeySpec(PropertyUtil.getCryptoKey().getBytes(charset), "AES");
    			Cipher cipher = Cipher.getInstance("AES");
    			cipher.init(Cipher.ENCRYPT_MODE, key);
    			byte[] in = inString.getBytes(charset);
    			byte[] out = cipher.doFinal(in);
    			outString = new String(bin2hex(out));
    		} catch (Exception e) {
    			throw new Exception("Eecrypt trouble has occurred.");
    		}
    
    		int length = 16 * ( ( charLength * 6 / 16 ) + 1 ) * 2;
    
    		// Assertion
    		if (outString.length() > length) {
    			throw new Exception("Assertion : Invalid encrypt length (" + String.valueOf(outString.length()) + " > " + String.valueOf(length) + ")");
    		}
    
    		return outString;
    	}
    
    	/**
    	 * Cripto Utility
    	 *   It decrypts data.
    	 */
    	public static String decrypt(String inString) throws Exception {
    
    		String outString;
    		try {
    			SecretKeySpec key = new SecretKeySpec(PropertyUtil.getCryptoKey().getBytes(charset), "AES");
    			Cipher cipher = Cipher.getInstance("AES");
    			cipher.init(Cipher.DECRYPT_MODE, key);
    			byte[] in = hex2bin(inString);
    			byte[] out = cipher.doFinal(in);
    			outString = new String(out, charset);
    		} catch (Exception ex) {
    				throw new Exception("Decrypt trouble has occurred.");
    		}
    
    		return outString;
    	}
    
    	// bin to hex
    	private static String bin2hex(byte[] data) {
    		StringBuffer sb = new StringBuffer();
    		for (byte b : data) {
    			String s = Integer.toHexString(0xff & b);
    			if (s.length() == 1) {
    				sb.append("0");
    			}
    			sb.append(s);
    		}
    	    return sb.toString();
    	}
    
    	// hex to bin
    	private static byte[] hex2bin(String hex) {
    		byte[] bytes = new byte[hex.length() / 2];
    		for (int index = 0; index < bytes.length; index++) {
    			bytes[index] = (byte) Integer.parseInt(hex.substring(index * 2, (index + 1) * 2), 16);
    		}
    		return bytes;
    	}
    }
    ```
    説明：
    パスワードのデクリプト（復号化）やエンクリプト（暗号化）の処理を行います。ログイン時においては、データベースの暗号化されたパスワードをデクリプト（復号化）して、ログイン画面のパスワードと一致するか否かを確認します。
    ```java:StatusResponse.java
    ・・・
    
    package cn.com.xxxx.fmockup.response;
    
    import java.util.HashMap;
    import java.util.Map;
    
    // JSON format:
    //
    // {
    //   "status"     : "status-string",
    //   "message"    : "message-string",
    //   "messageMap" : {
    //       "key"    : "value",
    //       ...
    //   }
    // }
    
    /**
     * JSON Response Structure
     *   All Web-API return this JSON.
     */
    
    public class StatusResponse {
    
    	private String status;
    	private String message;
    	private Map<String, String> messageMap = new HashMap<>();
    
    	public void setStatus(String status) {
    		this.status = status;
    	}
    
    	public String getStatus() {
    		return this.status;
    	}
    
    	public void setMessage(String message) {
    		this.message = message;
    	}
    
    	public String getMessage() {
    		return this.message;
    	}
    
    	public void addMessageMap(String key, String message) {
    		this.messageMap.put(key, message);
    	}
    
    	public Map<String, String> getMessageMap() {
    		return this.messageMap;
    	}
    }
    ```
    説明：
    レスポンスデータ（JSON）を返却するためのものです。

## 参考

**GET と POST**

https://qiita.com/kanataxa/items/522efb74421255f0e0a1

**Controller**

https://qiita.com/TEBASAKI/items/267c261db17f178e33eb	

**Validation**

https://blog.ohgaki.net/there-are-3-types-of-validations

**Controller、Service、Model の使い分け**

https://qiita.com/os1ma/items/66fb47f229896b32b2e8