尝试使用 Spring 引导 REST 从 POST 读取 JSON 字符串

我使用最新版本的 Spring Boot 通过 Restful Web Service 读取示例 JSON..。

下面是我的 pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">


<modelVersion>4.0.0</modelVersion>


<groupId>org.springframework</groupId>
<artifactId>myservice</artifactId>
<version>0.1.0</version>


<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.2.RELEASE</version>
</parent>


<properties>
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>


<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
</dependency>
<dependency>
</dependencies>


<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>


<repositories>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/libs-release</url>
</repository>
<repository>
<id>org.jboss.repository.releases</id>
<name>JBoss Maven Release Repository</name>
<url>https://repository.jboss.org/nexus/content/repositories/releases</url>
</repository>
</repositories>


<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>


</project>

以下是我的 Web 服务代码:

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/myservice")
public class BaseService {


@RequestMapping(value="/process", method = RequestMethod.POST)
public void process(@RequestBody String payload) throws Exception {
System.out.println(payload);
}
}

当我使用以下命令调用它时:

curl -H "Accept: application/json" -H "Content-type: application/json"
-X POST -d '{"name":"value"}' http://localhost:8080/myservice/process

我得到这个错误消息:

{"timestamp":1427515733546,"status":400,
"error":"Bad Request",


"exception":
"org.springframework.http.converter.HttpMessageNotReadableException","
message":
"Could not read JSON: Can not deserialize instance of java.lang.String
out of START_OBJECT token\n at


[Source: java.io.PushbackInputStream@8252f; line: 1, column: 1];
nested    exception is com.fasterxml.jackson.databind.JsonMappingException:
Can not deserialize instance of java.lang.String out of START_OBJECT token\n
at [Source: java.io.PushbackInputStream@8252f; line: 1, column: 1]",
"path":"/myservice/process"

我试图做的唯一一件事是传入一些有效的 JSON (通过 curl 作为字符串) ,并查看 String 有效负载是否以{“ name”: “ value”}的形式输入流程方法

我到底做错了什么?

谢谢你花时间读这封信。

299334 次浏览

The issue appears with parsing the JSON from request body, tipical for an invalid JSON. If you're using curl on windows, try escaping the json like -d "{"name":"value"}" or even -d "{"""name""":"value"""}"

On the other hand you can ommit the content-type header in which case whetewer is sent will be converted to your String argument

I think the simplest/handy way to consuming JSON is using a Java class that resembles your JSON: https://stackoverflow.com/a/6019761

But if you can't use a Java class you can use one of these two solutions.

Solution 1: you can do it receiving a Map<String, Object> from your controller:

@RequestMapping(
value = "/process",
method = RequestMethod.POST)
public void process(@RequestBody Map<String, Object> payload)
throws Exception {


System.out.println(payload);


}

Using your request:

curl -H "Accept: application/json" -H "Content-type: application/json" \
-X POST -d '{"name":"value"}' http://localhost:8080/myservice/process

Solution 2: otherwise you can get the POST payload as a String:

@RequestMapping(
value = "/process",
method = RequestMethod.POST,
consumes = "text/plain")
public void process(@RequestBody String payload) throws Exception {


System.out.println(payload);


}

Then parse the string as you want. Note that must be specified consumes = "text/plain" on your controller. In this case you must change your request with Content-type: text/plain:

curl -H "Accept: application/json" -H "Content-type: text/plain" -X POST \
-d '{"name":"value"}' http://localhost:8080/myservice/process

To add on to Andrea's solution, if you are passing an array of JSONs for instance

[
{"name":"value"},
{"name":"value2"}
]

Then you will need to set up the Spring Boot Controller like so:

@RequestMapping(
value = "/process",
method = RequestMethod.POST)
public void process(@RequestBody Map<String, Object>[] payload)
throws Exception {


System.out.println(payload);


}

To further work with array of maps, the followings could help:

@RequestMapping(value = "/process", method = RequestMethod.POST, headers = "Accept=application/json")
public void setLead(@RequestBody Collection<? extends Map<String, Object>> payload) throws Exception {


List<Map<String,Object>> maps = new ArrayList<Map<String,Object>>();
maps.addAll(payload);


}

To receive arbitrary Json in Spring-Boot, you can simply use Jackson's JsonNode. The appropriate converter is automatically configured.

    @PostMapping(value="/process")
public void process(@RequestBody com.fasterxml.jackson.databind.JsonNode payload) {
System.out.println(payload);
}

Jackson Library is what we need for Inject a Json string directly into the response without any extra parsing.

In cases where we have an already-escaped property and need to serialize it without any further escaping, we may want to use Jackson’s @JsonRawValue annotation on that field.

also in its documents has pointed :

Marker annotation that indicates that the annotated method or field should be serialized by including literal String value of the property as is, without quoting of characters. This can be useful for injecting values already serialized in JSON or passing javascript function definitions from server to a javascript client. Warning: the resulting JSON stream may be invalid depending on your input value.

Here is my DTO class:

public class UserProfileResponse {


private Long id;


private String userName;


@JsonRawValue
private String profile;


@JsonRawValue
private String settings;
}

without using @JsonRawValue my response would be something as below, because my profile and settings fields are fetching as JSON from my Database(Oracle for example), and spring serialize them again when wants to create a response for client:

{
"id": 25,
"userName": "admin",
"profile": "{ \"gender\": \"male\", \"nationalId\": \"0123456789\", \"contacts\": { \"phone\": \"02112341234\" } }",
"settings": "{\"lang\":\"fa-IR\",\"cols\":[],\"watch_lists\":{\"list_2\":[\"2\",\"3\"],\"list_1\":[\"1\",\"2\",\"3\"]}}"
}

but when I place @JsonRawValue on those variables, the response would serialize Like this:

{
"id": 25,
"userName": "admin",
"profile": {
"gender": "male",
"nationalId": "0123456789",
"contacts": {
"phone": "02112341234"
}
},
"settings": {
"lang": "fa-IR",
"cols": [],
"watch_lists": {
"list_2": [
"2",
"3"
],
"list_1": [
"1",
"2",
"3"
]
}
}
}