[英]Spring MVC: Complex object as GET @RequestParam
Suppose i have a page that lists the objects on a table and i need to put a form to filter the table.假设我有一个页面列出了表格上的对象,我需要放置一个表格来过滤表格。 The filter is sent as an Ajax GET to an URL like that: http://foo.com/system/controller/action?page=1&prop1=x&prop2=y&prop3=z过滤器作为 Ajax GET 发送到这样的 URL: http : //foo.com/system/controller/action?page=1&prop1= x& prop2=y&prop3=z
And instead of having lots of parameters on my Controller like:而不是在我的控制器上有很多参数,比如:
@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) { ... }
And supposing i have MyObject as:假设我有 MyObject 为:
public class MyObject {
private String prop1;
private String prop2;
private String prop3;
//Getters and setters
...
}
I wanna do something like:我想做这样的事情:
@RequestMapping(value = "/action")
public @ResponseBody List<MyObject> myAction(
@RequestParam(value = "page", required = false) int page,
@RequestParam(value = "myObject", required = false) MyObject myObject,) { ... }
Is it possible?是否有可能? How can i do that?我怎样才能做到这一点?
You can absolutely do that, just remove the @RequestParam
annotation, Spring will cleanly bind your request parameters to your class instance:您绝对可以这样做,只需删除@RequestParam
注释,Spring 会将您的请求参数干净地绑定到您的类实例:
public @ResponseBody List<MyObject> myAction(
@RequestParam(value = "page", required = false) int page,
MyObject myObject)
I will add some short example from me.我将添加一些来自我的简短示例。
The DTO class: 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);
}
}
Request mapping inside controller class:在控制器类中请求映射:
@RequestMapping(value="/handle", method=RequestMethod.GET)
@ResponseBody
public String handleRequest(SearchDTO search) {
LOG.info("criteria: {}", search);
return "OK";
}
Query:询问:
http://localhost:8080/app/handle?id=353,234
Result:结果:
[http-apr-8080-exec-7] INFO c.g.g.r.f.w.ExampleController.handleRequest:59 - criteria: SearchDTO[id={353,234}]
I hope it helps :)我希望它有帮助:)
UPDATE / KOTLIN更新/科特林
Because currently I'm working a lot of with Kotlin if someone wants to define similar DTO the class in Kotlin should have the following form:因为目前我正在使用 Kotlin 进行很多工作,如果有人想定义类似的 DTO,那么 Kotlin 中的类应该具有以下形式:
class SearchDTO {
var id: Array<Long>? = arrayOf()
override fun toString(): String {
// to string implementation
}
}
With the data
class like this one:使用这样的data
类:
data class SearchDTO(var id: Array<Long> = arrayOf())
the Spring (tested in Boot) returns the following error for request mentioned in answer: Spring(在 Boot 中测试)为回答中提到的请求返回以下错误:
"Failed to convert value of type 'java.lang.String[]' to required type 'java.lang.Long[]'; nested exception is java.lang.NumberFormatException: For input string: \\"353,234\\"" “无法将类型 'java.lang.String[]' 的值转换为所需的类型 'java.lang.Long[]';嵌套异常为 java.lang.NumberFormatException:对于输入字符串:\\"353,234\\""
The data class will work only for the following request params form:数据类仅适用于以下请求参数表单:
http://localhost:8080/handle?id=353&id=234
Be aware of this!请注意这一点!
Since the question on how to set fields mandatory pops up under each post, I wrote a small example on how to set fields as required:由于每个帖子下都会弹出如何设置必填字段的问题,我写了一个关于如何设置必填字段的小例子:
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?";
}
I have a very similar problem.我有一个非常相似的问题。 Actually the problem is deeper as I thought.其实问题比我想象的更深。 I am using jquery $.post
which uses Content-Type:application/x-www-form-urlencoded; charset=UTF-8
我正在使用 jquery $.post
它使用Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
as default.默认Content-Type:application/x-www-form-urlencoded; charset=UTF-8
。 Unfortunately I based my system on that and when I needed a complex object as a @RequestParam
I couldn't just make it happen.不幸的是,我的系统基于此,当我需要一个复杂的对象作为@RequestParam
我不能让它发生。
In my case I am trying to send user preferences with something like;在我的情况下,我试图用类似的东西发送用户偏好;
$.post("/updatePreferences",
{id: 'pr', preferences: p},
function (response) {
...
On client side the actual raw data sent to the server is;在客户端,发送到服务器的实际原始数据是;
...
id=pr&preferences%5BuserId%5D=1005012365&preferences%5Baudio%5D=false&preferences%5Btooltip%5D=true&preferences%5Blanguage%5D=en
...
parsed as;解析为;
id:pr
preferences[userId]:1005012365
preferences[audio]:false
preferences[tooltip]:true
preferences[language]:en
and the server side is;服务器端是;
@RequestMapping(value = "/updatePreferences")
public
@ResponseBody
Object updatePreferences(@RequestParam("id") String id, @RequestParam("preferences") UserPreferences preferences) {
...
return someService.call(preferences);
...
}
I tried @ModelAttribute
, added setter/getters, constructors with all possibilities to UserPreferences
but no chance as it recognized the sent data as 5 parameters but in fact the mapped method has only 2 parameters.我尝试了@ModelAttribute
,向UserPreferences
添加了具有所有可能性的 setter/getter、构造函数,但没有机会,因为它将发送的数据识别为 5 个参数,但实际上映射的方法只有 2 个参数。 I also tried Biju's solution however what happens is that, spring creates an UserPreferences object with default constructor and doesn't fill in the data.我也尝试了 Biju 的解决方案,但是发生的情况是,spring 创建了一个带有默认构造函数的 UserPreferences 对象,并且不填充数据。
I solved the problem by sending JSon string of the preferences from the client side and handle it as if it is a String on the server side;我通过从客户端发送首选项的 JSon 字符串并在服务器端将其作为字符串处理来解决该问题;
client:客户:
$.post("/updatePreferences",
{id: 'pr', preferences: JSON.stringify(p)},
function (response) {
...
server:服务器:
@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();
}
}
to brief, I did the conversion manually inside the REST method.简而言之,我在 REST 方法中手动进行了转换。 In my opinion the reason why spring doesn't recognize the sent data is the content-type.在我看来,spring 无法识别发送的数据的原因是内容类型。
While answers that refer to @ModelAttribute
, @RequestParam
, @PathParam
and the likes are valid, there is a small gotcha I ran into.虽然引用@ModelAttribute
、 @RequestParam
、 @PathParam
等的答案是有效的,但我遇到了一个小问题。 The resulting method parameter is a proxy that Spring wraps around your DTO.生成的方法参数是 Spring 包裹在 DTO 周围的代理。 So, if you attempt to use it in a context that requires your own custom type, you may get some unexpected results.因此,如果您尝试在需要您自己的自定义类型的上下文中使用它,您可能会得到一些意想不到的结果。
The following will not work:以下将不起作用:
@GetMapping(produces = APPLICATION_JSON_VALUE)
public ResponseEntity<CustomDto> request(@ModelAttribute CustomDto dto) {
return ResponseEntity.ok(dto);
}
In my case, attempting to use it in Jackson binding resulted in a com.fasterxml.jackson.databind.exc.InvalidDefinitionException
.就我而言,尝试在 Jackson 绑定中使用它会导致com.fasterxml.jackson.databind.exc.InvalidDefinitionException
。
You will need to create a new object from the dto.您将需要从 dto 创建一个新对象。
Yes, You can do it in a simple way.是的,您可以通过一种简单的方式做到这一点。 See below code of lines.请参阅下面的行代码。
URL - http://localhost:8080/get/request/multiple/param/by/map?name= 'abc' & id='123' URL - http://localhost:8080/get/request/multiple/param/by/map?name= 'abc' & id='123'
@GetMapping(path = "/get/request/header/by/map")
public ResponseEntity<String> getRequestParamInMap(@RequestParam Map<String,String> map){
// Do your business here
return new ResponseEntity<String>(map.toString(),HttpStatus.OK);
}
Accepted answer works like a charm but if the object has a list of objects it won't work as expected so here is my solution after some digging.接受的答案就像一个魅力,但如果对象有一个对象列表,它就不会按预期工作,所以这是我经过一些挖掘后的解决方案。
Following this thread advice, here is how I've done.按照这个线程建议,这是我的做法。
It isn't the best for debugging your API with postman but it is working as expected for me.它不是用邮递员调试 API 的最佳选择,但它按我的预期工作。
Original object: { page: 1, size: 5, filters: [{ field: "id", value: 1, comparison: "EQ" }原始对象: {页面:1,大小:5,过滤器:[{字段:“id”,值:1,比较:“EQ”}
Encoded object: eyJwYWdlIjoxLCJzaXplIjo1LCJmaWx0ZXJzIjpbeyJmaWVsZCI6ImlkUGFyZW50IiwiY29tcGFyaXNvbiI6Ik5VTEwifV19编码对象: 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)
}
And here the SearchFilterDTO and FilterDTO这里是 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
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.