@RequestBody'deki birden çok değişkeni Ajax kullanarak Spring MVC denetleyicisine iletme


113

Bir destek nesnesine sarılması gerekli midir? Bunu yapmak istiyorum:

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

Ve bunun gibi bir JSON kullanın:

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

Ama bunun yerine şunu kullanmalıyım:

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

Ve sonra bu JSON'u kullanın:

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

Bu doğru mu? Benim diğer seçenek değiştirmektir RequestMethodiçin GETve kullanım @RequestParamsorgu dizesi veya kullanımda @PathVariablebiriyle RequestMethod.

Yanıtlar:


92

Haklısınız, @RequestBody ek açıklamalı parametresinin isteğin tüm gövdesini tutması ve tek bir nesneye bağlanması beklenir, bu nedenle esasen seçeneklerinizle gitmek zorunda kalacaksınız.

Yaklaşımınızı kesinlikle istiyorsanız, yine de yapabileceğiniz özel bir uygulama var:

Bunun senin json olduğunu söyle:

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

ve buradaki iki parametreye bağlamak istiyorsunuz:

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

Öncelikle, @JsonArgistediğiniz bilgiye giden yol gibi JSON yolu ile özel bir açıklama tanımlayın :

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

Şimdi , asıl bağımsız değişkeni çözmek için yukarıda tanımlanan JsonPath'i kullanan bir Custom HandlerMethodArgumentResolver yazın :

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

    }
}

Şimdi bunu Spring MVC'ye kaydettirin. Biraz karıştı, ancak bu temiz bir şekilde çalışmalı.


2
Nasıl özel not oluşturabilirim, @JsonArg deyin lütfen?
Surendra Jnawali

Bu neden? şimdi arka uçta birçok farklı sarmalayıcı sınıfı oluşturmam gerekiyor. Bir Struts2 uygulamasını Springboot'a geçiriyorum ve ajax kullanılarak gönderilen JSON nesnelerinin aslında modelin iki veya daha fazla nesnesi olduğu birçok durum vardı: örneğin bir Kullanıcı ve Etkinlik
Jose Ospina

bu bağlantı size "bunu Spring MVC'ye
Bodil

3
hala bu seçeneğin neden bahara eklenmediğini merak ediyor. 2 uzun
süreniz olduğunda

@SurendraJnawali bunu böyle yapabilirsin@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface JsonArg { String value() default ""; }
Epono

88

@RequestBodyTek bir nesneye eşlenmesi gerektiği doğru olsa da , bu nesne bir olabilir Map, bu nedenle bu size ulaşmak istediğiniz şey için iyi bir yol sağlar (tek seferlik bir arka plan nesnesi yazmaya gerek yoktur):

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

Tam bir JSON ağacı istiyorsanız , Jackson'ın ObjectNode'una da bağlanabilirsiniz :

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

@JoseOspina neden bunu yapamıyorum. RequestBody ile Harita <dize, Nesne> ile ilişkili herhangi bir risk
Ben Cheng

@Ben Demek istediğim Map, içindeki herhangi bir sayıda nesneyi saklamak için BİR tek nesne kullanabilirsiniz , ancak üst düzey nesne yine de yalnızca bir olmalıdır, iki üst düzey nesne olamaz.
Jose Ospina

1
Sanırım dinamik bir yaklaşımın dezavantajı Map<String, String>şudur: API dokümantasyon kitaplıkları (swagger / springfox vb.) Muhtemelen istek / yanıt şemanızı kaynak kodunuzdan ayrıştıramaz.
stratovarius

10

Daha basit veri türleri için gövde ve yol değişkenini kullanarak sonrası bağımsız değişkenini karıştırabilirsiniz:

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

10

Birden çok nesneyi, parametreleri, değişkenleri vb. İletmek için. Bunu, parametreniz olarak jackson kitaplığındaki ObjectNode'u kullanarak dinamik olarak yapabilirsiniz. Bunu şu şekilde yapabilirsiniz:

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

Umarım bu yardımcı olur.


2

@RequestParamolduğu HTTP GETveya POSTistemci tarafından gönderilen parametre değişkeni var URL bir kesimi, istek haritalama edilir:

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

var1& var2istek parametreleridir.

http:/host/form/{params}

{params}bir istek eşlemesidir. servisinizi şu şekilde arayabilirsiniz: http:/host/form/userveya http:/host/form/firm firma ve kullanıcının kullanıldığı yer Pathvariable.


bu soruya cevap vermiyor ve yanlış, POST istekleriyle bir sorgu dizesi
kullanmıyorsunuz

1
@NimChimpsky: Elbette yapabilirsin. POST isteği yine de URL’de parametreler içerebilir.
Martijn Pieters

2

Kolay çözüm, nitelik olarak str1 ve str2'ye sahip bir yük sınıfı oluşturmaktır:

@Getter
@Setter
public class ObjHolder{

String str1;
String str2;

}

Ve sen geçtikten sonra

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

ve isteğinizin ana metni:

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

1
Bu ek açıklamaların paketi nedir? Autoimport yalnızca jdk.nashorn.internal.objects.annotations.Setter içe aktarmayı teklif etti; DÜZENLE. Lombok projectlombok.org/features/GetterSetter olduğunu varsayıyorum . Yanılıyorsam lütfen beni düzeltin
Gleichmut

@Gleichmut değişkenleriniz için basit alıcılar ve ayarlayıcılar kullanabilirsiniz. Beklediğiniz gibi çalışacak.
04'te ver

1

Json kullanmak yerine basit bir şey yapabilirsiniz.

$.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.>
                });

Şimdi denetleyicide ajax isteğini aşağıdaki gibi eşlemeniz gerekir:

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

Umarım bu size yardımcı olur.


1
Bu json ve işe yaramıyor. Yöntemde requestparam'ı belirliyorsunuz, ancak equestbody'yi ajax gönderi isteğinde json ile tanımlıyorsunuz.
NimChimpsky

Bkz. Ajax çağrısında JSON formatını kullanmadım. Basitçe iki istek parametresi kullandım ve denetleyicide bu parametreleri @RequestParam açıklama ile alabiliriz. Çalışıyor. Bunu kullanıyorum. Sadece bir dene.
Japan Trivedi

Bunu denedim, sorgunun amacı bu. Böyle çalışmıyor.
NimChimpsky

Lütfen tam olarak ne denediğinizi belirtin. Bunu sorunuzda gösterin. Sanırım anladığımdan farklı bir ihtiyacın var.
Japonya Trivedi

1
İlk denemede benim için çalıştı. Teşekkürler!
Humppakäräjät

1

Biju'nun çözümünü uyarladım:

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;

    }

}

Farklı olan nedir:

  • Jackson'ı json dönüştürmek için kullanıyorum
  • Ek açıklamada bir değere ihtiyacım yok, parametrenin adını MethodParameter'dan okuyabilirsiniz.
  • Metodparametresinden parametrenin türünü de okudum => bu yüzden çözüm genel olmalıdır (string ve DTO'lar ile test ettim)

BR


0

istek parametresi hem GET hem de POST için mevcut, Almak için URL'ye sorgu dizesi olarak eklenecek, ancak POST için İstek Gövdesi içindedir


0

Json'u nereye eklediğinizden emin değilim, ancak bunu açısal olarak yaparsam, requestBody: angluar:

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

0

İyi. İhtiyacınız olan alanları içeren bir Değer Nesnesi (Vo) oluşturmanızı öneririm. Kod daha basit, Jackson'ın işleyişini değiştirmiyoruz ve anlaşılması daha da kolay. Saygılarımızla!


0

Kullanarak istediğinizi elde edebilirsiniz @RequestParam. Bunun için aşağıdakileri yapmalısınız:

  1. Nesnelerinizi temsil eden RequestParams parametrelerini bildirin ve requiredboş bir değer gönderebilmek istiyorsanız seçeneği false olarak ayarlayın .
  2. Ön uçta, göndermek istediğiniz nesneleri dizge haline getirin ve bunları istek parametreleri olarak dahil edin.
  3. Arka uçta JSON dizelerini Jackson ObjectMapper veya benzeri bir şey kullanarak temsil ettikleri nesnelere geri döndürün ve işte!

Biliyorum, biraz hack ama işe yarıyor! ;)


0

ayrıca user @RequestBody Map<String, String> params, ardından params.get("key")parametrenin değerini almak için kullanabilirsiniz


0

Ayrıca requestBody'yi tutmak için bir MultiValue Haritası da kullanabilirsiniz. İşte bunun örneği.

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

İstek gövdesini tutmak için bir Harita kullanırken @RequestBody ek açıklamasından farklı olarak @RequestParam ile açıklama eklememiz gerekir

ve kullanıcıyı Json RequestBody'de gönderin

  @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");
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.