angularjs + spring mvc + json post request - json

I've been doing some research on the web regarding my problem, and I feel that my code reflects what's written in most of the examples - and yet, it still doesn't work. Thus my question.
Long story short, I want to send POST request to add new item to some "backend". For REST API I use Spring MVC, for sending the request I use $http from AngularJS
That's how I invoke the POST request
$scope.testAddItem = function(){
$http({
'url' : 'addNewItem',
'method' : 'POST',
'headers': {'Content-Type' : 'application/json'},
'data' : $scope.newItem
}).success(function(data){
$scope.marketForm.texts.push({'text' : data.text});
})
}
Which maps to the proper URL. Problem is that I get the following response
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<title>Error 400 BAD_REQUEST</title>
</head>
<body><h2>HTTP ERROR 400</h2>
<p>Problem accessing /poll/addNewItem/. Reason:
<pre> BAD_REQUEST</pre></p><hr /><i><small>Powered by Jetty://</small></i><br/>
I get this error either when I click the button on the website, and when I'm testing it via some REST Client.
Dependencies I've included in my pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
One note - sending other request via GET works.
My Controller class
#Controller
#RequestMapping(value = "/")
public class PollController {
#RequestMapping(method = RequestMethod.GET)
public String mainPage(){
return "poll";
}
#RequestMapping(value = "/getNewElement")
public #ResponseBody
List<PollItem> getNewElement(){
List<PollItem> listOfItems = new ArrayList<PollItem>(2);
listOfItems.add(new PollItem("test 1"));
listOfItems.add(new PollItem("test 2"));
return listOfItems;
}
#RequestMapping(value = "/addNewItem", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody PollItem addNewElement(#RequestBody PollItem pollItem){
pollItem.setId(2); // I know it makes no logical sense, but it's just for testing.
return pollItem;
}
}
Any clues ?
EDIT
After adding debug mode it seems that it's some JSON parsing problem
Method [public com.poll.model.PollItem com.poll.controller.PollController.addNewElement(com.poll.model.PollItem)]
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unexpected character ('a' (code 97)): expected a valid value (number, String, arr
ay, object, 'true', 'false' or 'null')
at [Source: HttpInputOverHTTP#5ce15706; line: 1, column: 2]; nested exception is org.codehaus.jackson.JsonParseException: Unexpected character ('a' (code 97)): expected
a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: HttpInputOverHTTP#5ce15706; line: 1, column: 2]
at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.readJavaType(MappingJacksonHttpMessageConverter.java:187)
at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.read(MappingJacksonHttpMessageConverter.java:179)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodAr
gumentResolver.java:138)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:183)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:98)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:79)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:157)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:124)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:738)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:551)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:568)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1111)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:478)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1045)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:199)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:462)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:279)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:232)
at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:534)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536)
at java.lang.Thread.run(Thread.java:724)
Caused by: org.codehaus.jackson.JsonParseException: Unexpected character ('a' (code 97)): expected a valid value (number, String, array, object, 'true', 'false' or 'null'
)
at [Source: HttpInputOverHTTP#5ce15706; line: 1, column: 2]
at org.codehaus.jackson.JsonParser._constructError(JsonParser.java:1433)
at org.codehaus.jackson.impl.JsonParserMinimalBase._reportError(JsonParserMinimalBase.java:521)
at org.codehaus.jackson.impl.JsonParserMinimalBase._reportUnexpectedChar(JsonParserMinimalBase.java:442)
at org.codehaus.jackson.impl.Utf8StreamParser._handleUnexpectedValue(Utf8StreamParser.java:2090)
at org.codehaus.jackson.impl.Utf8StreamParser._nextTokenNotInObject(Utf8StreamParser.java:606)
at org.codehaus.jackson.impl.Utf8StreamParser.nextToken(Utf8StreamParser.java:492)
at org.codehaus.jackson.map.ObjectMapper._initForReading(ObjectMapper.java:2770)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2718)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1923)
at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.readJavaType(MappingJacksonHttpMessageConverter.java:184)
... 38 more
EDIT2
It seems that it works, problem is that it supports only " (double quotes) where I want to use single-quotes. Is there any reason why I should double quotes always when doing JSON?
And what I should do change default behaviour of JSON mapper?

Ok, to answer the question - issue was that I used badly formatted JSON. I should be using double-quotes instead of single quotes (which is basically follwoing JSON standard. I'm new to this and I was a little bit confused, because a lot of websites uses single quotes for their examples - like for instance google charts ).
{"id":10,"text":"smth smth"}
is good
{'id':10,'text':'smth smth'}
is bad:)

That's true if your JSON is not mapped correctly with server side object then it will throw exception. I found very nice simple example here working exactly what you are looking for:
AngularJS Post Spring MVC JSON Example
AngularJS Form Post Spring MVC JSON

You may be missing the default constructor in your PollItem domain.
public class PollItem {
public PollItem () {
}
//your rest of the codes
}
Add this and your code will work just fine.
You already have a constructor defined in PollItem, which is making it the default constructor. Therfore adding the dummy constructor will solve your problem

Related

How to use default values of Kotlin (1.4.21) data class in Jackson (2.12.0) and Quarkus (1.11.1)

I am using Quarkus 1.11.1 with Kotlin 1.4.21 and Jackson 2.12.0.
I don't understand why when I send a POST request with a body of a data class that has a defined default parameter, this is not accepted and returns an error problem: Parameter specified as non-null is null
In the pom.xml file I have:
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jackson</artifactId>
</dependency>
The Quarkus Documentation says (https://quarkus.io/guides/kotlin#kotlin-and-jackson):
If the com.fasterxml.jackson.module:jackson-module-kotlin dependency and the quarkus-jackson extension (or the quarkus-resteasy-extension) have been added to project, then Quarkus automatically registers the KotlinModule to the ObjectMapper bean (see this guide for more details).
I have a data class like:
data class MyAttributes
#BsonCreator constructor(
#BsonProperty("myId")
#JsonProperty("myId")
var myId: String,
#BsonProperty("name")
#JsonProperty("name")
val name: String,
#BsonProperty("data")
#JsonProperty("data", defaultValue = "{}")
var data: MutableMap<String, Any> = mutableMapOf()
)
I noticed that the defaultValue in the #JsonProperty annotation is not useful, because it is used only to document expected values (https://fasterxml.github.io/jackson-annotations/javadoc/2.12/com/fasterxml/jackson/annotation/JsonProperty.html#defaultValue--)
If I send a JSON like:
{
"myId": "AB123",
"name": "my attribute name"
}
I get the error described previously, and the default value of the data field is ignored.
If I send:
{
"myId": "AB123",
"name": "my attribute name",
"data": {}
}
I don't get an error, because I send also the data field.
Can you tell me where am I doing wrong, please?
Thanks
Do you have a default constructor? By default jackson requires a default constructor. This is not very common with data classes so you can either provide the constructor or you can do something like this:
#Bean
fun objectMapper(): ObjectMapper = ObjectMapper()
.registerKotlinModule()

Jackson Object Mapper class given an error Spring mvc

Jackson Object Mapper class given an error Spring mvc
All required jar already added no error in project like jackson-core-asl, jackson-core-2.2.3 and jackson-all 1.9.0 still getting error
Remove the unnecessary jackson-all and jackson-core-asl dependencies.
Include the only one jackson-databind that automatically de/serializes the objects.
<jackson.version>2.8.9</jackson.version>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
Don't forget to return the whole List<Demo> instead of String.
#RequestMapping(value="/getJson", method = RequestMethod.GET)
public List<Demo> listJsonList {
// ...
return alldatalist;
}
And please, next time copy-paste the source code as a text, not as the image :)

SpringMvc controller to returns JSON, displays error: HTTP status 406

I am learning spring 4.2.4 by writing some webapp code.The idea is to return json file by controller.
I have already posted my questions before couple of days ago and still no i could get the right suggestions for my case. I am trying all the suggestions given by stackoverfolow none of the suggestions could work for me. Here is my controller:
.....
#RequestMapping(value="/getmessages",method=RequestMethod.GET, produces="application/json")
#ResponseBody
public Map<String,Object> getMessage(Principal prinicipal){
List<Message>message=null;
if(prinicipal==null){
message=new ArrayList<Message>();
}
else{
String username=prinicipal.getName();
message=usersService.getMessage(username);
}
Map<String,Object> data= new HashMap<String,Object>();
data.put("message", message);
data.put("number", message.size());
System.out.println("message has to be her\n"+message);
System.out.println("Number message has to be her is..."+message.size());
return data;
}
the message content which is to be retrieved from mysql is propely displayed in console.
The problem is conversion to JSON and return the result. I have been trying by change the jackson 1.9.x jar to jackson-fasterxml-2.x and it does not work. All other possible configuration of servlete also does not work for me.
When I add jackson-fasterxml-databind ....it displays file download dialogue box for filename"getmessages". to download and save...
I am very grateful for your help.
Finally I have solved this problem as follow:
I changed my return type from MAP to String and I converted my MAP to String in side my CONTROLLER as:
Converting my Map to String:
ObjectMapper mapper = new ObjectMapper();
String jsonFromMap = mapper.writeValueAsString(data);
I have also changed my dependencies as:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.2.3</version>
Inside #RequestMapping change
produces=application/json
to
produces=text/html
I think the problem is with browser unable to understand application/json and returning Map............
Any how I did what I want to do even-though I do not know how I did it!

How to receive JSON Messages in POST body in a JAX-RS Restful web service in CXF?

I'm trying to develop a REST service using Apache-CXF, on top of JAX-RS. For starters, I have a method called test that receives a String message and int value. I want the clients to be able to pass these parameters in a POST message body. I can't seem to achieve this.
Before I paste the code here, here are some details:
I'm using CXF without Spring
It's not a web app, so I don't have the WEB-INF folder with the web.xml
I test the service using SoapUI and Postman (Google Chrome application)
With the following code, I get WARNING: javax.ws.rs.BadRequestException: HTTP 400 Bad Request:
DemoService.java
#WebService(targetNamespace = "http://demoservice.com")
#Path("/demoService")
public interface DemoService {
#POST
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String test (String message, int value);
}
DemoServiceImpl.java
public class DemoServiceImpl implements DemoService {
#Override
public String test(String message, int value) {
return "test message: " + message + " value = : " + value;
}
}
DemoServer.java
public class DemoServer{
public static void main(String[] args) {
JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean();
DemoService demoService = new DemoServiceImpl();
serverFactory.setServiceBean(demoService);
serverFactory.setAddress("http://localhost:9090");
serverFactory.create();
}
}
My POM.xml (minus the attributes in the root tag, everything's there)
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>demoService</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<cxf.version>3.0.0</cxf.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- Jetty is needed if you're are not using the CXFServlet -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description</artifactId>
<version>3.0.0-milestone1</version>
</dependency>
</dependencies>
</project>
Testing with {"message":"hello there!", "value":"50"} to the URL http://localhost:9090/demoService/test gave a HTTP 400 Bad Reuest.
Then I saw this question on S.O.: How to access parameters in a RESTful POST method and tried this:
added the following nested class in DemoServer.java:
#XmlRootElement
public static class TestRequest {
private String message;
private int value;
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public int getValue() { return value; }
public void setValue(int value) { this.value = value; }
}
I also modified the DemoService interface and the implementation to use this class as a parameter in the test method, although this is still ultimately not what I want to do. (just showing the implementation here, question's already getting long):
#Override
public String test(TestRequest testRequest) {
String message = testRequest.getMessage();
int value = testRequest.getValue();
return "test message: " + message + " value = : " + value;
}
And to fix this error that I got: SEVERE: No message body reader has been found for class DemoService$TestRequest, ContentType: application/json (in Postman I see error 415 - unsupported media type) I added the following dependencies (jettison and another thing) to the POM.xml:
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>2.6.0</version>
</dependency>
I tested the service using the following JSON message, in a HTTP POST request:
{"testRequest":{"message":"hello there!", "value":"50"}}
This works. Though this solution where I use a TestRequest class to encapsulate the parameters works, that's not the solution I'm looking for. I want to be able to pass the two parameters in a JSON message, without having to introduce this TestRequest class (explicitly).
Questions:
Would this be easier to implement using Jersey?
I don't have a web.xml nor a WEB-INF folder, so I can't configure CXF in a cxf.xml file can I? A lot of tutorials online seem ot use a lot of XML configuration, but I don't want to deploy a framework like TomEE or Spring or Glassfish just to do that.
Searching online for solutions, I came across Spring Boot. Would you recommend using that, perhaps? Would that make developing web services like this easier?
Also, how do I get it to return the value in JSON format (or is it not supposed to do that for Strings?)
My friend pointed me to this stack exchange question: JAX-RS Post multiple objects
and also the following documentation: http://cxf.apache.org/docs/jax-rs-and-jax-ws.html
which states:
public class CustomerService {
public void doIt(String a, String b) {...};
}
By default JAX-RS may not be able to handle such methods as it
requires that only a single parameter can be available in a signature
that is not annotated by one of the JAX-RS annotations like
#PathParam. So if a 'String a' parameter can be mapped to a #Path
template variable or one of the query segments then this signature
won't need to be changed :
#Path("/customers/{a}")
public class CustomerService {
public void doIt(#PathParam("a") String a, String b) {...};
}
So, to answer my question, NO, it cannot be done.

RESTEasy: #Form deserialization issue in a #POST method

Here's a puzzle!
In a simple POST implementation:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response promote(#Form PromotionForm promotion) {
return Response.status(Response.Status.OK)
.entity(promotion.toString())
.build();
}
the argument passed to me does not have values set:
PromotionForm{name='null', csid=null}
But in debugger I can see that the request server received has the values in its input stream:
new BufferedReader(new InputStreamReader(
((HttpServletInputMessage) request).getInputStream())).readLine()
// returns: {"name":"form","csid":123}
After some debugging I could see that RESTEasy tries to derive arguments for the POST method call:
args[i++] = extractor.inject(input, response);
Which leads to FormInjector code:
propertyInjector.inject(request, response, target);
And eventually to FormParamInjector:
List<String> list = request.getDecodedFormParameters().get(paramName);
But request.getDecodedFormParameters() size is 0. RESTeasy does not try to read
anything from the requests' input stream for some reason.
Any ideas how I can make RESTeasy populate PromotionForm object correctly?
More information below.
Thanks for all you answers and comments in advance.
The client call is:
final PromotionForm form = new PromotionForm();
form.setName("form");
form.setCsid(123L);
final Response response = new ResteasyClientBuilder()
.disableTrustManager()
.build()
.target(targetField.getValue())
.request(requestField.getValue())
.cookie(cookieNameField.getValue(), cookieValueField.getValue())
.buildPost(Entity.entity(form, MediaType.APPLICATION_JSON_TYPE))
.invoke();
The PromotionForm:
import javax.ws.rs.FormParam;
public class PromotionForm {
#FormParam("name")
private String name;
#FormParam("csid")
private Long csid;
// setters & getters omitted
Dependencies:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.0.4.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>3.0.4.Final</version>
</dependency>
<!-- scannotation & resteasy-client ommitted -->
web.xml snippet:
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<context-param>
<!--If the url-pattern for the Resteasy servlet-mapping is not /*-->
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest</param-value>
</context-param>
Your method says that it consumes application/json. But the PromotionForm-class has #FormParam-annotations, which as the name implies, consumes form data.
To fix this, do one of the following:
Try to post form data instead of json. And change your #Consumes-annotation value to application/x-www-form-urlencoded
OR
Remove the #Form-annotation. Add JAXB-annotations on PromotionForm, so that the json data you post can be mapped to the PromotionForm-class.