Ajax를 사용하여 @RequestBody의 여러 변수를 Spring MVC 컨트롤러에 전달


113

받침 물체로 감싸 야합니까? 나는 이것을하고 싶다 :

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

그 맞습니까? 내 다른 옵션은 변경하는 것 RequestMethodGET사용 @RequestParam쿼리 문자열 또는 사용 @PathVariable중 하나와 RequestMethod.

답변:


92

맞습니다. @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)

이제 실제 인수를 해결하기 위해 위에 정의 된 JsonPath 를 사용 하는 Custom HandlerMethodArgumentResolver 를 작성합니다 .

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에 등록하십시오. 약간 관련이 있지만 이것은 깨끗하게 작동합니다.


2
@JsonArg와 같이 사용자 지정 주석을 어떻게 만드나요?
Surendra Jnawali

왜 이런거야? 이제 백엔드에서 다양한 래퍼 클래스를 만들어야합니다. Struts2 애플리케이션을 Springboot로 마이그레이션하고 있는데 ajax를 사용하여 보낸 JSON 객체가 실제로 모델의 두 개 이상의 객체 인 경우가 많이있었습니다. 예 : 사용자 및 활동
Jose Ospina

이 링크는 "어떻게 스프링 MVC와 함께이 등록"보여 geekabyte.blogspot.sg/2014/08/...
Bodil

3
이 옵션이 봄에 추가되지 않는 이유는 여전히 흥미 롭습니다. 당신이 걷고처럼 가지고 못해 래퍼 객체를 생성하지 않는 경우는 논리적 인 선택처럼 보인다
티비

@SurendraJnawali 당신은 이런 식으로 작업을 수행 할 수 있습니다@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface JsonArg { String value() default ""; }
Epono

88

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

@JoseOspina 왜 그렇게 할 수 없습니다. requestBody와지도 <문자열, 개체>과 관련된 모든 위험
벤 쳉

@Ben 내 말은 하나의 단일 Map객체를 사용하여 그 안에 원하는 수의 객체를 저장할 수 있지만 최상위 객체는 여전히 하나 여야하며 두 개의 최상위 객체가있을 수 없습니다.
Jose Ospina

1
다음 과 같은 동적 접근 방식 의 단점 Map<String, String>은 다음 과 같습니다 . API 문서 라이브러리 (swagger / springfox 등)는 소스 코드에서 요청 / 응답 스키마를 구문 분석 할 수 없을 것입니다.
stratovarius

10

더 간단한 데이터 유형에 대해 본문 및 경로 변수를 사용하여 post 인수를 혼합 할 수 있습니다.

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

10

여러 객체, 매개 변수, 변수 등을 전달합니다. 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.
}

도움이 되었기를 바랍니다.


2

@RequestParam는 IS HTTP GET또는 POST클라이언트가 보낸 매개 변수는 변수의 URL의 세그먼트는 요청 매핑입니다 :

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

var1& var2요청 매개 변수입니다.

http:/host/form/{params}

{params}요청 매핑입니다. 다음과 같이 서비스를 호출 할 수 있습니다. http:/host/form/user또는 http:/host/form/firm 회사 및 사용자가 사용되는 곳 Pathvariable.


이 질문에 대답하고 잘못되지 않습니다, 당신은 POST 요청에 쿼리 문자열을 사용하지 않는
님 침 스키

1
@NimChimpsky : 물론 가능합니다. POST 요청은 여전히 ​​URL에 매개 변수를 포함 할 수 있습니다.
Martijn Pieters

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

1
이 주석의 패키지는 무엇입니까? Autoimport는 import jdk.nashorn.internal.objects.annotations.Setter 만 제공했습니다. 편집하다. 나는 그것이 Lombok projectlombok.org/features/GetterSetter 라고 가정합니다 . 내가 틀렸다면 정정 해주세요
Gleichmut

@Gleichmut은 변수에 대해 간단한 getter 및 setter를 사용할 수 있습니다. 예상대로 작동합니다.
Gimnath

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

도움이 되었기를 바랍니다.


1
그것은 json이고 작동하지 않습니다. 메소드에 requestparam을 지정하고 있지만 ajax 포스트 요청에서 json으로 equestbody를 정의합니다.
NimChimpsky

Ajax 호출에서 JSON 형식을 사용하지 않았습니다를 참조하십시오. 두 개의 요청 매개 변수를 간단히 사용했으며 컨트롤러에서 @RequestParam 주석으로 해당 매개 변수를 가져올 수 있습니다. 작동 중입니다. 나는 이것을 사용한다. 시도해보세요.
Japan Trivedi 2012 년

나는 그것을 시도했다, 그것의 질문의 요점. 그렇게 작동하지 않습니다.
NimChimpsky

정확히 무엇을 시도했는지 지정하십시오. 귀하의 질문에 그것을 보여주십시오. 내가 이해 한 것과 다른 요구 사항이 있다고 생각합니다.
Japan Trivedi 2012 년

1
첫 번째 시도에서 나를 위해 일했습니다. 감사!
Humppakäräjät

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;

    }

}

다른 점 :

  • Jackson을 사용하여 json을 변환하고 있습니다.
  • 주석에 값이 필요하지 않습니다. MethodParameter에서 매개 변수 이름을 읽을 수 있습니다.
  • 또한 Methodparameter =>에서 매개 변수 유형을 읽었으므로 솔루션은 일반적이어야합니다 (문자열과 DTO로 테스트).

BR


0

요청 매개 변수는 GET 및 POST 모두에 존재합니다. Get의 경우 URL에 쿼리 문자열로 추가되지만 POST의 경우 요청 본문 내에 있습니다.


0

json을 어디에 추가했는지 확실하지 않지만 각도로 이렇게하면 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);
}

0

좋은. 필요한 필드가 포함 된 값 개체 (Vo)를 만드는 것이 좋습니다. 코드는 더 간단하고 Jackson의 기능을 변경하지 않으며 이해하기 훨씬 쉽습니다. 문안 인사!


0

을 사용하여 원하는 것을 얻을 수 있습니다 @RequestParam. 이를 위해 다음을 수행해야합니다.

  1. 개체를 나타내는 RequestParams 매개 변수를 선언하고 requirednull 값을 보낼 수 있도록 하려면 옵션을 false로 설정합니다 .
  2. 프런트 엔드에서 보낼 개체를 문자열 화하고 요청 매개 변수로 포함합니다.
  3. 백엔드에서 JSON 문자열을 Jackson ObjectMapper 또는 이와 유사한 것을 사용하여 나타내는 객체로 되 돌리십시오.

알아요, 약간의 해킹이지만 작동합니다! ;)


0

당신은 또한 사용자 @RequestBody Map<String, String> params, 그런 다음 params.get("key")매개 변수의 가치를 얻기 위하여 사용할 수 있습니다


0

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");
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.