I have a problem with posting JSON to a Jersey REST service - GET is working perfectly but POST seems tricky. I've been working on this problem for awhile now, with no solution so far. Any help is much appreciated!
It seems it cant find the U RL to send the json?Here is what FireBug console shows:
POST http://localhost:9998/data 400 Bad Request
Post source: name=Tony
**Response Headers**
Connection close
Content-Length 0
Content-Type text/html; charset=iso-8859-1
Date Fri, 20 Apr 2012 10:13:24 GMT
**Request Headers**
Accept application/json, text/javascript, */*; q=0.01
Accept-Encoding gzip, deflate
Accept-Language sv-se,sv;q=0.8,en-us;q=0.5,en;q=0.3
Connection keep-alive
Content-Length 9
Content-Type application/json; charset=UTF-8
Host localhost:9998
Referer http://localhost:9998/static/page.html
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko/20100101 Firefox/11.0
X-Requested-With XMLHttpRequest
I'm doing the POST as follows:
<button id='btn' value="knapp" name="knapp" />
<script type="text/javascript">
$('#btn').click(function(){
$.ajax({
url: '/data',
type: 'POST',
contentType: 'application/json',
data: {name:"Tony"},
dataType: 'json'
});
})
</script>
Javabean class with #XmlRootElement:
#XmlRootElement
public class StatusBean {
private String name;
public StatusBean() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Resource method:
#Path("/data")
public class PostData {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public StatusBean post(StatusBean sb) {
System.out.println(sb);
return sb;
}
}
The server, set up with Grizzly:
public class Main {
public static final URI BASE_URI = getBaseURI();
public static void main(String[] args) throws IOException {
HttpServer httpServer = startServer();
Map<String,String> initParams = new HashMap<String, String>();
initParams.put("com.sun.jersey.config.property.packages", "server");
SelectorThread selector = GrizzlyWebContainerFactory.create("http://localhost:9998/", initParams );
System.out.println(String.format("Jersey app started with WADL available at "
+ "%sapplication.wadl\nTry out %shelloworld\nHit enter to stop it...",
BASE_URI, BASE_URI));
System.in.read();
httpServer.stop();
}
protected static HttpServer startServer() throws IOException {
System.out.println("Starting grizzly...");
ClassNamesResourceConfig rc = new ClassNamesResourceConfig(PostData.class);
// rc.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, true);
HttpServer server = GrizzlyServerFactory.createHttpServer(BASE_URI, rc);
server.getServerConfiguration().addHttpHandler(new StaticHttpHandler(new File(".").getAbsolutePath()), "/static");
return server;
}
private static int getPort(int defaultPort) {
String port = System.getProperty("jersey.test.port");
if (null != port) {
try {
return Integer.parseInt(port);
} catch (NumberFormatException e) {
}
}
return defaultPort;
}
private static URI getBaseURI() {
return UriBuilder.fromUri("http://localhost/").port(getPort(9998)).build();
}
}
Try making your bean serializable.
#XmlRootElement
public class StatusBean implements Serializable {
....
}
Check your POST url. It should be `
http://localhost:9998/{projectname}/{restservletmapping}/data
For example, if my web.xml looks like this and my project name is SampleProject
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
URL would be : http://localhost:9998/SampleProject/rest/data
You can use tools for testing REST services like SOAP UI or browser addons like POSTMAN, REST CONSOLE, etc.
If above things are fine and REST service is giving response with testing tools.
Then it could be problem of Cross Origin Policy in ajax.
I had the same problem. The issue is that your data is not converted to JSON string automatically.
So you just need to call JSON.stringify(...) on your data before posting it:
<button id='btn' value="knapp" name="knapp" />
<script type="text/javascript">
$('#btn').click(function(){
$.ajax({
url: '/data',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({name:"Tony"}),
dataType: 'json'
});
})
</script>
This should work.
From your server config I see that you haven't configured JAX-RS with Grizzly. On the base of that example you should somehow pass such property
Map<String,String> initParams = new HashMap<String, String>();
initParams.put( "com.sun.jersey.config.property.packages", "package.with.your.StatusBean.class" );
Another configuration option is to use
ResourceConfig rc = new PackagesResourceConfig("your.package.with.resources");
and start grizzly server:
GrizzlyServerFactory.createHttpServer(BASE_URI, rc);
See details: http://jersey.java.net/nonav/documentation/latest/user-guide.html (Chapter "Deploying the root resource"). Try to run first example they have.
Are you sure that the path you're posting to is complete? You should define another Path annotation on the post method and use that in the URL you're posting to:
#Path("/data")
public class PostData {
#Path("/postStatus")
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public StatusBean post(StatusBean sb) {
System.out.println(sb);
return sb;
}
}
Then use the /data/postStatus path to post your request to:
<button id='btn' value="knapp" name="knapp" />
<script type="text/javascript">
$('#btn').click(function(){
$.ajax({
url: '/data/postStatus',
type: 'POST',
contentType: 'application/json',
data: {name:"Tony"},
dataType: 'json'
});
})
</script>
You have probably forgotten to register the JSON mapper, i.e. Jackson (or whatever mapper you use). The feature is not enabled automatically, you have to load the class in your ResourceConfig:
org.glassfish.jersey.jackson.JacksonFeature.class
sample
also, see JSON howto
Related
I'm trying to do a proof of concept that involves a .Net system posting a file to a Rest endpoint on a Java Spring Boot application. I keep getting the "Required Parameter is not present" error. There are a lot of SO questions with this error, and I've attempted the solutions presented in those with no luck. Can anyone see what I'm doing wrong?
Here's my C# code:
private async Task<string> PostFileAsync(string uri, System.IO.FileStream fileStream)
{
using (var client = _httpClientFactory())
{
using (var content = new MultipartFormDataContent())
{
content.Add(new StreamContent(fileStream), "assetFile");
using (var message = await client.PostAsync(uri, content))
{
return await message.Content.ReadAsStringAsync();
}
}
}
}
Here's the request as Fiddler sees it:
POST http://10.0.2.2:8083/asset/1000/1001 HTTP/1.1
Content-Type: multipart/form-data; boundary="bac9aebd-d9ff-40ef-bcf3-4fffdd1b2c00"
Host: 10.0.2.2:8083
Content-Length: 158
Expect: 100-continue
Connection: Keep-Alive
--bac9aebd-d9ff-40ef-bcf3-4fffdd1b2c00
Content-Disposition: form-data; name=assetFile
foo,bar,10
foo2,bar2,12
--bac9aebd-d9ff-40ef-bcf3-4fffdd1b2c00--
Here's my Controller:
#RestController
#RequestMapping("/asset/")
public class AssetController {
#RequestMapping(path="{merchantId}/{assetId}", method=RequestMethod.POST)
public String getAsset(
HttpServletRequest request,
#RequestParam("assetFile") MultipartFile file,
#PathVariable("merchantId") long merchantId,
#PathVariable("assetId") long assetId) throws IOException
{
return "It worked!";
}
}
Here's my config:
#SpringBootApplication(exclude={MultipartAutoConfiguration.class})
public class MySpringApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringApplication.class, args);
}
#Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
System.out.println("multipartResolver()");
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
return multipartResolver;
}
}
And here's the response:
HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 25 Mar 2016 19:34:55 GMT
Connection: close
f3
{"timestamp":1458934495566,"status":400,"error":"Bad Request","exception":"org.springframework.web.bind.MissingServletRequestParameterException","message":"Required MultipartFile parameter 'assetFile' is not present","path":"/asset/1000/1001"}
0
Edited because I posted the wrong C# code
Ok, maybe I hadn't tried ALL of the solutions I saw on SO.
This question had a solution for me.
I had to use #ModelAttribute rather than #RequestParam.
I ma using Spring MVC and trying to use jQuery. I have this on my web page:
$(document).ready(function () {
var entity = {mag: "status_key", paper: "View10"};
$("#btn").click(function () {
$.ajax({
url: "ajaxJsonPost",
type: 'post',
dataType: 'json',
data: JSON.stringify(entity),
contentType: 'application/json',
});
});
});
Spring server has this:
#RequestMapping(value = "ajaxJsonPost", method = RequestMethod.POST)
public void postJson(#RequestBody Entity en) throws IOException {
System.out.println("writing entity: " + en.toString());
}
OK, Entity cames to server. BUT browser console prints 404 not found. I know that my POST request needs any response. In the Internet I've found solution which recommends me to return ResponseEntity object, OR use annotation #ResponseStatus. They both return HttpStatus well, but I don't know in which cases I should use them. What is the best way?
#Controller
#RequestMapping("/apipath")
public class SomeController {
#RequestMapping(value = "/ajaxJsonPost", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String postJson(#RequestBody final Entity en) {
System.out.println(en.toString());
//assuming you have a class "EntityService" and
//it has a method postData
//which takes Entity object as parameter and pushes into database.
EntityService.postData(en);
System.out.println("added");
return "success";
}
}
Entity object on the Server side
#JsonAutoDetect
public class Entity {
private String mag;
private String paper;
public String getMag() {
return mag;
}
public void setMag(final String mag) {
this.mag = mag;
}
public String getPaper() {
return paper;
}
public void setPaper(final String paper)
this.paper = paper;
}
}
ajax
$(document).ready(function () {
var entity = {mag: "status_key", paper: "View10"};
$("#btn").click(function () {
$.ajax({
url: "/apipath/ajaxJsonPost",
type: 'post',
dataType: 'json',
data: JSON.stringify(entity),
contentType: 'application/json',
success : function(response) {
alert(response);
},
error : function() {
alert('error');
}
});
});
});
And as far as why and when to use #ResponseStatus and #ResponseEntity, there is already a short and simple answer here by #Sotirios Delimanolis. When use #ResponseEntity .
It says :
ResponseEntity is meant to represent the entire HTTP response. You can
control anything that goes into it: status code, headers, and body.
#ResponseBody is a marker for the HTTP response body and
#ResponseStatus declares the status code of the HTTP response.
#ResponseStatus isn't very flexible. It marks the entire method so you
have to be sure that your handler method will always behave the same
way. And you still can't set the headers. You'd need the
HttpServletResponse or a HttpHeaders parameter.
Basically, ResponseEntity lets you do more.
I have a service, which I can access with the following jQuery code (from google chrome with --disable-web-security)
$.ajax({
type: 'POST',
url: "http://10.30.1.2:9234/myapp/v6/token/generate",
headers: {
"Content-Type":"application/json",
"Accept":"application/json"
},
data: JSON.stringify({
"staffId" : "13254",
"password" : "JustADummyPassword"
})
}).done(function(data) {
console.log(data);
});
$.ajax({
type: 'GET',
url: "http://10.30.1.2:9234/myapp/v6/user/appl/Firstname/Lastname/email#address.com/1998-01-01",
headers: {
"Content-Type":"application/json",
"Accept":"application/json"
}
}).done(function(data) {
console.log(data);
});
The first call sets a cookie, which is required for the second call to authenticate. This works fine, and both requests return expected results.
I am trying to set up automated testing for the service, and have this written in JAVA, using RestAssured.
public class UserApplication {
public static Map<String, String> authCookies = null;
public static String JSESSIONID = null;
public static void main(String[] args) {
Response resp = hello();
resp = apiUserApplication();
}
public static Response apiUserApplication() {
String userAppl = "http://10.30.1.2:9234/myapp/v6/user/appl/Firstname/Lastname/email#address.com/1998-01-01";
Response response = RestAssured.given()
.cookie("JSESSIONID", JSESSIONID).and()
.header("Accept", "application/json").and()
.header("Content-Type", "application/json").and()
.when().get(userAppl);
return response;
}
public static Response hello() {
String helloUrl = "http://10.30.1.2:9234/myapp/v6/hello";
Response response = RestAssured.given().cookies(authCookies)
.contentType("application/json").when().get(helloUrl);
return response;
}
}
The first call (hello) works fine, and returns 200 code, and gets a valid token for use in the second call. The error I am getting from the second call with a 400 status code is...
{"errors":["Content type 'null' not supported"]}
I'm not experienced with RestAssured, but your code is set up to first call, hello, then apiUserApplication. You have some class level variables that you default to null, but you never explicitly give them a value. In particular, it looks in the second call, apiUserApplication, you are setting the value of the cookie to a null value.
I also do not see why you are returning this response object? It would make sense if you were to examine it, ensure that the response has data as you expected and that you then set this session ID for the second request.
It looks like JSESSIONID and authCookies are initialized as null and not changing in this code.
I have a relatively simple RESTful web service which uses Jersey and Eclipselink MOXy.
I can POST requests to the service if the data is formatted as XML, but if I send it as JSON instead, the server generates an HTTP 400 (Bad Request), with the message: "The request sent by the client was syntactically incorrect.".
The service-side looks like this:
#POST
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Subscription post(Subscription Subscription) {
return Subscriptions.addSubscription(Subscription);
}
If I send it XML data from Javascript in a webpage like this, there is no problem:
$.ajax({
url: 'http://localhost:8080/MyService/subscription',
type: 'POST',
data: "<subscription><notificationType>EMAIL</notificationType><notificationAddress>test#example.com</notificationAddress></subscription>",
headers: {
Accept : "application/xml",
"Content-Type": "application/xml"
},
// .. other params ...
);
However, with the equivalent JSON I get HTTP 400 - Bad Request:
$.ajax({
url: 'http://localhost:8080/MyService/subscription',
type: 'POST',
data: JSON.stringify(
{subscription:{notificationType:"EMAIL",notificationAddress:"test#example.com"}}
),
dataType: 'json'
headers: {
Accept : "application/json",
"Content-Type": "application/json"
}
// .. other params ...
);
I have inspected the request using Fiddler, and the data formatting and headers all look correct.
The interesting thing is that I can successfully unmarshall the exact same JSON string if I plug it into this code:
String json = "{\"subscription\":{\"notificationType\":\"EMAIL\",\"notificationAddress\":\"test#example.com\"}}";
JAXBContext context = JAXBContext.newInstance(Subscription.class);
Unmarshaller m = context.createUnmarshaller();
m.setProperty("eclipselink.media-type", "application/json");
StringReader sr = new StringReader(json);
Subscription sub = (Subscription)m.unmarshal(sr);
System.out.println(sub.toString());
The subscription class is defined as:
#XmlRootElement(name="subscription")
public class Subscription {
public enum NotificationType { EMAIL, SMS };
private String notificationAddress;
private NotificationType notificationType;
public String getNotificationAddress() {
return notificationAddress;
}
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public NotificationType getNotificationType() {
return notificationType;
}
public void setNotificationType(NotificationType notificationType) {
this.notificationType = notificationType;
}
public Subscription() {
}
#Override
public String toString() {
String s = "Subscription";
if (getNotificationAddress() != null) {
s += "(" + getNotificationType().toString() + ":" + getNotificationAddress() + ")";
}
return s;
}
}
I have configured Eclipselink MOXy as my JAXB provider by adding this line to jaxb.properties in the package that contains my Subscriber class:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
and that seems to work, at least for marshalling objects going out from the service.
What am I missing?
EDIT:
This is what Fiddler captures when I post the JSON data:
POST http://localhost:8080/MyService/subscription HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 86
Accept: application/json
Origin: http://localhost:8080
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36
Content-Type: application/json
Referer: http://localhost:8080/TestPage/AddSubscription.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
{"subscription":{"notificationType":"EMAIL","notificationAddress":"test#example.com"}}
UPDATE:
I took Option#2 from Blaise's answer below, created an Application-derived class, thus:
import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
public class MyApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>(2);
set.add(MOXyJsonProvider.class);
set.add(SubscriptionResource.class); // the class containing my #POST service method.
return set;
}
}
And added to web.xml:
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.example.MyApplication</param-value>
And now I don't get an HTTP 400, and the code in my service is hit, which it wasn't before, however the passed-in Subscription object has all uninitialized fields, e.g. notificationAddress is null.
If I post using XML, it still works ok.
UPDATE#2:
I have reduced my code to the smallest subset that demonstrates the problem, and you can get it here:
https://www.dropbox.com/sh/2a6iqw65ey0ahrk/D2ILi_722z
The above link contains a .zip with 2 Eclipse projects; TestService (the Jersey RESTful service that accepts a Subscription object) and TestPage (a .html page with some JavaScript to POST a subscription object in either JSON or XML).
If you put a breakpoint in the post method of the service, and use the test page to send the JSON request, you'll see an un-initialised Subscription object gets passed in.
EclipseLink JAXB (MOXy) can be used with Jersey in a couple of different ways to produce JSON.
Option #1 - Add a jaxb.properties File
Jersey can leverage a JAXB (JSR-222) implementation to produced JSON. If you add a jaxb.properties in with your domain model then you can specify MOXy as that provider. For a while the following bug existed in Jersey that prevented MOXy from being used in this way which may be what you are hitting now.
https://java.net/jira/browse/JERSEY-753
This method of producing JSON has some limitations and may not be what your ultimately want.
Option #2 - Leverage MOXyJsonProvider
As of EclipseLink 2.4.0 MOXy offers its own JSON-binding based on the JAXB and MOXy metadata. You can configure this using the MOXyJsonProvider.
import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
public class MyApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>(1);
set.add(SubscriptionResource.class);
return set;
}
#Override
public Set<Object> getSingletons() {
MOXyJsonProvider moxyJsonProvider = new MOXyJsonProvider();
moxyJsonProvider.setIncludeRoot(true);
HashSet<Object> set = new HashSet<Object>(1);
set.add(moxyJsonProvider);
return set;
}
}
For More Information
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
This is the way I would recommend using MOXy with Jersey, or any other JAX-RS provider.
Homepage.jsp:
<script type="text/javascript">
var startZArray = new Array();
function add()
{
var startZ = $('#startZ').val();
startZArray.push(startZ);
}
function submitP()
{
$.ajax({
type: "POST",
url:"/horn/rest/main/schedule",
data: startZArray,
success:function()
{alert('worked');}
});
}
</script>
Homepage.java
#POST
#Path("/schedule")
public void tigerMessage(
#Context final HttpServletResponse response,
#Context final HttpServletRequest request) throws Exception
{
String[] myParams = request.getParameterValues("startZArray");
System.out.println(myParams);
}
Unfortunately myParams prints out null. I know that inside add startZArray is getting populated. But I'm not sure if data: startZArray is the proper way to pass it. Also I know that the Jersey url is being hit because I do see null printed out. Does anyone have any ideas?
Edit:
When I do:
[~] curl -i -X POST -d "{\"startZArray\":\"testMessage\"}" http://localhost:8080/horn/rest/main/schedule
HTTP/1.1 204 No Content
Server: Apache-Coyote/1.1
Date: Wed, 24 Apr 2013 20:15:18 GMT
I still get null so I think something is wrong in my java code?
Edit2:
I updated for debugging the submitP() method:
function submitP()
{
var myarray = ['Element 1', 'Element 2', 'Element 3'] ;
var dataobject = {
postvar: myarray
} ;
$.ajax({
type: "POST",
url:"/horn/rest/main/schedule",
data: dataobject,
success:function()
{
alert('worked');
}
});
}
When I run the code in firebug the output post looks something like this:
Paramaters:
postvar[] Element 1
postvar[] Element 2
postvar[] Element 3
Source:
postvar%5B%5D=Element+1&postvar%5B%5D=Element+2&postvar%5B%5D=Element+3
The post had a status of 204 and said "No Content" for some reason.
I've also tried putting postvar[] and just plain postvar into the getParameterValues method with no luck.
With your submitP() in Edit2
Try this in jersey
#POST
#Path("/schedule")
public String tigerMessage(
#FormParam("postvar[]") List<String> vars) throws Exception
{
System.out.println(vars)
return ""
}
Update:
Another approach
#POST
#Path("/schedule")
public String tigerMessage(
Form form) throws Exception
{
System.out.println(form);
// Then get parameters from form
return ""
}
you are not sending your data with a parameter so you can't get that with startZArray
request.getParameterValues("startZArray");
Try this -
$.ajax({
type: "POST",
url:"/horn/rest/main/schedule",
data:{ "startZArray" : startZArray },
success:function()
{alert('worked');}
});