簡體   English   中英

使用 Ajax 將@RequestBody 中的多個變量傳遞給 Spring MVC 控制器

[英]Passing multiple variables in @RequestBody to a Spring MVC controller using Ajax

是否有必要包裹在支持對象中? 我想做這個:

@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或將@PathVariable與任一RequestMethod一起使用。

你是對的,@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)

現在編寫一個 Custom 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 中注冊它。 有點涉及,但這應該可以正常工作。

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

用於傳遞多個對象、參數、變量等。 您可以使用 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.
}

我希望這會有所幫助。

您可以通過將 body 和 path 變量用於更簡單的數據類型來混合 post 參數:

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

@RequestParam是客戶端發送的HTTP GETPOST參數,請求映射是一段可變的 URL:

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

var1 & var2是請求參數。

http:/host/form/{params}

{params}是一個請求映射。 您可以像這樣調用您的服務: http:/host/form/userhttp:/host/form/firm其中公司和用戶用作Pathvariable

簡單的解決方案是創建一個具有 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"
}

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

希望這對你有幫助。

我已經調整了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

你也可以使用@RequestBody Map<String, String> params ,然后使用params.get("key")來獲取參數的值

您還可以使用 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");
    }

GET 和 POST 都存在請求參數,對於 Get 它將作為查詢字符串附加到 URL 但對於 POST 它在請求正文中

不確定你在哪里添加 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);
}

好的。 我建議創建一個包含您需要的字段的值對象 (Vo)。 代碼更簡單,我們不改變Jackson的功能,更容易理解。 問候!

您可以通過使用@RequestParam來實現您想要的。 為此,您應該執行以下操作:

  1. 如果您希望能夠發送空值,請聲明代表您的對象的 RequestParams 參數並將required選項設置為 false。
  2. 在前端,對要發送的對象進行字符串化並將它們包含為請求參數。
  3. 在后端,使用 Jackson ObjectMapper 或類似的東西將 JSON 字符串轉換回它們代表的對象,瞧!

我知道,它有點黑客,但它有效! ;)

使用內部類

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

如果有人對 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); } }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM