@RequestParam や @ModelAttribute でリクエストパラメータ1つを受け取るときの挙動

## 概要
- Spring Web MVC の @RequestParam や @ModelAttribute でリクエストパラメータ1つを受け取ったときの挙動を確認するサンプルを示す

## 動作確認環境

- Java 11 (AdoptOpenJDK 11.0.6+10)
- Gradle 6.2.2
- Spring Boot 2.2.5
- Spring Web MVC 5.2.4

## 挙動確認サンプルコード DemoController.java

```java
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoController {

  public static void main(String[] args) {
    SpringApplication.run(DemoController.class, args);
  }

  @GetMapping("/request-param")
  public String foo(@RequestParam(name = "myParam") String myParam) {
    return "myParam=[" + myParam + "]\n\n";
  }

  @GetMapping("/model-attribute")
  public String bar(@ModelAttribute(name = "myParam") String myParam) {
    return "myParam=[" + myParam + "]\n\n";
  }

  @ExceptionHandler
  public String handleError(Throwable e) {
    return e.toString() + "\n\n";
  }
}
```

## Gradle で Spting Boot を起動

build.gradle ファイルを用意。

```groovy
plugins {
  id 'org.springframework.boot' version '2.2.5.RELEASE'
  id 'io.spring.dependency-management' version '1.0.9.RELEASE'
  id 'java'
}

group = 'com.example'
version = '0.0.1'
sourceCompatibility = '11'

repositories {
  mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-web'
}
```

gradle bootRun で Spring Boot を起動。

```
$ gradle bootRun

> Task :bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)
```

## curl でリクエスト

a.sh というファイルを用意。

```sh
curl http://localhost:8080/request-param
curl http://localhost:8080/request-param?myParam
curl http://localhost:8080/request-param?myParam=hello
curl http://localhost:8080/request-param?myParam=%20%20%20
curl http://localhost:8080/request-param?myParam=+++
curl http://localhost:8080/request-param?myParam=%20%20Hello%20World%20%20
curl http://localhost:8080/request-param?myParam=++Hello+World++
curl http://localhost:8080/model-attribute
curl http://localhost:8080/model-attribute?myParam
curl http://localhost:8080/model-attribute?myParam=hello
curl http://localhost:8080/model-attribute?myParam=%20%20%20
curl http://localhost:8080/model-attribute?myParam=+++
curl http://localhost:8080/model-attribute?myParam=%20%20Hello%20World%20%20
curl http://localhost:8080/model-attribute?myParam=++Hello+World++
```

sh -x で実行。

```
$ sh -x a.sh 
+ curl http://localhost:8080/request-param
org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'myParam' is not present

+ curl 'http://localhost:8080/request-param?myParam'
myParam=[]

+ curl 'http://localhost:8080/request-param?myParam=hello'
myParam=[hello]

+ curl 'http://localhost:8080/request-param?myParam=%20%20%20'
myParam=[   ]

+ curl 'http://localhost:8080/request-param?myParam=+++'
myParam=[   ]

+ curl 'http://localhost:8080/request-param?myParam=%20%20Hello%20World%20%20'
myParam=[  Hello World  ]

+ curl 'http://localhost:8080/request-param?myParam=++Hello+World++'
myParam=[  Hello World  ]

+ curl http://localhost:8080/model-attribute
myParam=[]

+ curl 'http://localhost:8080/model-attribute?myParam'
myParam=[]

+ curl 'http://localhost:8080/model-attribute?myParam=hello'
myParam=[hello]

+ curl 'http://localhost:8080/model-attribute?myParam=%20%20%20'
myParam=[]

+ curl 'http://localhost:8080/model-attribute?myParam=+++'
myParam=[]

+ curl 'http://localhost:8080/model-attribute?myParam=%20%20Hello%20World%20%20'
myParam=[  Hello World  ]

+ curl 'http://localhost:8080/model-attribute?myParam=++Hello+World++'
myParam=[  Hello World  ]
```

## 挙動

### @RequestParam(name = "myParam") の挙動

- パラメータ名が指定されていない場合: MissingServletRequestParameterException 例外が発生
- パラメータ値が指定されていない場合: 引数に空文字列がセットされる

### @ModelAttribute(name = "myParam") の挙動

- パラメータ名が指定されていない場合: 引数に空文字列がセットされる
- パラメータ値が指定されていない場合: 引数に空文字列がセットされる
- パラメータ値が半角空白文字のみで構成されている場合: 引数に空文字列がセットされる

## 参考資料

- [入門 \| Spring MVC で Web コンテンツの提供 \- コードサンプル](https://spring.pleiades.io/guides/gs/serving-web-content/)
- [RequestParam \(Spring Framework 5\.2\.4\.RELEASE API\) \- Javadoc 日本語訳](https://spring.pleiades.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html)
- [ModelAttribute \(Spring Framework 5\.2\.4\.RELEASE API\) \- Javadoc 日本語訳](https://spring.pleiades.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html)
- [ExceptionHandler \(Spring Framework 5\.2\.4\.RELEASE API\) \- Javadoc 日本語訳](https://spring.pleiades.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/web/bind/annotation/ExceptionHandler.html)
- [7.1.11. エラー処理 \- Spring Boot の機能 \- 公式ドキュメントの日本語訳](https://spring.pleiades.io/spring-boot/docs/2.2.5.RELEASE/reference/html/spring-boot-features.html#boot-features-error-handling)
- [技術/HTTP/URLエンコードで 0x20\(スペース\) を "\+" にすべきか "%20" にすべきか \- Glamenv\-Septzen\.net](https://www.glamenv-septzen.net/view/1170)