I want to get a map schema in components, e.g.
components:
schemas:
Response:
type: object
additionalProperties:
type: string
Here is my code:
#Bean
public OpenAPI customOpenAPI() {
MapSchema mySchema = new MapSchema();
mySchema.setAdditionalProperties(new StringSchema());
return new OpenAPI().components(new Components()
.addSchemas("Response", mySchema)
);
}
But it will raise an exception:
{"timestamp":"2022-11-24 14:23:54.686","component":"","level":"WARN","thread-id":"http-nio-8080-exec-1","logger":"org.springdoc.core.OpenAPIService","message":"Json Processing Exception occurred: Cannot deserialize value of type `java.lang.Boolean` from Object value (token `JsonToken.START_OBJECT`)
at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: io.swagger.v3.oas.models.OpenAPI["components"]->io.swagger.v3.oas.models.Components["schemas"]->java.util.LinkedHashMap["Response"])"}
{"timestamp":"2022-11-24 14:23:54.711","component":"","level":"ERROR","thread-id":"http-nio-8080-exec-1","logger":"org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet]","message":"Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause"}
java.lang.NullPointerException: null
at java.base/java.util.Objects.requireNonNull(Objects.java:208)
at org.springdoc.core.OpenAPIService.buildOpenAPIWithOpenAPIDefinition(OpenAPIService.java:520)
at org.springdoc.core.OpenAPIService.build(OpenAPIService.java:259)
at org.springdoc.api.AbstractOpenApiResource.getOpenApi(AbstractOpenApiResource.java:314)
at org.springdoc.webmvc.api.OpenApiResource.openapiYaml(OpenApiResource.java:156)
at org.springdoc.webmvc.api.OpenApiWebMvcResource.openapiYaml(OpenApiWebMvcResource.java:133)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
If I changed the code to
#Bean
public OpenAPI customOpenAPI() {
MapSchema mySchema = new MapSchema();
mySchema.setAdditionalProperties(false); <-- I pass a boolean value instead of a schema
return new OpenAPI().components(new Components()
.addSchemas("Response", mySchema)
);
}
SpringDoc can generate the swagger doc successfully.
components:
schemas:
Response:
type: object
additionalProperties: false
Can you help to check what's wrong with my previous code ? Thanks.
I'm using spring-boot 2.7.5 and
org.springdoc/springdoc-openapi-webmvc-core 1.6.11
i am sending post request through postman request to my spring boot application but it encounters the following error message
2022-08-03 11:30:51.637 WARN 7140 --- [nio-9002-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unexpected character (' ' (code 160)): was expecting double-quote to start field name; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character (' ' (code 160)): was expecting double-quote to start field name<EOL> at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 2, column: 3]]
this is my postman request
{ "operationClient": "03", "module": "03", "moduleName": "ECH", "operation": "10", "ip": "127.0.0.1", "channelId": "5", "connectionType": "1" }
my API call request
localhost:9002/cms/online/v1/channel/operation
my controller class of spring-boot
#PostMapping(value = "/operation", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResponseBean> doChannelOperation(#RequestBody #Valid RequestBean requestBean) throws Exception {
System.out.println(requestBean.toString());
ResponseBean responseBean = services.sendRequest(requestBean);
return new ResponseEntity<>(responseBean, HttpStatus.OK);
}
io.restassured.path.json.exception.JsonPathException: Failed to parse
the JSON document
It is throwing error in the code written for these Feature file steps:
Given Add new location with "<name>" "<language>" "<address>" parameters
Then user calls "AddPlaceAPI" with http "POST" method
Then API successful response with "status" as "OK"
The stepDefinition code for these steps
#Given("Add new location with {string} {string} {string} parameters")
public void Add_new_location_with_three_parameters(String name_G,String address_G,String language_G) throws IOException {
res=given().spec(requestSpecification())
.body(data.googleAddPlace(name_G,address_G,language_G));
}
#Then("user calls {string} with http {string} method")
public void user_calls_with_http_method(String resource_G,String http_method)
{
APIResources resourceAPI = APIResources.valueOf(resource_G);
System.out.println("Resource value" + resourceAPI.getResource());
resspec =new ResponseSpecBuilder().expectStatusCode(200).expectContentType(ContentType.JSON).build();
if(http_method.equalsIgnoreCase("POST"))
response= res.when().post(resourceAPI.getResource());
else if(http_method.equalsIgnoreCase("GET"))
response= res.when().post(resourceAPI.getResource());
}
#Then("API successful response with {string} as {string}")
public void api_successful_response_with_as(String string, String string2) {
// Write code here that turns the phrase above into concrete actions
assertEquals(getJsonPath(response,string),string2);
}
getJsonPath method Definition where it is throwing Error
public String getJsonPath(Response response,String key)
{
String resp=response.asString();
JsonPath js = new JsonPath(resp);
return js.get(key).toString();
}
enter image description here
Junit Error Log
io.restassured.path.json.exception.JsonPathException: Failed to parse the JSON document
at io.restassured.path.json.JsonPath$ExceptionCatcher.invoke(JsonPath.java:1002)
at io.restassured.path.json.JsonPath$4.doParseWith(JsonPath.java:967)
at io.restassured.path.json.JsonPath$JsonParser.parseWith(JsonPath.java:1047)
at io.restassured.path.json.JsonPath.get(JsonPath.java:202)
at resources.Utils.getJsonPath(Utils.java:56)
at stepDefinations.stepDefination.api_successful_response_with_as(stepDefination.java:117)
at ✽.API successful response with "status" as "OK"(file:///C:/Users/gaura/eclipse-workspaceNew/APIFramework/src/test/java/features/googlemapsValidation.feature:6)
Caused by: groovy.json.JsonException: Lexing failed on line: 1, column: 1, while reading '<', no possible valid JSON value or
punctuation could be recognized.
at groovy.json.JsonLexer.nextToken(JsonLexer.java:86)
at groovy.json.JsonLexer$nextToken.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
at io.restassured.internal.path.json.ConfigurableJsonSlurper.parse(ConfigurableJsonSlurper.groovy:97)
at io.restassured.internal.path.json.ConfigurableJsonSlurper$parse.callCurrent(Unknown
Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:171)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:185)
at io.restassured.internal.path.json.ConfigurableJsonSlurper.parseText(ConfigurableJsonSlurper.groovy:83)
at io.restassured.path.json.JsonPath$4$1.method(JsonPath.java:965)
at io.restassured.path.json.JsonPath$ExceptionCatcher.invoke(JsonPath.java:1000)
at io.restassured.path.json.JsonPath$4.doParseWith(JsonPath.java:967)
at io.restassured.path.json.JsonPath$JsonParser.parseWith(JsonPath.java:1047)
at io.restassured.path.json.JsonPath.get(JsonPath.java:202)
at resources.Utils.getJsonPath(Utils.java:56)
at stepDefinations.stepDefination.api_successful_response_with_as(stepDefination.java:117)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown
Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown
Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at io.cucumber.java.Invoker.invoke(Invoker.java:27)
at io.cucumber.java.JavaStepDefinition.execute(JavaStepDefinition.java:27)
at io.cucumber.core.runner.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:63)
at io.cucumber.core.runner.TestStep.executeStep(TestStep.java:64)
at io.cucumber.core.runner.TestStep.run(TestStep.java:49)
at io.cucumber.core.runner.PickleStepTestStep.run(PickleStepTestStep.java:46)
at io.cucumber.core.runner.TestCase.run(TestCase.java:51)
at io.cucumber.core.runner.Runner.runPickle(Runner.java:66)
at io.cucumber.junit.PickleRunners$NoStepDescriptions.run(PickleRunners.java:149)
at io.cucumber.junit.FeatureRunner.runChild(FeatureRunner.java:83)
at io.cucumber.junit.FeatureRunner.runChild(FeatureRunner.java:24)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at io.cucumber.junit.Cucumber.runChild(Cucumber.java:185)
at io.cucumber.junit.Cucumber.runChild(Cucumber.java:83)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at io.cucumber.junit.Cucumber$RunCucumber.evaluate(Cucumber.java:219)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
I tried combination of following versions for required jar files in pom.xml file and its working fine for me. You can also try the same:
JUnit- version: 4.1.2
Cucumber - java - info.cukes - version: 1.2.4
Cucumber - junit - info.cukes - version: 1.2.3
Cucumber - pico container - info.cukes - version: 1.2.4
Cucumber - core - info.cukes - version: 1.2.6
Cucumber - jvm deps - info.cukes - version: 1.0.5
Rest Assurd - version: 3.0.2
I'm creating a GAE project with Spring which will also use cloud SQL. While testing this app in local I'm pointing to my local MySQL environment but when I'll deploy it to GAE, it will point to cloud SQL instance. So I want to configure my driverName in datasource bean depending on environment. To this we generally use following in our java code
if (SystemProperty.environment.value() == SystemProperty.Environment.Value.Production) {
// Load the class that provides the new "jdbc:google:mysql://" prefix.
Class.forName("com.mysql.jdbc.GoogleDriver");
url = "jdbc:google:mysql://<your-project-id>:<your-instance-name>/<your-database-name>?user=root";
} else {
// Local MySQL instance to use during development.
Class.forName("com.mysql.jdbc.Driver");
url = "jdbc:mysql://127.0.0.1:3306/<your-database-name>?user=root";
}
Now I want to achieve the same in applicationcontext.xml using Spring Expression language. I haven't done this before and not able to achieve it. Please guide me. This is what I tried
<bean id="isDev" class="java.lang.Boolean">
<constructor-arg value="#{ systemProperties[environment.value] == SystemProperty.Environment.Value.Development ? true : false }" />
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="#{ isDev ? com.mysql.jdbc.Driver : com.mysql.jdbc.GoogleDriver }" />
.
.
.
But I'm getting exception, attaching a part of exception
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'feedbackFormController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.sandeepapplabs.custengage.services.FeedbackFormService com.sandeepapplabs.custengage.controllers.FeedbackFormController.feedbackFormService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'feedbackFormService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.sandeepapplabs.custengage.daos.FeedbackFormDAO com.sandeepapplabs.custengage.services.impl.FeedbackFormServiceImpl.feedbackFormDAO; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'feedbackFormDAO' defined in ServletContext resource [/WEB-INF/custengage-servlet.xml]: Cannot resolve reference to bean 'dataSource' while setting bean property 'dataSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in ServletContext resource [/WEB-INF/custengage-servlet.xml]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is java.lang.NullPointerException
I believe you need to quote the class names to make them literals...
... 'com.mysql.jdbc.Driver' ...
However it is easier to use Spring Profiles](http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/beans.html#beans-definition-profiles-xml) and enable the profile you want.
You can use Spring profiles to change concrete bean classes. Let's say you have 2 profiles: "prod" and "dev". Your bean methods should have #Profile annotation as below:
#Configuration
public class AppConfig {
...
#Bean
#Profile("prod")
public Object prodDataSource() {
return new ...
}
#Bean
#Profile("dev")
public Object getDataSource() throws Exception {
return new ...
}
}
If you are using Maven, you can select profile through pom.xml:
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<spring.profile>dev</spring.profile>
</properties>
</profile>
<profile>
<id>prod</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>prod</name>
</property>
</activation>
<properties>
<spring.profile>prod</spring.profile>
</properties>
</profile>
</profiles>
You can pass profile selection argument by -D like: mvn -Dprod clean compile test install
EDIT:
In application.properties following property selects profile:
spring.profiles.active=${spring.profile}
I'm creating a REST API that will accept JSON requests.
I'm testing it out using CURL:
curl -i -POST -H 'Accept: application/json' -d '{"id":1,"pan":11111}' http://localhost:8080/PurchaseAPIServer/api/purchase
But getting the following error:
HTTP/1.1 415 Unsupported Media Type
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 1051
Date: Wed, 25 Apr 2012 21:36:14 GMT
The server refused this request because the request entity is in a format not supported by the requested resource for the requested method ().
When debugging it never even gets into my create action in the controller.
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
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.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.app.model.Purchase;
import com.app.service.IPurchaseService;
#Controller
public class PurchaseController {
#Autowired
private IPurchaseService purchaseService;
#RequestMapping(value = "purchase", method = RequestMethod.GET)
#ResponseBody
public final List<Purchase> getAll() {
return purchaseService.getAll();
}
#RequestMapping(value = "purchase", method = RequestMethod.POST)
#ResponseStatus( HttpStatus.CREATED )
public void create(#RequestBody final Purchase entity) {
purchaseService.addPurchase(entity);
}
}
UPDATE
I added Jackson config to AppConfig.java:
#Configuration
#ComponentScan(basePackages = "com.app")
public class AppConfig {
#Bean
public AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter()
{
final AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter = new AnnotationMethodHandlerAdapter();
final MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();
HttpMessageConverter<?>[] httpMessageConverter = { mappingJacksonHttpMessageConverter };
String[] supportedHttpMethods = { "POST", "GET", "HEAD" };
annotationMethodHandlerAdapter.setMessageConverters(httpMessageConverter);
annotationMethodHandlerAdapter.setSupportedMethods(supportedHttpMethods);
return annotationMethodHandlerAdapter;
}
}
My GETs are working correctly now:
curl -i -H "Content-Type:application/json" -H "Accept:application/json" http://localhost:8080/PurchaseAPIServer/api/purchase
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 26 Apr 2012 21:19:55 GMT
[{"id":1,"pan":111}]
But I get the following when attempting a POST:
curl -i -X POST -H "Content-Type:application/json" -H "Accept:application/json" http://localhost:8080/PurchaseAPIServer/api/purchaseMe -d "{"id":2,"pan":122}"
HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 971
Date: Thu, 26 Apr 2012 21:29:56 GMT
Connection: close
The request sent by the client was syntactically incorrect ().
My Model:
#Entity
#XmlRootElement
public class Purchase implements Serializable {
/**
*
*/
private static final long serialVersionUID = 6603477834338392140L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long pan;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getPan() {
return pan;
}
public void setPan(Long pan) {
this.pan = pan;
}
}
Any ideas where I'm going wrong?
Thanks
As sdouglass suggested, Spring MVC automatically detects Jackson and sets up a MappingJacksonHttpMessageConverter to handle conversion to/from JSON. But I did need explicity configure the converter to get it to work as he also pointed out.
I added the following and my CURL GET requests were working..Hooray.
AppConfig.java
#Configuration
#ComponentScan(basePackages = "com.app")
public class AppConfig {
#Bean
public AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter()
{
final AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter = new AnnotationMethodHandlerAdapter();
final MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();
HttpMessageConverter<?>[] httpMessageConverter = { mappingJacksonHttpMessageConverter };
String[] supportedHttpMethods = { "POST", "GET", "HEAD" };
annotationMethodHandlerAdapter.setMessageConverters(httpMessageConverter);
annotationMethodHandlerAdapter.setSupportedMethods(supportedHttpMethods);
return annotationMethodHandlerAdapter;
}
}
curl -i -H "Content-Type:application/json" -H "Accept:application/json" http://localhost:8080/PurchaseAPIServer/api/purchase
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 26 Apr 2012 21:19:55 GMT
[{"id":1,"pan":111}]
But the following CURL POST was still not working (Never hitting the controller action and giving no console debug info.
curl -i -X POST -H "Content-Type:application/json" http://localhost:8080/PurchaseAPIServer/api/purchaseMe -d "{"id":2,"pan":122}"
HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 971
Date: Thu, 26 Apr 2012 21:29:56 GMT
Connection: close
The request sent by the client was syntactically incorrect ().
So I added Logback to get some detailed debugging started.
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/home/thomas/springApps/purchaseapi.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n
</pattern>
</encoder>
</appender>
<logger name="org.hibernate" level="DEBUG" />
<logger name="org.springframework" level="TRACE" />
<logger name="org.springframework.transaction" level="INFO" />
<logger name="org.springframework.security" level="INFO" /> <!-- to debug security related issues (DEBUG) -->
<logger name="org.springframework.web.servlet.mvc" level="TRACE" /> <!-- some serialization issues are at trace level here: org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod -->
<!-- our service -->
<logger name="com.app" level="DEBUG" />
<!-- <logger name="com.app" level="INFO" /> --><!-- to follow if setup is being executed -->
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</configuration>
Adding TRACE level debugging to org.springframework.web.servlet.mvc gave me the answer to the problem.
2012-04-28 14:17:44,579 DEBUG [http-bio-8080-exec-3] o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor [AbstractMessageConverterMethodArgumentResolver.java:117] Reading [com.app.model.Purchase] as "application/json" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter#74a14fed]
2012-04-28 14:17:44,604 TRACE [http-bio-8080-exec-3] o.s.w.s.m.m.a.ServletInvocableHandlerMethod [InvocableHandlerMethod.java:159] Error resolving argument [0] [type=com.app.model.Purchase]
HandlerMethod details:
Controller [com.app.controller.PurchaseController]
Method [public void com.app.controller.PurchaseController.create(com.app.model.Purchase)]
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unexpected character ('p' (code 112)): was expecting double-quote to start field name
I changed my CURL POSTs to the following an it all worked:
curl -i -X POST -H "Content-Type:application/json" http://localhost:8080/PurchaseAPIServer/api/purchase -d '{"pan":11111}'
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Content-Length: 0
Date: Sat, 28 Apr 2012 13:19:40 GMT
Hopefully someone finds this useful.
If I recall correctly the Spring docs say that Spring MVC will automatically detect Jackson on the classpath and set up a MappingJacksonHttpMessageConverter to handle conversion to/from JSON, but I think I have experienced situations where I had to manually/explictly configure that converter to get things to work. You may want to try adding this to your MVC config XML:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
</list>
</property>
</bean>
UPDATE: It was this plus properly formatting the JSON being posted, see https://stackoverflow.com/a/10363876/433789
Its 2014 and I wanted to add a few updates to this question which helped me solve the same problem.
Code update to replace deprecated AnnotationMethodHandlerAdapter in Spring 3.2
#Configuration
public class AppConfig {
#Bean
public RequestMappingHandlerAdapter annotationMethodHandlerAdapter()
{
final RequestMappingHandlerAdapter annotationMethodHandlerAdapter = new RequestMappingHandlerAdapter();
final MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJackson2HttpMessageConverter();
List<HttpMessageConverter<?>> httpMessageConverter = new ArrayList<HttpMessageConverter<?>>();
httpMessageConverter.add(mappingJacksonHttpMessageConverter);
String[] supportedHttpMethods = { "POST", "GET", "HEAD" };
annotationMethodHandlerAdapter.setMessageConverters(httpMessageConverter);
annotationMethodHandlerAdapter.setSupportedMethods(supportedHttpMethods);
return annotationMethodHandlerAdapter;
}
}
HTTP/1.1 415 Unsupported Media Type error
After spending many hours trying to figure out why I am STILL GETTING a 415 error even after adding the correct JSON configuration I finally realized that the problem was NOT with the server side but with the client side. In order for Spring to accept your JSON you MUST make sure that you are sending both "Content-Type : application/json" and "Accept: application/json" as part of your http headers. for me specifically it was an android application HttpUrlConnection which I had to set as:
public static String doPost(final String urlString,final String requestBodyString) throws IOException {
final URL url = new URL(urlString);
final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Accept", "application/json");
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
urlConnection.setChunkedStreamingMode(0);
urlConnection.connect();
final PrintWriter out = new PrintWriter(urlConnection.getOutputStream());
out.print(requestBodyString);
out.close();
final InputStream in = new BufferedInputStream(urlConnection.getInputStream());
final String response = readIt(in);
in.close(); //important to close the stream
return response;
} finally {
urlConnection.disconnect();
}
}
Try adding a descriptor of what's in your POST request. That is, add to curl the header:
Content-Type: application/json
If you don't add it, curl will use the default text/html regardless of what you actually send.
Also, in PurchaseController.create() you have to add that the type accepted is application/json.
I had the same problem, which was solved by two changes in my code :
Missing #PathVariable in my method argument, my method didn't have any
Following method in my SpringConfig class since the one I had with handler interceptor was deprecated and giving some issue:
public RequestMappingHandlerAdapter RequestMappingHandlerAdapter()
{
final RequestMappingHandlerAdapter requestMappingHandlerAdapter = new RequestMappingHandlerAdapter();
final MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();
final String[] supportedHttpMethods = { "POST", "GET", "HEAD" };
requestMappingHandlerAdapter.getMessageConverters().add(0, mappingJacksonHttpMessageConverter);
requestMappingHandlerAdapter.setSupportedMethods(supportedHttpMethods);
return requestMappingHandlerAdapter;
}
Here is a unit test solution similar to yoram givon's answer - https://stackoverflow.com/a/22516235/1019307.
public class JSONFormatTest
{
MockMvc mockMvc;
// The controller used doesn't seem to be important though YMMV
#InjectMocks
ActivityController controller;
#Before
public void setup()
{
MockitoAnnotations.initMocks(this);
this.mockMvc = standaloneSetup(controller).setMessageConverters(new MappingJackson2HttpMessageConverter())
.build();
}
#Test
public void thatSaveNewDataCollectionUsesHttpCreated() throws Exception
{
String jsonContent = getHereJSON02();
this.mockMvc
.perform(
post("/data_collections").content(jsonContent).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isCreated());
}
private String getHereJSON01()
{
return "{\"dataCollectionId\":0,\"name\":\"Sat_016\",\"type\":\"httpUploadedFiles\"," ...
}
}
Run the unit test and the print() should print out the MockHttpServletRequest including the Exception.
In Eclipse (not sure about how to do this in other IDEs), click on the Exception link and a properties dialog for that exception should open. Tick the 'enabled' box to break on that exception.
Debug the unit test and Eclipse will break on the exception. Inspecting it should reveal the problem. In my case it was because I had two of the same entity in my JSON.
I experienced once and finally solved it by adding the jar file jackson-mapper-asl.jar. Go check if you have included all these dependencies although the exception itself does not tell u that.
And you really don't need to explicitly configure the bean, and you don't need to put "consumes" in #RequestMapping statement. I'm using Spring 3.1 btw.
contentType : "application/json" is the only one you need to configure. yes, client side.
Try to add the following code in your app configuration
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</mvc:message-converters>
I had the same problem and I resolved it.
1 add MappingJackson2HttpMessageConverter as described in that thread (see also section 4 http://www.baeldung.com/spring-httpmessageconverter-rest)
2 use correct command (with escape symbols):
curl -i -X POST -H "Content-Type:application/json" -d "{\"id\":\"id1\",\"password\":\"password1\"}" http://localhost:8080/user