135

是否有必要包装在一个支持对象中?我想做这个:

@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"
    }
}

那是对的吗?我的另一个选择是更改RequestMethodtoGET@RequestParam在查询字符串中使用或@PathVariable与其中任何一个一起使用RequestMethod

4

16 回答 16

114

虽然确实@RequestBody必须映射到单个对象,但该对象可以是 a 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"
于 2015-11-17T05:17:39.270 回答
107

你是对的,@RequestBody 注释参数应该包含请求的整个主体并绑定到一个对象,所以你基本上必须选择你的选项。

如果您绝对想要您的方法,则可以使用自定义实现:

说这是你的 json:

{
    "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)

现在编写一个自定义HandlerMethodArgumentResolver,它使用上面定义的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 "";

    }
}

现在只需使用 Spring MVC 注册它。有点涉及,但这应该可以干净地工作。

于 2012-10-15T14:19:32.510 回答
15

用于传递多个对象、参数、变量等。您可以使用 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.
}

我希望这会有所帮助。

于 2019-03-10T05:59:06.910 回答
12

您可以通过使用 body 和 path 变量来混合 post 参数以获得更简单的数据类型:

@RequestMapping(value = "new-trade/portfolio/{portfolioId}", method = RequestMethod.POST)
    public ResponseEntity<List<String>> newTrade(@RequestBody Trade trade, @PathVariable long portfolioId) {
...
}
于 2016-05-09T18:30:53.283 回答
2

@RequestParam是客户端发送的HTTP GETorPOST参数,请求映射是一段 URL,它是可变的:

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

var1&var2是请求参数。

http:/host/form/{params}

{params}是一个请求映射。您可以将您的服务称为 :http:/host/form/userhttp:/host/form/firm 将公司和用户用作Pathvariable.

于 2012-10-15T10:44:53.487 回答
2

简单的解决方案是创建一个具有 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"
}
于 2020-02-07T08:54:33.650 回答
1

您可以做简单的事情,而不是使用 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";
}

希望这对您有所帮助。

于 2012-10-15T12:15:44.823 回答
1

我已经改编了Biju的解决方案:

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;

    }

}

有什么不同:

  • 我正在使用杰克逊来转换 json
  • 我不需要注解中的值,你可以从 MethodParameter 中读取参数的名称
  • 我还从 Methodparameter => 中读取了参数的类型,因此解决方案应该是通用的(我使用字符串和 DTO 对其进行了测试)

BR

于 2014-12-02T22:58:56.113 回答
1

您还可以使用 MultiValue Map 来保存 requestBody。这里是它的示例。

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

与使用 Map 保存请求正文时的 @RequestBody 注释不同,我们需要使用 @RequestParam 进行注释

并在 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");
    }
于 2019-10-22T15:56:55.873 回答
0

GET 和 POST 都存在请求参数,对于 Get,它将作为查询字符串附加到 URL,但对于 POST,它位于请求正文中

于 2013-06-16T18:14:58.050 回答
0

不知道你在哪里添加 json 但如果我用 angular 这样做,它可以在没有 requestBody 的情况下工作: angluar:

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

爪哇:

@PostMapping(URL_MATCH)
public ResponseEntity<Void> match(Long str1, Long str2) {
  log.debug("found: {} and {}", str1, str2);
}
于 2018-03-22T20:53:41.253 回答
0

好的。我建议创建一个包含您需要的字段的值对象 (Vo)。代码更简单,我们不改变杰克逊的功能,更容易理解。问候!

于 2018-05-20T19:41:12.947 回答
0

您可以使用@RequestParam. 为此,您应该执行以下操作:

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

我知道,它有点破解,但它的工作原理!;)

于 2018-08-26T18:39:21.247 回答
0

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

于 2019-01-24T03:22:18.993 回答
0

使用内部类

@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;
        }
    }
}
于 2021-09-23T20:28:32.697 回答
0

如果有人对 webflux 解决方案感兴趣,下面是基于 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);
    }
}

于 2021-11-30T14:05:45.180 回答