2026年5月27日水曜日

SpringBoot でレスポンスに DTO を使うサンプルコード

SpringBoot でレスポンスに DTO を使うサンプルコード

概要

前回リクエスト情報を DTO に変換して安全にデータベースの情報を取得する方法を紹介しました

今回はレスポンスでも DTO を導入してみます

環境

  • macOS 26.4.1
  • openjdk 26.0.1
  • SpringBoot 4.0.6
  • jasypt-spring-boot 4.0.4
  • gradle 9.5.1
  • VSCode 1.121.0
  • MySQL 9.6.0

レスポンス用 DTO の作成

レスポンス用の DTO には返却したい情報だけを定義します

今回はテストなので name だけ返却するようにします

  • vim src/main/java/com/example/demo/dto/UserResponse.java
package com.example.demo.dto;

import jakarta.validation.constraints.NotBlank;

public class UserResponse {

	@NotBlank
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

UserService の修正

getAllUsers を先ほど作成した UserResponse が帰るように修正します

stream を使ってクロージャっぽく変換しています

  • vim src/main/java/com/example/demo/service/UserService.java
package com.example.demo.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.dto.UserRequest;
import com.example.demo.dto.UserResponse;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;

@Service
public class UserService {

	@Autowired
	private UserRepository userRepository;

	public void createUser(UserRequest req) {

		// 例:ビジネスルール
		if (userRepository.existsByEmail(req.getEmail())) {
			throw new IllegalArgumentException("既に登録されています");
		}

		User user = new User();
		user.setName(req.getName());
		user.setEmail(req.getEmail());

		userRepository.save(user);
	}

	public List<UserResponse> getAllUsers() {
		List<User> users = new ArrayList<>();
		userRepository.findAll().forEach(users::add);

		return users.stream().map(this::toResponse).toList();
	}

	private UserResponse toResponse(User user) {
		UserResponse response = new UserResponse();
		response.setName(user.getName());
		return response;
	}
}

コントローラの修正

コントローラからは Service レイヤーのみを扱うように修正します
これでコントローラが直接 Repository を扱うことはなくなりました

  • vim src/main/java/com/example/demo/MainController.java
package com.example.demo;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.dto.UserRequest;
import com.example.demo.dto.UserResponse;
import com.example.demo.service.UserService;

import jakarta.validation.Valid;

@Controller
@RequestMapping(path = "/demo")
public class MainController {

	@Autowired
	private UserService userService;

	@PostMapping(path = "/add")
	public @ResponseBody String addNewUser(@Valid @RequestBody UserRequest req) {
		userService.createUser(req);
		return "Saved";
	}

	@GetMapping(path = "/all")
	public @ResponseBody List<UserResponse> getAllUsers() {
		return userService.getAllUsers();
	}
}

動作確認

  • ./gradlew bootRun --args='--jasypt.encryptor.password=xxx'

curl で GET し UserResponse に定義されている値だけ返ってくることを確認しましょう

  • curl -XGET http://localhost:8080/demo/all
[{"name":"First"}]

最後に

SpringBoot でレスポンスに DTO を導入してみました
レスポンスに DTO を導入するメリットとしては以下のようなものがあります

  • 予期せぬデータベース情報の返却を防ぐ
  • データベースの情報を加工してレスポンスを生成する

基本的にはレスポンスにも DTO を含めるのがベストプラクティスにはなります

0 件のコメント:

コメントを投稿