フロントエンドとバックエンドの分離開発モデルにおいて、フロントエンドがバックエンドにリクエストを送信する際、バックエンドはリクエストデータを効果的に処理・解析する必要があります。コードの保守性と可読性を向上させるために、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を返します
}
}
}
コード説明
-
クラスアノテーション:
@Data:Lombok のアノテーションで、getter、setter、toString、equals、hashCodeなどのメソッドを自動生成し、定型コードの記述を削減します。@NoArgsConstructor:引数なしコンストラクタを自動生成します。@AllArgsConstructor:すべてのパラメータを持つコンストラクタを自動生成します。
-
メンバー変数:
private Map<String, Object> payload;:フロントエンドから送信されたリクエストデータを格納するために使用され、キーは文字列型、値はオブジェクト型です。
-
メソッド説明:
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 クラスを拡張し、さまざまなデータ型解析メソッドを追加して、さまざまなビジネス要件に対応できます。