【Java】簡単なSpringソースコードでJSONPの有効性を確認する

### 【概要】
SpringでJSON形式の固定文言を返却するWeb APIを作成します。
作成したWeb APIをブラウザから非同期処理で呼び出し、JSONPの有効性を確認します。
確認するパターンとしては下記4通りで、一番右のWeb API呼び出し結果の列にはパターンごとの実行結果を記載しています。

| パターンNo | 同一オリジンである | JSONPを使用する | Web API呼び出し結果 |
|:----------:|:-----------------:|:------:|:------------:|
| 1          | Yes            | Yes    | 正常      |
| 2          | Yes            | No | 正常      |
| 3          | No        | Yes     | 正常      |
| 4          | No        | No | エラー      |

### 【環境】
Java11
Google Chrome

### 【構成イメージ】
ポート番号のみを変更したサーバを2台用意します。
サーバに用意されているWeb API(Javaソース)は、ほぼ同一です。
ブラウザには同一オリジンであるSame-Originサーバのhtmlを表示し、そのhtmlに用意されたボタンをクリックすることでWeb APIを呼び出します。

![構成イメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121997/665578be-75f9-5186-128e-67025799ec27.png)

### 【コード】
[GitHub](https://github.com/nobu12/jsonp-study)にアップロードしてあります。

Javaソースは、JSON形式の固定文言を返却するWeb APIです。
htmlソースは、Web APIを呼び出すための画面です。
ボタンをクリックすることにより非同期処理でWeb APIを呼び出し、呼び出し結果をダイアログで表示します。

<details><summary>ソースコード一部抜粋</summary>

javaソースはWeb APIのメイン処理を抜粋しています。

```java
    public String execute(@RequestParam(required = false) String callback, HttpServletResponse response) {
		// JSON形式の固定文言
		String json = "{ \"name\": \"suzuki\", \"age\": \"30\" }";
		
//		パターン4の回避用
//		response.setHeader("Access-Control-Allow-Origin", "http://localhost:50001");

		// JSONPを使用する場合
		if (callback != null) {
			// JSONP用
			return callback + "(" + json + ")";
		}
		return json;
    }
```

index.htmlはsameorigin-apiプロジェクトのみにあります。

```html:index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<script>
	function getJson(jsonUrl) {
		$.ajax({
			url: jsonUrl,
			dataType: 'json'
		}).done(function (json) {
			alert(JSON.stringify(json));
		}).fail(function () {
			alert('getJson failed');
		});
	}
	
	function getJsonp(jsonUrl) {
		$.ajax({
			url: jsonUrl,
			dataType: 'jsonp',
			jsonpCallback: 'test',
		}).done(function (json) {
			alert(JSON.stringify(json));
		}).fail(function () {
			alert('getJsonp failed');
		});
	}
</script>
</head>
<body>
<button onclick="getJsonp('http://localhost:50001/sameorigin/api?callback=test')">パターン1 Same-Origin かつ JSONP使用</button>
<br><br>
<button onclick="getJson('http://localhost:50001/sameorigin/api')">パターン2 Same-Origin かつ JSONP不使用</button>
<br><br>
<button onclick="getJsonp('http://localhost:50002/crossorigin/api?callback=test')">パターン3 Cross-Origin かつ JSONP使用</button>
<br><br>
<button onclick="getJson('http://localhost:50002/crossorigin/api')">パターン4 Cross-Origin かつ JSONP不使用</button>
</body>
</html>
```

</details>

### 【実行準備】
Eclipseにプロジェクトをmavenプロジェクトとしてインポートし、pom.xmlをインストール実行します。
その後、それぞれのプロジェクトをSpring Bootアプリケーションで起動し、以下のURLへアクセスします。
http://localhost:50001
![実行準備.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121997/18df418d-da48-04c2-af0e-788dcf4b4361.png)

### 【実行結果】
パターン1～4までのボタンをクリックした時の実行結果は下記通りとなります。
パターン1～3までの実行結果はいずれも同じです。

#### パターン1～3
Web API呼び出し結果は正常のため、JSON形式の固定文言が出力されます。
![パターン1～3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121997/bc302b04-cb41-c3db-4aff-3fe134dee23d.png)

#### パターン4
Web API呼び出し結果はエラーのため、JSON形式の固定文言が出力されずエラーメッセージが出力されます。
エラーとなった原因は同一オリジンではないサイトに対して、JSONPを使用していないためです。
![パターン4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121997/a1bf52cf-fd02-c0c1-809f-f08a364569e5.png)

開発者ツールのコンソールタブを開くと下記エラーメッセージが出力されていることが確認できます。
ただ、エラーメッセージはJSONPが使われていませんという説明ではなく、レスポンスヘッダにAccess-Control-Allow-Originヘッダがありませんという説明になります。
![cross-origin_json_error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121997/575fe457-575f-b53a-0430-ace32e0a8998.png)

CORSポリシーのエラー文を抜粋したのが下記文言です。
```
Access to XMLHttpRequest at 'http://localhost:50002/crossorigin/api' from origin 'http://localhost:50001' 
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
```

このエラー文で説明された通り、JavaソースにAccess-Control-Allow-Originヘッダを追加すればエラーが解消し、パターン1～3と同じ結果となります。
```diff_java
-//		response.setHeader("Access-Control-Allow-Origin", "http://localhost:50001");
+		response.setHeader("Access-Control-Allow-Origin", "http://localhost:50001");
```