web-dev-qa-db-fra.com

Passage de plusieurs variables de @RequestBody à un contrôleur Spring MVC à l'aide d'Ajax

Est-il nécessaire d'envelopper un objet de support? Je veux faire ça:

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

Et utilisez un JSON comme ceci:

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

Mais au lieu de cela, je dois utiliser: 

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

Et puis utilisez ce JSON:

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

Est-ce exact? Mon autre option serait de changer la RequestMethod en GET et d'utiliser @RequestParam dans la chaîne de requête ou d'utiliser @PathVariable avec soit RequestMethod

79
NimChimpsky

Vous avez raison, le paramètre annoté @RequestBody est censé contenir tout le corps de la demande et se lier à un seul objet. Vous devrez donc choisir vos options.

Si vous voulez absolument votre approche, vous pouvez faire une implémentation personnalisée:

Dis que c'est ton json:

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

et vous voulez le lier aux deux params ici:

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

Commencez par définir une annotation personnalisée, par exemple @JsonArg, avec le chemin JSON comme chemin d'accès aux informations souhaitées:

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

Maintenant, écrivez un Custom HandlerMethodArgumentResolver qui utilise le JsonPath défini ci-dessus pour résoudre l’argument actuel:

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

    }
}

Maintenant, enregistrez ceci avec Spring MVC. Un peu impliqué, mais cela devrait fonctionner proprement.

69
Biju Kunjummen

S'il est vrai que @RequestBody doit mapper sur un seul objet, cet objet peut être une Map, ce qui vous permet d'obtenir un bon moyen d'atteindre vos objectifs (inutile d'écrire un objet unique):

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

Vous pouvez également vous connecter au ObjectNode de Jackson si vous voulez une arborescence JSON complète:

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

Vous pouvez mélanger l'argument post en utilisant la variable body et path pour des types de données plus simples:

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

@RequestParam est le paramètre HTTP GET ou POST envoyé par le client, le mappage de demande est un segment d'URL dont la variable est:

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

var1 & var2 sont des paramètres de demande.

http:/Host/form/{params}

{params} est un mappage de demande. vous pouvez appeler votre service comme suit: http:/Host/form/user ou http:/Host/form/firm où firm et user sont utilisés comme Pathvariable.

2
psisodia

Pour transmettre plusieurs objets, paramètres, variables, etc. Vous pouvez le faire dynamiquement en utilisant ObjectNode à partir de la bibliothèque jackson comme paramètre. Vous pouvez le faire comme ça:

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

J'espère que cette aide.

1
azwar_akbar

Vous pouvez obtenir ce que vous voulez en utilisant @RequestParam. Pour cela, vous devriez faire ce qui suit:

  1. Déclarez les paramètres RequestParams qui représentent vos objets et définissez l'option required sur false si vous souhaitez pouvoir envoyer une valeur null.
  2. Sur le frontend, indiquez les objets à envoyer et incluez-les en tant que paramètres de requête.
  3. Sur le backend, restituez les chaînes JSON dans les objets qu’elles représentent à l’aide de Jackson ObjectMapper ou quelque chose du genre, et le tour est joué!

Je sais, c'est un peu un bidouillage mais ça marche! ;)

0
Maurice

Je ne sais pas où vous ajoutez le JSON mais si je le fais comme ça avec angular cela fonctionne sans le 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);
}
0
tibi

Bon .. Je suggère de créer un objet de valeur (Vo) contenant les champs dont vous avez besoin. Le code est plus simple, nous ne changeons pas le fonctionnement de Jackson et il est encore plus facile à comprendre . Cordialement!

0
Matias Zamorano

vous pouvez également utiliser @RequestBody Map<String, String> params, puis utiliser params.get("key") pour obtenir la valeur du paramètre

0
Will

le paramètre request existe pour GET et POST. Pour Get, il sera ajouté en tant que chaîne de requête à l'URL, mais pour POST, il figure dans le corps de la requête.

0
Kaleem

J'ai adapté la solution de 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;

    }

}

Quelle est la différence:

  • J'utilise Jackson pour convertir json
  • Je n'ai pas besoin d'une valeur dans l'annotation, vous pouvez lire le nom du paramètre Dans le MethodParameter
  • Je lis aussi le type du paramètre dans MethodParameter =>, donc la solution doit être générique (je l'ai testée avec des chaînes et des DTO)

BR

0
user3227576

Au lieu d'utiliser JSON, vous pouvez faire quelque chose de simple.

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

Maintenant, dans le contrôleur, vous devez mapper la demande ajax comme ci-dessous:

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

J'espère que cela vous aide.

0
Japan Trivedi