Spring MVC:复杂对象为GET @RequestParam


190

假设我有一个列出表中对象的页面,并且我需要放置一个表格来过滤表。过滤器以Ajax GET的形式发送到如下网址: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中的表单处理Spring MVC Form Handling Example是什么以及如何使用@ModelAttribute。只需在Google上搜索“ Spring MVC表单处理 ”,您将获得大量的教程/示例。但请确保使用现代的表单处理方式,即Spring v2.5 +
informatik01

Answers:


246

您绝对可以做到这一点,只需删除@RequestParam注释,Spring就会干净地将您的请求参数绑定到您的类实例:

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

8
碧珠,能举个例子吗?我正在对Rest API进行基本的http GET调用,并且没有花哨的形式。
bschandramohan

32
请注意,默认情况下,Spring要求MyObject的getter / setter方法自动绑定它。否则它将不会绑定myObject。
aka_sh 2014年

20
如何在MyObject中设置字段为可选/非可选?不知道如何找到这个合适的文档..
worldsayshi

6
@Biju,是否有一种方法可以控制默认值,并且MyObject此后是否需要使用默认值,就像我们可以使用@RequestParam一样?
阿列克谢

6
@BijuKunjummen如何更新MyObjectSnake大小写以接受查询参数并将其映射到的驼峰式成员MyObject。例如,?book_id=4应该与?的bookId成员映射MyObject
Vivek Vardhan

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}]

希望对您有所帮助:)

更新/科特琳

因为目前,如果有人想定义类似的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())

Spring(在Boot中测试)针对答案中提到的请求返回以下错误:

“无法将类型'java.lang.String []'的值转换为所需类型'java.lang.Long []';嵌套的异常是java.lang.NumberFormatException:对于输入字符串:\“ 353,234 \””

数据类仅适用于以下请求参数形式:

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

注意这一点!


1
是否可以将dto字段设置为“ required”?
正常的

4
我建议尝试使用Spring MVC验证器。例如:codejava.net/frameworks/spring/...
Przemek诺瓦克

很好奇,它不需要注释!我想知道,是否有必要为此添加显式注释?
詹姆斯·沃特金斯

8

我有一个非常相似的问题。其实问题比我想的还要深。我使用jQuery的$.post,它使用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添加了setter / getters,构造方法,UserPreferences但没有任何可能,因为它将发送的数据识别为5个参数,但实际上映射的方法只有2个参数。我还尝试了Biju的解决方案,但是发生的是,spring使用默认构造函数创建了一个UserPreferences对象,并且没有填写数据。

我通过从客户端发送首选项的JSon字符串来解决该问题,并像在服务器端将其作为String一样进行处理;

客户:

 $.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 2015年

1
我也有完全相同的问题。我使用@RequestMapping(method = POST, path = "/settings/{projectId}") public void settings(@PathVariable String projectId, @RequestBody ProjectSettings settings)
PetrÚjezdský16

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);
}

就我而言,尝试在Jackson绑定中使用它会导致com.fasterxml.jackson.databind.exc.InvalidDefinitionException

您将需要从dto创建一个新对象。



0

接受的答案就像一个超级按钮一样工作,但是如果该对象具有对象列表,则将无法按预期工作,因此这是我进行一些挖掘后的解决方案。

遵循此线程建议,这是我的工作方式。

  • 前端:将您的对象字符串化,而不是使用base64对其进行编码以便提交。
  • 后端:解码base64字符串,然后将字符串json转换为所需的对象。

用邮递员调试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
)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.