Spring MVC:GET @RequestParamとしての複雑なオブジェクト


191

テーブルのオブジェクトを一覧表示するページがあり、テーブルをフィルター処理するフォームを配置する必要があるとします。フィルターは、Ajax GETとして次のようなURLに送信されます。http//foo.com/system/controller/action?page = 1&prop1 = x&prop2 = y&prop3 = z

そして、私のようなコントローラにたくさんのパラメータを置く代わりに:

@RequestMapping(value = "/action")
public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    @RequestParam(value = "prop1", required = false) String prop1,
    @RequestParam(value = "prop2", required = false) String prop2,
    @RequestParam(value = "prop3", required = false) String prop3) { ... }

そして、私がMyObjectを持っていると仮定すると:

public class MyObject {
    private String prop1;
    private String prop2;
    private String prop3;

    //Getters and setters
    ...
}

私は次のようなことをしたいです:

@RequestMapping(value = "/action")
public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    @RequestParam(value = "myObject", required = false) MyObject myObject,) { ... }

出来ますか?どうやってやるの?


1
「@ModelAttribute」と「@RequestMapping」を組み合わせて使用​​してみてください。MyObjectがモデルになります。
michal 2013年

@michal +1。これを行う方法を示すいくつかのチュートリアルを次に示します。Spring 3 MVC:Spring 3.0 MVCでのフォームの処理何であり、どのように使用するか@ModelAttributeSpring MVCフォーム処理の例。「Spring MVCフォームの処理」をググると、大量のチュートリアル/例が表示されます。ただし、必ず最新のフォーム処理方法を使用して
ください。

回答:


247

絶対にそれを行うことができます。@RequestParamアノテーションを削除するだけで、Springはリクエストパラメータをクラスインスタンスにクリーンにバインドします。

public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    MyObject myObject)

8
美寿さん、これをどう呼ぶか例を挙げていただけますか?Rest APIに対して基本的なhttp GET呼び出しを行っていますが、特別なフォームはありません。
bschandramohan 2013

32
Springはデフォルトで、MyObjectが自動的にバインドするためのゲッター/セッターを必要とすることに注意してください。それ以外の場合は、myObjectをバインドしません。
aka_sh 2014

20
MyObjectでフィールドをオプション/非オプションに設定するにはどうすればよいですか?方法がわからない、このための適切な文書を見つけるために...
worldsayshi

6
@ Biju、@ RequestParamでMyObject実行できるのと同様の方法で、デフォルト値を制御し、そのために必要な方法はありますか?
Alexey 2017年

7
@BijuKunjummenを更新しMyObjectて、Snakeケースでクエリパラメータを受け入れ、のラクダケースメンバーにマップする方法を教えてくださいMyObject。たとえば、は?のメンバーで?book_id=4マッピングする必要がbookIdありMyObjectます。
Vivek Vardhan 2017

55

私からの短い例をいくつか追加します。

DTOクラス:

public class SearchDTO {
    private Long id[];

    public Long[] getId() {
        return id;
    }

    public void setId(Long[] id) {
        this.id = id;
    }
    // reflection toString from apache commons
    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

コントローラクラス内のリクエストマッピング:

@RequestMapping(value="/handle", method=RequestMethod.GET)
@ResponseBody
public String handleRequest(SearchDTO search) {
    LOG.info("criteria: {}", search);
    return "OK";
}

クエリ:

http://localhost:8080/app/handle?id=353,234

結果:

[http-apr-8080-exec-7] INFO  c.g.g.r.f.w.ExampleController.handleRequest:59 - criteria: SearchDTO[id={353,234}]

それが役に立てば幸いです:)

更新/ KOTLIN

現在誰かが同様のDTOを定義したい場合、私はKotlinで多くの作業をしているので、Kotlinのクラスは次の形式になります:

class SearchDTO {
    var id: Array<Long>? = arrayOf()

    override fun toString(): String {
        // to string implementation
    }
}

このdataようなクラスで:

data class SearchDTO(var id: Array<Long> = arrayOf())

スプリング(ブートでテスト済み)は、回答で言及されたリクエストに対して次のエラーを返します。

"タイプ 'java.lang.String []'の値を必要なタイプ 'java.lang.Long []'に変換できませんでした。ネストされた例外はjava.lang.NumberFormatException:入力文字列の場合:\" 353,234 \ ""

データクラスは、次のリクエストパラメータフォームでのみ機能します。

http://localhost:8080/handle?id=353&id=234

これに注意してください!


1
「必須」をdtoフィールドに設定することは可能ですか?
通常の

4
Spring MVCバリデーターを試すことをお勧めします。例:codejava.net/frameworks/spring/...
Przemek貰える

これが注釈を必要としないことは非常に奇妙です!不必要ではありますが、これに対する明示的な注釈はありますか?
James Watkins

8

非常に似た問題があります。実際、問題は私が思ったより深いです。デフォルト$.postで使用するjquery を使用しContent-Type:application/x-www-form-urlencoded; charset=UTF-8ています。残念ながら私はそれに基づいて自分のシステムを構築し、複雑なオブジェクトを@RequestParamそれを実現することはできませんでした。

私の場合、ユーザー設定を次のようなもので送信しようとしています。

 $.post("/updatePreferences",  
    {id: 'pr', preferences: p}, 
    function (response) {
 ...

クライアント側では、サーバーに送信される実際の生データは次のとおりです。

...
id=pr&preferences%5BuserId%5D=1005012365&preferences%5Baudio%5D=false&preferences%5Btooltip%5D=true&preferences%5Blanguage%5D=en
...

として解析;

id:pr
preferences[userId]:1005012365
preferences[audio]:false
preferences[tooltip]:true
preferences[language]:en

そしてサーバー側は;

@RequestMapping(value = "/updatePreferences")
public
@ResponseBody
Object updatePreferences(@RequestParam("id") String id, @RequestParam("preferences") UserPreferences preferences) {

    ...
        return someService.call(preferences);
    ...
}

私が試み@ModelAttribute、にすべての可能性のある追加セッター/ゲッター、コンストラクタUserPreferencesそれは5つのパラメータとして送信されたデータを認識実際にマッピングされた方法でのみ2つのパラメータを有するようにない機会を。私はBijuの解決策も試しましたが、何が起こるかというと、SpringはデフォルトのコンストラクターでUserPreferencesオブジェクトを作成し、データを入力しません。

クライアント側から設定のJSon文字列を送信し、サーバー側の文字列であるかのように処理することで問題を解決しました。

クライアント:

 $.post("/updatePreferences",  
    {id: 'pr', preferences: JSON.stringify(p)}, 
    function (response) {
 ...

サーバ:

@RequestMapping(value = "/updatePreferences")
public
@ResponseBody
Object updatePreferences(@RequestParam("id") String id, @RequestParam("preferences") String preferencesJSon) {


        String ret = null;
        ObjectMapper mapper = new ObjectMapper();
        try {
            UserPreferences userPreferences = mapper.readValue(preferencesJSon, UserPreferences.class);
            return someService.call(userPreferences);
        } catch (IOException e) {
            e.printStackTrace();
        }
}

簡単に言うと、RESTメソッド内で手動で変換を行いました。私の意見では、Springが送信されたデータを認識しない理由はコンテンツタイプです。


1
私は同じ問題を経験し、同じ方法でそれを解決しました。ちなみに、あなたは今日より良い解決策を見つけましたか?
Shay Elkayam

1
私もまったく同じ問題を抱えています。私はより@RequestMapping(method = POST, path = "/settings/{projectId}") public void settings(@PathVariable String projectId, @RequestBody ProjectSettings settings)
適切

4

フィールドを必須に設定する方法についての質問が各投稿の下にポップアップ表示されるため、必要に応じてフィールドを設定する方法について簡単な例を書きました:

public class ExampleDTO {
    @NotNull
    private String mandatoryParam;

    private String optionalParam;

    @DateTimeFormat(iso = ISO.DATE) //accept Dates only in YYYY-MM-DD
    @NotNull
    private LocalDate testDate;

    public String getMandatoryParam() {
        return mandatoryParam;
    }
    public void setMandatoryParam(String mandatoryParam) {
        this.mandatoryParam = mandatoryParam;
    }
    public String getOptionalParam() {
        return optionalParam;
    }
    public void setOptionalParam(String optionalParam) {
        this.optionalParam = optionalParam;
    }
    public LocalDate getTestDate() {
        return testDate;
    }
    public void setTestDate(LocalDate testDate) {
        this.testDate = testDate;
    }
}

@RequestMapping(value = "/test", method = RequestMethod.GET)
public String testComplexObject (@Valid ExampleDTO e){
    System.out.println(e.getMandatoryParam() + " " + e.getTestDate());
    return "Does this work?";
}

0

参照してください答えながら@ModelAttribute@RequestParam@PathParamおよび同類が有効である、私はに走った小さな落とし穴があります。結果のメソッドパラメータは、SpringがDTOをラップするプロキシです。したがって、独自のカスタムタイプを必要とするコンテキストで使用しようとすると、予期しない結果が生じる可能性があります。

以下は機能しません:

@GetMapping(produces = APPLICATION_JSON_VALUE)
public ResponseEntity<CustomDto> request(@ModelAttribute CustomDto dto) {
    return ResponseEntity.ok(dto);
}

私の場合は、ジャクソンにそれを使用しようとするとの結果バインディングcom.fasterxml.jackson.databind.exc.InvalidDefinitionException

dtoから新しいオブジェクトを作成する必要があります。



0

受け入れられた答えは魅力のように機能しますが、オブジェクトにオブジェクトのリストが含まれている場合、期待どおりに機能しないため、いくつかの掘り下げ後の解決策を次に示します。

続いて、このスレッドのアドバイス、ここで私がやった方法です。

  • フロントエンド:オブジェクトを文字列化して、送信用にbase64でエンコードします。
  • バックエンド:base64文字列をデコードしてから、文字列jsonを目的のオブジェクトに変換します。

postmanを使用したAPIのデバッグには最適ではありませんが、期待どおりに機能しています。

元のオブジェクト: {ページ:1、サイズ:5、フィルター:[{フィールド: "id"、値:1、比較: "EQ"}

エンコードされたオブジェクト: eyJwYWdlIjoxLCJzaXplIjo1LCJmaWx0ZXJzIjpbeyJmaWVsZCI6ImlkUGFyZW50IiwiY29tcGFyaXNvbiI6Ik5VTEwifV19

@GetMapping
fun list(@RequestParam search: String?): ResponseEntity<ListDTO> {
    val filter: SearchFilterDTO = decodeSearchFieldDTO(search)
    ...
}

private fun decodeSearchFieldDTO(search: String?): SearchFilterDTO {
    if (search.isNullOrEmpty()) return SearchFilterDTO()
    return Gson().fromJson(String(Base64.getDecoder().decode(search)), SearchFilterDTO::class.java)
}

そして、ここでSearchFilterDTOとFilterDTO

class SearchFilterDTO(
    var currentPage: Int = 1,
    var pageSize: Int = 10,
    var sort: Sort? = null,
    var column: String? = null,
    var filters: List<FilterDTO> = ArrayList<FilterDTO>(),
    var paged: Boolean = true
)

class FilterDTO(
    var field: String,
    var value: Any,
    var comparison: Comparison
)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.