Springのリクエストとレスポンス

📢 この記事は gemini-2.5-flash によって翻訳されました

ブラウザがリクエストを送ると、まずDispatcherServletを経由して各*Controllerプログラムに処理が渡されるんだ。処理が終わったら、DispatcherServletに結果が返されて、そこからブラウザに戻る流れだよ。

DispatcherServletは、コアコントローラとかフロントコントローラって呼ばれるね。

ブラウザからのリクエストデータはHttpServletRequestオブジェクトに、レスポンスデータはHttpServletResponseにそれぞれカプセル化されるんだ。

*Controllerはリクエストを受け取って、レスポンスを設定するんだ。

リクエスト

シンプルなパラメータ

まず、昔ながらの方法だと、リクエストパラメータはHttpServletRequestオブジェクトを使って手動で取得するよ。

1
2
3
4
5
6
7
8
9
@RequestMapping("/simpleParam")
public String simpleParam(HttpServletRequest request){
    String name = request.getParameter("name");
    String ageStr = request.getParameter("age");

    int age = Integer.parseInt(ageStr);
    System.out.println(name + ";" + age);
    return "OK";
}

ブラウザで http://localhost:8080/simpleParam?name=tom&age=18 にアクセスすると、ウェブページには「OK」って文字列が返ってきて、プログラムでは「Tom;18」って表示されるよ。


当然、このやり方だと面倒すぎるよね。SpringBootなら、パラメータ名と仮引数名を同じにするだけで、仮引数でパラメータを受け取れるんだ。

1
2
3
4
5
@RequestMapping("/simpleParam")
public String simpleParam(String name, Integer age){
    System.out.println(name + ";" + age);
    return "OK";
}

結果は上と同じだよ。


ただ、上の方法だと、リクエストのパラメータ名が違ってもそのまま実行されて、存在しないパラメータはnullになっちゃうんだ。

@RequestParamを使えば、リクエスト名と仮引数名が違う場合でもマッピングできるようになるよ。

1
2
3
4
5
6
7
@RequestMapping("/simpleParam")
public  String simpleParam(@RequestParam(name = "name") String username, Integer age){
    // @RequestParamを宣言すると、そのパラメータは必須になるから、渡さないとエラーになっちゃう
    // もし@RequestParam(name = "name", required = false)に変えれば、そのパラメータは必須じゃなくなるよ
    System.out.println(username + ";" + age);
    return "OK";
}

エンティティパラメータ

パラメータが少ないなら上の方法でいいんだけど、データが多い場合はシンプルなエンティティオブジェクトを使えるよ。リクエストパラメータ名と仮引数オブジェクトのプロパティ名を同じにして、POJOを定義すれば受け取れるんだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@RequestMapping("/simplePojo")
public String simplePojo(User user){
    System.out.println(user);
    return "OK";
}

// Userクラス
public class User{
    private String name;
    private Integer age;
    // set/get/toString メソッド
}

ブラウザで http://localhost:8080/simplePojo?name=tom&age=18 にアクセスすると、ウェブページには「OK」って文字列が返ってきて、プログラムでは「User{name=‘tom’, age=18}」って表示されるよ。


複雑なエンティティオブジェクトの場合:リクエストパラメータ名と仮引数プロパティ名を同じにして、オブジェクトの階層構造に合わせてネストされたPOJOプロパティパラメータを受け取れるんだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@RequestMapping("/complexPojo")
public String complexPojo(UserComplex user){
    System.out.println(user);
    return "OK";
}

// Userクラス
public class UserComplex{
    private String name;
    private Integer age;
    private Address address;
    // set/get/toString
}

// Addressクラス
public class Address{
    private String province;
    private String city;
    // set/get/toString
}

ブラウザで http://localhost:8080/complexPojo?name=tom&age=18&address.province=beijing&address.city=beijing にアクセスすると、ウェブページには「OK」って文字列が返ってきて、プログラムでは「UserComplex{name=‘tom’, age=1000, address=Address{province=‘ads,beijing’, city=‘asda,beijing’}}」って表示されるよ。

配列パラメータ

リクエストパラメータ名と仮引数配列名が同じで、リクエストパラメータが複数ある場合、配列型の仮引数を定義するだけでパラメータを受け取れるよ。例えば、チェックボックスなんかの場合だね。

1
2
3
4
5
@RequestMapping("/arrayParam")
public String arrayParam(String[] hobby){
    System.out.println(Arrays.toString(hobby));
    return "OK";
}

ブラウザで http://localhost:8080/arrayParam?hobby=ads&hobby=58s にアクセスすると、ウェブページには「OK」って文字列が返ってきて、プログラムでは「[ads, 58s]」って表示されるよ。

コレクションパラメータ

リクエストパラメータ名と仮引数配列名が同じで、リクエストパラメータが複数ある場合、@RequestParamを使ってパラメータの関係をバインドするんだ。

1
2
3
4
5
@RequestMapping("/listParam")
public String listParam(@RequestParam List<String> hobby){
    System.out.println(hobby);
    return "OK";
}

ブラウザで http://localhost:8080/listParam?hobby=ads&hobby=58s にアクセスすると、ウェブページには「OK」って文字列が返ってきて、プログラムでは「[ads, 58s]」って表示されるよ。

日付パラメータ

@DateTimeFormatアノテーションを使って、日付パラメータのフォーマット変換を行うんだ。

注意点として、パラメータの渡し方には3つの形式があるよ。

  • yyyy-MM-dd HH:mm:ss
  • yyyy/MM/dd HH:mm:ss
  • yyyy年MM月dd日 HH時mm分ss秒

フロントエンドとバックエンドで統一する必要があるよ。

1
2
3
4
5
@RequestMapping("/dateParam")
public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime localDateTime){
    System.out.println(localDateTime);
    return "OK";
}

ブラウザで http://localhost:8080/dateParam?localDateTime=2024-01-01%2001:01:01 にアクセスすると、ウェブページには「OK」って文字列が返ってきて、プログラムでは「2024-01-01T01:01:01」って表示されるよ。

JSONパラメータ

JSONデータのキー名と仮引数オブジェクトのプロパティ名を同じにして、POJO型の仮引数を定義すればパラメータを受け取れるよ。@RequestBodyでマークする必要があるんだ。

JSONデータを送信したい場合は、必ずPOSTリクエストを使う必要があるよ。

1
2
3
4
5
@RequestMapping("/jsonParam")
public String jsonParam(@RequestBody UserComplex userComplex){
    System.out.println(userComplex);
    return "OK";
}

リクエストデータ

1
2
3
4
5
6
7
8
{
    "name": "jane",
    "age": "18",
    "address": {
        "province": "上海",
        "city": "上海"
    }
}

上記のJSONデータをPOSTメソッドで http://localhost:8080/jsonParam にリクエストすると、ウェブページには「OK」って文字列が返ってきて、プログラムでは「UserComplex{name=‘jane’, age=18, address=Address{province=‘上海’, city=‘上海’}}」って表示されるよ。

パスパラメータ

リクエストURLで直接パラメータを渡す方法だよ。{...}を使ってそのパスパラメータを識別して、@PathVariableで取得する必要があるんだ。

1
2
3
4
5
@RequestMapping("/path/{id}")
public String pathParam(@PathVariable Integer id){
    System.out.println(id);
    return "OK";
}

ブラウザで http://localhost:8080/path/52 にアクセスすると、ウェブページには「OK」って文字列が返ってきて、プログラムでは「52」って表示されるよ。

ブラウザで http://localhost:8080/path/255 にアクセスすると、ウェブページには「OK」って文字列が返ってきて、プログラムでは「255」って表示されるよ。


複数のパスパラメータの場合だよ。

1
2
3
4
5
@RequestMapping("/path/{id}/{name}")
public String pathParam(@PathVariable Integer id, @PathVariable String name){
    System.out.println(id + ";" + name);
    return "OK";
}

ブラウザで http://localhost:8080/path/255/Tom にアクセスすると、ウェブページには「OK」って文字列が返ってきて、プログラムでは「255;Tom」って表示されるよ。

まとめ

  1. シンプルなパラメータ
    • メソッドの仮引数を定義して、リクエストパラメータ名と仮引数変数を一致させるんだ。
    • もし一致しない場合は、@RequestParamを使って手動でマッピングするよ。
  2. エンティティパラメータ
    • リクエストパラメータ名をエンティティオブジェクトのプロパティ名と一致させると、自動的に受け取ってカプセル化してくれるんだ。
  3. 配列・コレクションパラメータ
    • 配列:リクエストパラメータ名と配列名を一致させると、直接カプセル化されるよ。
    • コレクション:リクエストパラメータ名と配列名を一致させて、@RequestParamで関係をバインドするんだ。
  4. 日付パラメータ
    • @DateTimeFormat
  5. JSONパラメータ
    • @RequestBody
  6. パスパラメータ
    • @PathVariable

レスポンス

@RequestBodyを使ってレスポンスを処理するよ。

上のリクエストでレスポンスできたのは、@RestControllerアノテーションが@Controller@ResponseBodyを含んでいるからなんだ。

@RequestBody

タイプ:メソッドアノテーション、クラスアノテーション

場所:Controllerメソッド上またはクラス上

作用:メソッドの戻り値を直接レスポンスするよ。もし戻り値の型がエンティティオブジェクトやコレクションだったら、JSON形式に変換してレスポンスされるんだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@RestController
public class ResponseController {
    @RequestMapping("/hello")
    public String hello(){
        System.out.println("Hello SpringBoot");
        return "Hello SpringBoot";
    }

    @RequestMapping("/getAddr")
    public Address getAddr(){
        Address address = new Address();
        address.setProvince("上海");
        address.setCity("上海");
        return address;
    }

    @RequestMapping("/listAddr")
    public List<Address> listAddr(){
        List<Address> list = new ArrayList<>();
        Address address1 = new Address();
        address1.setProvince("北京");
        address1.setCity("北京");
        Address address2 = new Address();
        address2.setProvince("上海");
        address2.setCity("上海");

        list.add(address1);
        list.add(address2);
        return list;
    }
}

対応するURLにアクセスしてテストしてみてね。


統一されたレスポンス結果

上の3つのメソッドは戻り値の型が全部違うから、フロントエンド開発がちょっと大変だよね。だから、レスポンス結果を一つのクラスに統一できるんだ。

1
2
3
4
5
6
7
8
9
public class Result{
    // レスポンスコード、1は成功、0は失敗を表すよ
    private Integer code;
    // メッセージ
    private String msg;
    // 返されるデータ
    private Object data;
    // ...
}

すべてのレスポンスメソッドがResultオブジェクトを返すようにすれば、プロジェクトの管理と保守がもっと楽になるんだ。

例えば、こんなクラスだよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class Result {
    private Integer code ;//1 成功 , 0 失敗
    private String msg; //メッセージ
    private Object data; //データ data

    public Result() {
    }
    public Result(Integer code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }

    public static Result success(Object data){
        return new Result(1, "success", data);
    }
    public static Result success(){
        return new Result(1, "success", null);
    }
    public static Result error(String msg){
        return new Result(0, msg, null);
    }

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}

そうすると、上のコードはこう変えられるよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@RequestMapping("/hello")
public Result hello(){
    System.out.println("Hello SpringBoot");
    return Result.success("Hello SpringBoot");
}

@RequestMapping("/getAddr")
public Result getAddr(){
    Address address = new Address();
    address.setProvince("上海");
    address.setCity("上海");
    return Result.success(address);
}

@RequestMapping("/listAddr")
public Result listAddr(){
    List<Address> list = new ArrayList<>();
    Address address1 = new Address();
    address1.setProvince("北京");
    address1.setCity("北京");
    Address address2 = new Address();
    address2.setProvince("上海");
    address2.setCity("上海");

    list.add(address1);
    list.add(address2);
    return Result.success(list);
}

対応するURLにアクセスしてテストしてみてね。

Visits Since 2025-02-28

Hugo で構築されています。 | テーマ StackJimmy によって設計されています。