Javaによるフロントエンドリクエストのラップ:データ処理の最適化

フロントエンドとバックエンドの分離開発モデルにおいて、フロントエンドがバックエンドにリクエストを送信する際、バックエンドはリクエストデータを効果的に処理・解析する必要があります。コードの保守性と可読性を向上させるために、DataRequest クラスをラップしてフロントエンドからのリクエストデータを統一的に処理することができます。ここでは、DataRequest クラスのラップ方法と使用例を詳しく説明します。

背景と要件分析

実際のプロジェクト開発では、フロントエンドが送信するリクエストデータには、文字列、ブール値、リスト、整数、長整数、倍精度浮動小数点数など、さまざまなデータ型が含まれる可能性があります。バックエンドがこれらのデータを処理する際には、異なるデータ型を解析・変換する必要があります。各コントローラーメソッドでデータ解析コードを繰り返し記述することを避けるために、DataRequest クラスをラップし、データ解析ロジックをこのクラスにカプセル化することで、コードの再利用性を向上させることができます。

DataRequest クラスのラップ

コード実装


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class DataRequest {
    private Map<String, Object> payload;

    public void addEntry(String key, Object value){
        if (payload == null) {
            payload = new java.util.HashMap<>();
        }
        payload.put(key, value);
    }

    public Object retrieve(String key){
        if (payload == null) {
            return null;
        }
        return payload.get(key);
    }

    /**
     * 文字列として値を取得します。
     * @param key キー
     * @return 文字列値、またはnull
     */
    public String getStringValue(String key){
        Object value = retrieve(key);
        if (value == null) {
            return null;
        }
        if (value instanceof String) {
            return (String) value;
        }
        return value.toString();
    }

    /**
     * ブール値として値を取得します。
     * @param key キー
     * @return ブール値、またはfalse
     */
    public Boolean getBooleanValue(String key){
        Object value = retrieve(key);
        if (value == null) {
            return false;
        }
        if (value instanceof Boolean) {
            return (Boolean) value;
        }
        // 文字列 "true" を true と解釈します
        return Boolean.parseBoolean(value.toString());
    }

    /**
     * リストとして値を取得します。
     * @param key キー
     * @return リスト値、または空のリスト
     */
    public List<?> getListValue(String key){
        Object value = retrieve(key);
        if (value == null) {
            return Collections.emptyList();
        }
        if (value instanceof List) {
            return (List<?>) value;
        }
        return Collections.emptyList(); // 型が一致しない場合は空リストを返します
    }

    /**
     * 整数として値を取得します。
     * @param key キー
     * @return 整数値、またはnull
     */
    public Integer getIntegerValue(String key) {
        Object value = retrieve(key);
        if (value == null) {
            return null;
        }
        if (value instanceof Integer) {
            return (Integer) value;
        }
        // 数値文字列からの変換を試みます
        try {
            return Integer.parseInt(value.toString());
        } catch (NumberFormatException e) {
            // 浮動小数点数から整数への変換も試みます
            try {
                return (int)Double.parseDouble(value.toString());
            } catch (NumberFormatException e2) {
                return null; // 変換できない場合はnullを返します
            }
        }
    }

    /**
     * マップとして値を取得します。
     * @param key キー
     * @return マップ値、またはnull
     */
    public Map<?, ?> getMapValue(String key){
        Object value = retrieve(key);
        if (value == null) {
            return null;
        }
        if (value instanceof Map) {
            return (Map<?, ?>) value;
        }
        return null; // 型が一致しない場合はnullを返します
    }

    /**
     * 長整数として値を取得します。
     * @param key キー
     * @return 長整数値、またはnull
     */
    public Long getLongValue(String key) {
        Object value = retrieve(key);
        if (value == null) {
            return null;
        }
        if (value instanceof Long) {
            return (Long) value;
        }
        // 数値文字列からの変換を試みます
        try {
            return Long.parseLong(value.toString());
        } catch (NumberFormatException e) {
            return null; // 変換できない場合はnullを返します
        }
    }

    /**
     * 倍精度浮動小数点数として値を取得します。
     * @param key キー
     * @return 倍精度浮動小数点数値、またはnull
     */
    public Double getDoubleValue(String key) {
        Object value = retrieve(key);
        if (value == null) {
            return null;
        }
        if (value instanceof Double) {
            return (Double) value;
        }
        // 数値文字列からの変換を試みます
        try {
            return Double.parseDouble(value.toString());
        } catch (NumberFormatException e) {
            return null; // 変換できない場合はnullを返します
        }
    }
}

コード説明

  1. クラスアノテーション
    • @Data:Lombok のアノテーションで、gettersettertoStringequalshashCode などのメソッドを自動生成し、定型コードの記述を削減します。
    • @NoArgsConstructor:引数なしコンストラクタを自動生成します。
    • @AllArgsConstructor:すべてのパラメータを持つコンストラクタを自動生成します。
  2. メンバー変数
    • private Map<String, Object> payload;:フロントエンドから送信されたリクエストデータを格納するために使用され、キーは文字列型、値はオブジェクト型です。
  3. メソッド説明
    • addEntry(String key, Object value)payload にキーと値のペアを追加します。payload が null の場合は初期化します。
    • retrieve(String key):キーに基づいて対応する値を取得します。payload が null の場合は null を返します。
    • getStringValue(String key):文字列型の値を取得します。値が null の場合は null を返します。値が文字列型の場合は直接返します。それ以外の場合は文字列に変換して返します。
    • getBooleanValue(String key):ブール値を取得します。値が null の場合は false を返します。値がブール型の場合は直接返します。値の文字列表現が "true" の場合は true を返します。それ以外の場合は false を返します。
    • getListValue(String key):リスト型の値を取得します。値が null の場合は空のリストを返します。値がリスト型の場合は直接返します。それ以外の場合は空のリストを返します。
    • getIntegerValue(String key):整数型の値を取得します。payload または値が null の場合は null を返します。値が整数型の場合は直接返します。それ以外の場合は整数への変換を試み、失敗した場合は null を返します。Double.parseDouble からの変換も考慮しています。
    • getMapValue(String key):マップ型の値を取得します。payload または値が null の場合は null を返します。値がマップ型の場合は直接返します。それ以外の場合は null を返します。
    • getLongValue(String key):長整数型の値を取得します。payload または値が null の場合は null を返します。値が長整数型の場合は直接返します。それ以外の場合は長整数への変換を試み、失敗した場合は null を返します。
    • getDoubleValue(String key):倍精度浮動小数点数型の値を取得します。payload または値が null の場合は null を返します。値が倍精度浮動小数点数型の場合は直接返します。それ以外の場合は倍精度浮動小数点数への変換を試み、失敗した場合は null を返します。

使用例

以下は、DataRequest クラスの使用例です。コントローラーメソッドでこのクラスを使用して、フロントエンドから送信されたリクエストデータを処理する方法を示しています。


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.RestController;

// 仮のクラス定義
class Result {
    public static Result success() { return new Result(); }
    public static Result error(int code, String message) { return new Result(); }
}
enum ResultCode { FAIL, SUCCESS }
class MyException extends RuntimeException {
    public MyException(String message) { super(message); }
}
interface PassengerService {
    void deletePassenger(Integer id);
}

@RestController
@RequestMapping("/passenger")
public class PassengerController {

    private final PassengerService passengerService;

    // コンストラクタインジェクション
    public PassengerController(PassengerService passengerService) {
        this.passengerService = passengerService;
    }

    @PostMapping("/remove")
    public Result removePassenger(@RequestBody DataRequest requestData){
        try{
            Integer passengerId = requestData.getIntegerValue("id");
            if(passengerId == null){
                // IDがnullの場合は例外をスロー
                throw new MyException("Passenger ID cannot be null.");
            }
            passengerService.deletePassenger(passengerId);
            return Result.success();
        } catch(MyException e){
            // エラーハンドリング
            return Result.error(ResultCode.FAIL.ordinal(), e.getMessage());
        } catch (Exception e) {
            // その他の予期せぬエラー
            return Result.error(ResultCode.FAIL.ordinal(), "An unexpected error occurred.");
        }
    }
}

例の説明

removePassenger メソッドでは、@RequestBody DataRequest requestData を使用してフロントエンドから送信されたリクエストデータを受信し、DataRequest オブジェクトとしてラップします。その後、requestData.getIntegerValue("id") メソッドを呼び出して、リクエストデータ内の "id" フィールドの値を取得します。ID が null の場合は MyException 例外をスローし、それ以外の場合は passengerService.deletePassenger(passengerId) メソッドを呼び出して対応する乗客情報を削除し、操作成功のレスポンスを返します。

まとめ

DataRequest クラスをラップすることにより、フロントエンドリクエストデータの解析ロジックを 1 つのクラスにカプセル化し、コードの再利用性と保守性を向上させることができます。実際のプロジェクトでは、必要に応じて DataRequest クラスを拡張し、さまざまなデータ型解析メソッドを追加して、さまざまなビジネス要件に対応できます。

タグ: Java Spring Boot REST API Data Handling Code Refactoring

5月18日 00:09 投稿