【非同期処理(Ajax)】SpringBootでJavaScript(Vanilla.JS)による非同期処理(Ajax)の実装方法

# はじめに
今回は、SpringBootによるAjaxの実装について解説します。

プライベートでのWebアプリ開発で実装した方法なので、比較的実践に近い方法だと思いますので、参考にしていただけますと幸いです。

なお、今回の実装では、**jQueryではなく素のJavaScriptを用いての実装**となります。
(全然、素のJavaScriptでの実装を書いた記事がなかった・・・)
※ 本記事の内容は、以下の前提に当てはまる方向けです

# 前提
- JavaScript中級レベル以上
- Ajaxについて基礎知識がある
- SpringBootのMVCモデルについての基礎知識がある
- SpringのRestについての基礎知識がある

# 実装方法
実装する方法は、コントローラーを`Rest`とし、非同期通信処理を行うリクエストをJavaScriptで指定し、JavaScriptでサーバーとやり取りするという流れで実装します。

そのため、メインはフロントエンド部分の実装となり、CRUDなどのビジネスロジック(Serviceクラス等)については触れませんので、ご了承ください。

## Controller

```java:ExampleRestController.java
@RestController
@RequestMapping("/")
public class ExampleRestController {

	/**サービスクラス*/
	@Autowired
	Service service;

	/**
	 * 入力処理を行う(非同期)
	 * 
	 * @param form フォーム
	 * @return 0
	 * @throws Exception 
	 */
	@PostMapping("/regist")
	public int restPostPayment(ExampleForm form) throws Exception {

		// 入力処理の呼び出し
		service.inputExample(form);

		return 0;
	}

}
```

コントローラーの実装では、非同期処理を行う際に`Rest`とするために`@RestController`アノテーションを付与するだけで実装できます。

あとは、非同期処理にしたい処理のリクエストを`@GetMapping`や`@PostMapping`等でマッピングし、非同期処理にしたいビジネスロジック等の処理を呼び出すだけです。

返り値は通常であれば、Viewを返すためStringとしますが、非同期通信では、フロント側(JavaScript側)での画面遷移の指定が必要となるので、返り値はint型の0としています。

## HTML

```html:example.html
<form th:action="@{/regist}" th:object="${form}" method="post" id="form">
  <input type="text" th:field="*{text}">
  <div id="textInvalid" style="color: red"></div>
  <input type="submit" id="submit">
</form>
```

HTMLではformタグ等にid属性を付与しているのが特徴的かと思います。
後でJavaScriptで使用するためにid属性を付与しています。

## JavaScript

```JavaScript:example.js
'use strict';

{

	/** 入力処理を非同期で行う処理 */
	function submit() {

		const btn = document.getElementById('submit');
		const form = document.getElementById('form');

		// 送信ボタンを押したときに非同期通信を行う
		btn.addEventListener('click', e => {
			// フォームのデータをFormDataに渡す
			const formData = new FormData(form);
			// XMLHttpRequestを取得
			const XHR = new XMLHttpRequest();

			// 非同期通信を行うリクエストのURLを指定
			XHR.open('POST', '/regist', true);
			// 非同期通信のレスポンスを指定
			XHR.responseType = 'json';
			// サーバーにフォームのデータを送信(POST)する
			XHR.send(formData);

			// 非同期通信時に行う処理(コールバック関数)
			XHR.onload = () => {
				// HTTPステータスコードが200以外の場合
				if (XHR.status != 200) {
					// エラーメッセージを表示する
					alert(`Error ${XHR.status}: ${XHR.statusText}`);
					// バリデーションチェックを行う
					checkValidation();
					return null;
				};

				// HTTPステータスコードが200の場合
				// フォームの値をリセット(デフォルト)値にする
                // 画面遷移する場合は以下の処理を不要
				resetField();
				// バリデーションメッセージを非表示にする
                // 画面遷移する場合は以下の処理は不要
				validationReset();
               
               　// 画面遷移する場合以下を追加する
               window.location.href = '/遷移したい画面のURL';
           }

			// イベントの初期化(二重でデータを送信することを防ぐ)
			e.preventDefault();
		});
	}

    /** バリデーションチェックを行う処理 */
	function checkValidation() {
		// バリデーションチェック対象のフィールドを取得
		const input = document.getElementById('text');
		// バリデーション表示をする要素を取得
		const textValid = document.getElementById('textInvalid');

		// バリデーション該当時にバリデーション要素を表示する
		textValid.style.display = "block";

        // チェック数が少ない場合は、if文で記述
        //　　筆者の場合、3つ以上のチェックがあったためswitch文としている
		switch (true) {
			// フィールド値が空の場合
			case input.value === '':
				paymentValid.textContent = '金額は必須入力です';
				break;

	       // 以下にバリデーションチェックを必要なだけ追加していく
		}
	}

    /** バリデーション要素を非表示にする処理 */
	function validationReset() {
		const valid = document.getElementById('textInvalid');
		valid.style.display = 'none';
	}

	/** フィールド値を初期状態にする処理 */
	function resetField() {
		// フィールドを取得(input要素)
		const input = document.getElementById('text');

		// フィールドを初期状態にする
		input.value = '';
	}
	window.addEventListener('load', submit);
} 
```

解説はほぼほぼコメントに書いたので、そちらを参照していただければと思いますが、重要な点だけ述べさせていただきます。

まず、非同期通信をする際には、非同期通信用のWebAPIが必要になります。
それが`XMLHttpRequest()`というWebAPIです。

そのAPIにあるメソッドを利用して、非同期通信を実装していくという形になります。

特に非同期通信をするリクエストを指定する`open()メソッド`がここでは重要です。
当然ですが、プログラムはリクエストを指定しないと、どれを非同期にしたらいいのか分からなくなるのでエラーを吐きます、、、

あと重要なのが`FormDataインターフェース`です。
これに非同期通信に該当する**form要素**を渡すことで、`XMLHttpRequest`のメソッドを利用してサーバーにデータを送信することができます。

どちらも公式ドキュメントに記載がありますので、公式ドキュメントを参照してみるのが良いかと思います。

なお、ここで**画面遷移を行いたい場合は**、`window.location.href = '/遷移したいURL'`をコールバック関数内に記述することで、非同期処理後に画面遷移を行うことができます。

注意としては、登録処理は非同期で行われますが、画面遷移は同期的に行われるので、「画面遷移が非同期じゃない!?エラーか?!」とならないようにしていただければと思います。


解説は以上です。
参考になれば幸いです。

# 参考文献
> [公式リファレンス ~XMLHttpRequest~](https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest)