使用 Ajax 将@RequestBody 中的多个变量传递给 Spring MVC 控制器

是否有必要包裹一个对象? 我想这样做:

@RequestMapping(value = "/Test", method = RequestMethod.POST)
@ResponseBody
public boolean getTest(@RequestBody String str1, @RequestBody String str2) {}

像这样使用 JSON:

{
"str1": "test one",
"str2": "two test"
}

但我不得不用:

@RequestMapping(value = "/Test", method = RequestMethod.POST)
@ResponseBody
public boolean getTest(@RequestBody Holder holder) {}

然后使用这个 JSON:

{
"holder": {
"str1": "test one",
"str2": "two test"
}
}

是这样吗?我的另一个选择是将 RequestMethod改为 GET,并在查询字符串中使用 @RequestParam,或者将 @PathVariableRequestMethod一起使用。

309362 次浏览

@RequestParam是由客户端发送的 HTTP GETPOST参数,请求映射是 URL 的一段变量:

http:/host/form_edit?param1=val1&param2=val2

var1var2是请求参数。

http:/host/form/{params}

{params}是一个请求映射。您可以调用您的服务,如: http:/host/form/userhttp:/host/form/firm 其中公司和用户被用作 Pathvariable

您可以做一些简单的事情,而不是使用 json。

$.post("${pageContext.servletContext.contextPath}/Test",
{
"str1": "test one",
"str2": "two test",


<other form data>
},
function(j)
{
<j is the string you will return from the controller function.>
});

现在,您需要在控制器中映射 ajax 请求,如下所示:

 @RequestMapping(value="/Test", method=RequestMethod.POST)
@ResponseBody
public String calculateTestData(@RequestParam("str1") String str1, @RequestParam("str2") String str2, HttpServletRequest request, HttpServletResponse response){
<perform the task here and return the String result.>


return "xyz";
}

希望这个能帮到你。

您是正确的,@RequestBody 带注释的参数应该保存请求的整个主体并绑定到一个对象,因此您实际上必须使用您的选项。

如果您绝对想要自己的方法,那么有一个自定义的实现,您可以这样做:

说这是你儿子:

{
"str1": "test one",
"str2": "two test"
}

你想把它绑定到这里的两个参数上:

@RequestMapping(value = "/Test", method = RequestMethod.POST)
public boolean getTest(String str1, String str2)

首先定义一个自定义注释(比如 @JsonArg) ,使用 JSON 路径(比如您想要的信息的路径) :

public boolean getTest(@JsonArg("/str1") String str1, @JsonArg("/str2") String str2)

现在编写一个 Custom 参数解析器,它使用上面定义的 JsonPath来解析实际参数:

import java.io.IOException;


import javax.servlet.http.HttpServletRequest;


import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;


import com.jayway.jsonpath.JsonPath;


public class JsonPathArgumentResolver implements HandlerMethodArgumentResolver{


private static final String JSONBODYATTRIBUTE = "JSON_REQUEST_BODY";
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(JsonArg.class);
}


@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String body = getRequestBody(webRequest);
String val = JsonPath.read(body, parameter.getMethodAnnotation(JsonArg.class).value());
return val;
}


private String getRequestBody(NativeWebRequest webRequest){
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
String jsonBody = (String) servletRequest.getAttribute(JSONBODYATTRIBUTE);
if (jsonBody==null){
try {
String body = IOUtils.toString(servletRequest.getInputStream());
servletRequest.setAttribute(JSONBODYATTRIBUTE, body);
return body;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return "";


}
}

现在只需要在 SpringMVC 中注册它,虽然有点复杂,但是应该可以正常工作。

GET 和 POST 都存在 Request 参数,对于 GET,它将作为查询字符串附加到 URL,但是对于 POST,它在 Request Body 中

我调整了比朱的解决方案:

import java.io.IOException;


import javax.servlet.http.HttpServletRequest;


import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;


import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;




public class JsonPathArgumentResolver implements HandlerMethodArgumentResolver{


private static final String JSONBODYATTRIBUTE = "JSON_REQUEST_BODY";


private ObjectMapper om = new ObjectMapper();


@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(JsonArg.class);
}


@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String jsonBody = getRequestBody(webRequest);


JsonNode rootNode = om.readTree(jsonBody);
JsonNode node = rootNode.path(parameter.getParameterName());


return om.readValue(node.toString(), parameter.getParameterType());
}




private String getRequestBody(NativeWebRequest webRequest){
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);


String jsonBody = (String) webRequest.getAttribute(JSONBODYATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);
if (jsonBody==null){
try {
jsonBody = IOUtils.toString(servletRequest.getInputStream());
webRequest.setAttribute(JSONBODYATTRIBUTE, jsonBody, NativeWebRequest.SCOPE_REQUEST);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return jsonBody;


}


}

有什么不同:

  • 我在利用杰克逊改变杰森的信仰
  • 我不需要注释中的值,您可以读取 方法参数的参数
  • 我还将参数的类型从 Methodreference = > 中读出,因此解决方案应该是通用的(我使用字符串和 DTO 对它进行了测试)

BR

虽然 @RequestBody必须映射到一个对象,但是这个对象可以是 Map,所以这是一个很好的实现方法(不需要写一个一次性的备份对象) :

@RequestMapping(value = "/Test", method = RequestMethod.POST)
@ResponseBody
public boolean getTest(@RequestBody Map<String, String> json) {
//json.get("str1") == "test one"
}

如果你想要一个完整的 JSON 树,你也可以绑定到 Jackson 的 ObjectNode:

public boolean getTest(@RequestBody ObjectNode json) {
//json.get("str1").asText() == "test one"

对于更简单的数据类型,可以使用 body 和 path 变量混合 post 参数:

@RequestMapping(value = "new-trade/portfolio/{portfolioId}", method = RequestMethod.POST)
public ResponseEntity<List<String>> newTrade(@RequestBody Trade trade, @PathVariable long portfolioId) {
...
}

不确定你在哪里添加了 json,但是如果我像这样使用 angle,它不需要 requestBody 就可以工作: 盎格鲁:

    const params: HttpParams = new HttpParams().set('str1','val1').set('str2', ;val2;);
return this.http.post<any>( this.urlMatch,  params , { observe: 'response' } );

Java:

@PostMapping(URL_MATCH)
public ResponseEntity<Void> match(Long str1, Long str2) {
log.debug("found: {} and {}", str1, str2);
}

很好。 我建议创建一个包含所需字段的 Value Object (Vo)。代码更简单,我们不改变杰克逊的功能,它甚至更容易理解。 问候!

你可以通过使用 @RequestParam来达到你想要的效果。为此,你应该做以下事情:

  1. 声明表示对象的 RequestParams 参数,如果希望能够发送空值,则将 required选项设置为 false。
  2. 在前端,将要发送的对象字符串化,并将它们作为请求参数包含在内。
  3. 在后端,使用 Jackson ObjectMapper 或类似的工具将 JSON 字符串返回到它们表示的对象中,瞧!

我知道,这有点不靠谱,但是很管用! ;)

您也可以使用 @RequestBody Map<String, String> params,然后使用 params.get("key")来获得参数的值

用于传递多个对象、参数、变量等。可以使用 Jackson 库中的 ObjectNode 作为参数动态执行此操作。你可以这样做:

@RequestMapping(value = "/Test", method = RequestMethod.POST)
@ResponseBody
public boolean getTest(@RequestBody ObjectNode objectNode) {
// And then you can call parameters from objectNode
String strOne = objectNode.get("str1").asText();
String strTwo = objectNode.get("str2").asText();


// When you using ObjectNode, you can pas other data such as:
// instance object, array list, nested object, etc.
}

希望这个能帮上忙。

您还可以使用 MultiValue 映射将 requestBody 保存在。 这里有一个例子。

    foosId -> pathVariable
user -> extracted from the Map of request Body

与@RequestBody 注释不同,在使用 Map 保存需要用@RequestParam 注释的请求主体时,需要使用@RequestBody 注释

并在 Json RequestBody 中发送用户

  @RequestMapping(value = "v1/test/foos/{foosId}", method = RequestMethod.POST, headers = "Accept=application"
+ "/json",
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE ,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public String postFoos(@PathVariable final Map<String, String> pathParam,
@RequestParam final MultiValueMap<String, String> requestBody) {
return "Post some Foos " + pathParam.get("foosId") + " " + requestBody.get("user");
}

简单的解决方案是创建一个包含 str1和 str2属性的有效负载类:

@Getter
@Setter
public class ObjHolder{


String str1;
String str2;


}

在你能通过之后

@RequestMapping(value = "/Test", method = RequestMethod.POST)
@ResponseBody
public boolean getTest(@RequestBody ObjHolder Str) {}

你的要求是:

{
"str1": "test one",
"str2": "two test"
}

使用内部类

@RestController
public class MyController {


@PutMapping("/do-thing")
public void updateFindings(@RequestBody Bodies.DoThing body) {
...
}




private static class Bodies {
public static class DoThing {
public String name;
public List<String> listOfThings;
}
}
}

如果有人对网络流量解决方案感兴趣,下面是一个反应版本,基于 Biju 的答案。

请注意,有一个非常小但同步块,需要保护身体被消耗不止一次。如果您喜欢完全非阻塞版本,我建议在同一个调度程序上发布获得 json 的通量,以使检查和读取顺序进行。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;


import java.nio.charset.StandardCharsets;


@Slf4j
@RequiredArgsConstructor
public class JsonArgumentResolver implements HandlerMethodArgumentResolver {
private static final String ATTRIBUTE_KEY = "BODY_TOSTRING_RESOLVER";
private final ObjectMapper objectMapper;




@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(JsonArgument.class);
}




@Override
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
ServerWebExchange exchange) {
String fieldName = parameter.getParameterName();
Class<?> clz = parameter.getParameterType();


return getRequestBody(exchange).map(body -> {
try {
JsonNode jsonNode = objectMapper.readTree(body).get(fieldName);
String s = jsonNode.toString();
return objectMapper.readValue(s, clz);
} catch (JsonProcessingException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
});
}




private Mono<String> getRequestBody(ServerWebExchange exchange) {
Mono<String> bodyReceiver;
synchronized (exchange) {
bodyReceiver = exchange.getAttribute(ATTRIBUTE_KEY);
if (bodyReceiver == null) {
bodyReceiver = exchange.getRequest().getBody()
.map(this::convertToString)
.single()
.cache();
exchange.getAttributes().put(ATTRIBUTE_KEY, bodyReceiver);
}
}
return bodyReceiver;
}


private String convertToString(DataBuffer dataBuffer) {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);
return new String(bytes, StandardCharsets.UTF_8);
}
}