😺

Re:ゼロから始めるSpring Boot実践#1 WebBlog開発#1 ユーザ作成

に公開

機能一覧※再掲

  1. ユーザ
    1.1 新規登録 ← 本記事の内容
    1.2 ログイン
    1.3 ユーザ情報取得
    1.4ユーザ情報更新
    1.5 アバター画像変更
    1.6 パスワード変更
  2. 記事カテゴリ
  3. 記事管理

API設計

1.基本情報

リクエストパス:/user/register
リクエストメソッド:post
説明:新規ユーザ登録用API

2.リクエストパラメータ

パラメータフォーマット:x-www-form-urlencoded
リクエストパラメータ説明:

パラメータ名 説明 必須か 備考
username ユーザー名 string 必須 5~16文字の非空文字列
password パスワード string 必須 5~16文字の非空文字列

リクエストデータ例:

username=zhangsan&password=123456

3.レスポンスデータ

レスポンスデータ形式:application/json
レスポンスパラメータ説明:

名称 必須か デフォルト値 説明 その他情報
code number 必須 - レスポンスコード、0-成功、1-失敗
message string 任意 - メッセージ情報 -
data object 任意 - 返却されるデータ -

レスポンスデータ例:

{
    "code": 0,
    "message": "操作成功",
    "data": null
}

API開発

1.準備作業 - Lombokの導入

エンティティのgetter、setterを自動生成させるため、lombokの依存を導入する。pom.xmlファイルに追加するだけ。

2.準備作業 - エンティティ作成

データベースの各項目に対応するエンティティを作成する。
DB設計:
https://storage.googleapis.com/zenn-user-upload/d4247defc7a6-20250427.png

2.1 Userクラス作成

今はUserエンティティだけ。

@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private String nickname;
    private String email;
    private String userPic;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

2.2 レスポンス用クラス作成

異なるControllerメソッドからの戻り値を統一させるために、共通利用できる戻り値の型を作る。

//統一したレスポンスの型クラス
@NoArgsConstructor  //引数なしのコンストラクタ(デフォルトコンストラクタ)を自動生成する
@AllArgsConstructor  //全フィールドを引数に持つコンストラクタを自動生成する
@Data
public class Result<T> {
    private Integer code; // ステータスコード 0-成功 1-失敗
    private String message; // メッセージ
    private T data; // レスポンスデータ

    // 操作完了のレスポンスを返す(データ付き)
    public static <E> Result<E> success(E data) {
        return new Result<>(0, "操作完了", data);
    }

    // 操作完了のレスポンスを返す
    public static Result success() {
        return new Result(0, "操作完了", null);
    }

    // 操作失敗のレスポンスを返す
    public static Result error(String message) {
        return new Result(1, message, null);
    }
}

ここで注意すべきことは、@NoArgsConstructor@AllArgsConstructorの利用だ。
@AllArgsConstructorがなければコンパイルエラーになり、@NoArgsConstructorがなければ実行時にトラブルになるリスクがある。2つ一緒に使うのが一般的らしい。

✅ 1. @NoArgsConstructorが必要な理由
Springなどのフレームワークがリフレクションでインスタンスを作るときに、引数なしコンストラクタが必要になることが多いから。
例えば、JSONデシリアライズ(@RequestBodyでリクエストを受け取るときなど)では、最初に空オブジェクトを作ってからプロパティを埋める流れになる。
引数なしコンストラクタがないと、エラーになるケースがある。

✅ 2. @AllArgsConstructorが必要な理由
コードからすぐに初期化されたResultオブジェクトを作りたいとき、毎回Setterを呼びたくないから。
特にこのクラスでは、success()やerror()メソッド内でこうしてコンストラクタ呼び出ししていますよね:

return new Result<>(0, "操作成功", data);

ここで一発でオブジェクト生成するためには、すべてのフィールドを引数に取るコンストラクタが必要。

3. 開発するメソッド・クラスを考える

実装するためのメソッドは下図通り。

4. UserControllerの開発

メイン処理:
1.ユーザ名は既に使用されているかをチェック
2.新規ユーザの登録

UserControllerを作成する際に、ServiceやMapperなどは未作成の状態であるため、IDEからエラーがでる。IDEの提案を承諾してServiceやMapperなどを自動的に作っていけばよい。

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/register")
    public Result register(String username, String password) {

        //1.ユーザ名は既に使用されているかをチェック
        User user = userService.findByUserName(username);
        if (user == null) {
            //2.新規ユーザの登録
            userService.register(username, password);
            return Result.success();
        } else {
            return Result.error("ユーザ名は既に使われています。");
        }
    }
}

5. UserSeriveとUserServiceImplの開発

UserSerive

public interface UserService {
    User findByUserName(String username);

    void register(String username, String password);
}

UserServiceImpl

パスワードは平文でDBに保存してはいけないのでMD5ハッシュ化してからDBに保存する。

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User findByUserName(String username) {
        User user = userMapper.findByUserName(username);
        System.out.println(user);
        return user;
    }

    @Override
    public void register(String username, String password) {
        //パスワード暗号化
        String md5string = Md5Util.getMD5String(password);
        //マッパーに追加
        userMapper.add(username, md5string);
    }
}

6. Mapperの開発

@Mapper
public interface UserMapper {

    // ユーザ名でユーザ検索する。
    @Select("select * from rezerosb_weblog.user where username=#{username}")
    User findByUserName(String username);

    // 追加挿入
    @Insert("insert into rezerosb_weblog.user(username,password,create_time,update_time)" +
            " values(#{username},#{password},now(),now())")
    void add(String username,String password);
}

APIテスト

Postmanを使ってAPIを叩いてuser登録できたかをDBテーブルで確認する。

Postman

データレコード確認

Discussion