假设我有一个页面,列出了一个表上的对象,我需要放一个表单来过滤表。过滤器作为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,) { ... }

这可能吗? 我该怎么做呢?


当前回答

我将添加一些简短的例子。

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

因为目前我正在大量使用Kotlin,如果有人想定义类似的DTO, Kotlin中的类应该有以下形式:

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

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

使用像这样的数据类:

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

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

“转换java.lang类型的值失败。字符串[]'到所需的类型 “java.lang.Long[]”;嵌套异常为 java.lang.NumberFormatException:用于输入字符串:\"353,234\""

data类只对以下请求参数表单有效:

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

要注意这一点!

其他回答

你完全可以这样做,只需要删除@RequestParam注释,Spring就会把你的请求参数绑定到你的类实例上:

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

我也有类似的问题。事实上,问题比我想的要深刻。我使用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/getter,构造函数与所有可能性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不能识别发送数据的原因是内容类型。

我将添加一些简短的例子。

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

因为目前我正在大量使用Kotlin,如果有人想定义类似的DTO, Kotlin中的类应该有以下形式:

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

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

使用像这样的数据类:

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

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

“转换java.lang类型的值失败。字符串[]'到所需的类型 “java.lang.Long[]”;嵌套异常为 java.lang.NumberFormatException:用于输入字符串:\"353,234\""

data类只对以下请求参数表单有效:

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

要注意这一点!

由于在每篇文章下面都会弹出关于如何设置字段强制的问题,我写了一个关于如何按要求设置字段的小例子:

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

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

虽然引用@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中创建一个新对象。