Amdatu multi-part-form #FormParam is always null - java

So i use Amdatu inside a Felix framework to create an OSGi enabled JSON Rest Service.
When i use #GET i get the id value as expected
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("file")
public String getFile(#QueryParam("id") String id) {
System.out.println("id : "+id);
return null;
}
When i use #POST FormParam is always null
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Path("file")
public String getFile(#FormParam("id") String id) {
System.out.println("id : "+id);
return null;
}
When i use #POST but with application JSON i always get the entire raw json and not the value.
I followed this video : http://www.amdatu.org/howto/createwebapp.html
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Path("file")
public String getFile(String id) throws Exception {
return id
}
I'm using the advanced rest client plugin for chrome to test the service.
Using libraries
org.amdatu.web.rest.jaxrs:1.0.4
org.amdatu.web.rest.wink:1.0.8
jackson-jaxrs:1.9.13
jackson-mapper-asl:1.9.13
jackson-core-asl:1.9.13
Update :
I had the depedencies in my maven bundle set to "provided" by changing them to "compile" MediaType.APPLICATION_FORM_URLENCODED now works.
But the MediaType.MULTIPART_FORM_DATA still doesnt.
During the post of the form my header is :
Content-Type: multipart/form-data
if i remove the #FormParam then the id is filled with :
id : --ARCFormBoundary5xbnwa6as8aor
Content-Disposition: form-data; name="id"
9
--ARCFormBoundary5xbnwa6as8aor--
the moment i add #FormParam the value is null.

The JAX-RS specification doesn't say anything about how multipart/form-data should be handled. Different JAX-RS implementations have their own proprietary ways to deal with this. As far as I can find, Apache Wink (that we build on top of) doesn't support #FormParam for multipart. It does seems that there are some support types in Wink for multi part: http://wink.apache.org/documentation/1.1.1/html/7.8%20MultiPart.html but these classes are not exposed by the Amdatu Wink bundle, and I have never tried using this either.
A workaround I use myself is the example below. This is useful when multipart is used to upload files together with other form fields (this is why multipart is mostly used). I use Apache File Upload to parse the request and get access to both the uploaded files and form fields.
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
public void test(#Context HttpServletRequest request) {
ServletFileUpload uploader = new ServletFileUpload(new DiskFileItemFactory());
try {
List<FileItem> parseRequest = uploader.parseRequest(request);
for (FileItem fileItem : parseRequest) {
if (fileItem.isFormField()) {
System.out.println(fileItem.getFieldName() + ": "
+ fileItem.getString());
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}

It looks like you're using the wrong Mediatype in #Consumes.
I created an example (see below) that accepts a parameter from a form, either by omitting the #Consumes completely, or setting it to MediaType.APPLICATION_FORM_URLENCODED.
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void test(#FormParam("id") String id) {
System.out.println(id);
}
HTML
<form action="/agenda" method="post">
<input type="id" name="id"/>
<input type="submit" value="Test">
</form>

Related

Use #POST instead of #GET to get user input

I am a newbie to JSON. I wish to ask that how can I convert the following code from #GET to #POST so that I can retrieve the appdataid from user JSON input and not from the link.
AppDataService ads = new AppDataService();
#GET
#Path("/{appdataid}")
#Produces(MediaType.APPLICATION_JSON)
public AppData getAppData(#PathParam("appdataid")long appdataid){
return ads.getAppData(appdataid);
}
Code for AppDataService
public AppDataService(){
appdatas.put(1L, new AppData(1,"Success", " ", "123456"));
appdatas.put(2L, new AppData(2,"Failure", " ", "654321"));
}
public AppData getAppData(long id){
return appdatas.get(id);
}
Instead of user entering http://localhost:8080/gni/webapi/appdata/1 for the result
{
"id": 1,
"message": " ",
"status": "Success",
"token": "123456"
}
I hope that I can receive the user input with #POST and JSON format.
I have tried the following code but not working.
#POST
#Path("/{appdataid}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public AppData getAppData(#PathParam("appdataid")long appdataid){
return ads.getAppData(appdataid);
}
Thanks for everyone that willing to help me.
If you want to POST JSON to your resource, you need to define a POJO for it to deserialise the message body into, for example:
public class AppDataRequest {
#JsonProperty
private long id;
public long getId() {
return id;
}
}
The use this type for in the resource:
#POST
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public AppData getAppData(AppDataRequest appDataRequest){
return ads.getAppData(appDataRequest.getId());
}
Note, the #Path annotation no longer has appdataid templated, and the parameter does not have an annotation.
SIDE NOTE
I have to wonder what you are trying to achieve here. The HTTP methods can and should be used to help describe the behaviour of your system. A request to get data should use a GET resource. POST resources should be used for creating new data. Sometimes there are good reasons to break this convention (such as the lack of a message body in a GET request), but there are usually other ways around them. Everything about your example tells my it should be a GET request, but I do not know the specifics of you application, so feel free to ignore this if it does not apply.
I don't think you really need JSON for that. You can use #FormParam for such a simple payload (ID)
#POST
#Path("some_url")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)//optional
public AppData getAppData(#FormParam("appdataid")long appdataid){
return ads.getAppData(appdataid);
}
You can call your web service from a console (Jersey assumed)
WebTarget target = client.target(UriBuilder.fromUri("http://localhost:8088/Your_root_url").build());
try{
MultivaluedMap<String, Integer> formData = new MultivaluedHashMap<String, String>();
formData.add("appdataid", 1);
Response res= target.path("some_url").request().
post(Entity.entity(formData, MediaType.APPLICATION_FORM_URLENCODED));
AppData entity = (AppData) res.readEntity(AppData.class);
//System.out.println(entity);//if toString() is overriden
}catch(Exception e){
System.out.println(e);
return;
}
EDIT: The only reason I see for wanting to use a POST in this situation is securiry (and even that is arguable)

Simple restful JSON POST with java as server and jquery as client

Before I ask my question I have to say that I have read more than 20 questions and articles about this problem and none of them could solve it.
My problem is I have a restful server in java like this:
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public void downloadByCode(#RequestBody final String stringRequest, final HttpServletResponse response)
{
try
{
final ObjectMapper objectMapper = new ObjectMapper();
final JsonNode jsonRequest = objectMapper.readValue(stringRequest, JsonNode.class);
// ...
// some processings here to create the result
// ....
final ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result);
// Flush the result
outputStream.flush();
}
catch (final Exception exception)
{
LOG.debug("Exception Thrown [downloadByCode]", exception);
}
}
And I have tried different ways to send a json to this server with jquery (but all of them create errors):
$.ajax({
url:"/downloadByCode",
type:"POST",
data: JSON.stringify({"name":"value"}) });
415 "errors message" : "Content type 'application/x-www-form
urlencoded;charset=UTF-8' not supported", "type" :
"HttpMediaTypeNotSupportedError"
So I tried to fix it by adding contentType:
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
400 "errors message" : "Could not instantiate JAXBContext for class
[class java.lang.String]: null; nested exception is
javax.xml.bind.JAXBException\n - with linked
exception:\n[java.lang.NullPointerException", "type"
:"HttpMessageConversionError"
I tried to send json object directly instead of JSON.stringify and it gives the same 400 error.
I tried to add different consumes to the #requestMapping but still no luck.
I tried to define my own class instead of JsonNode but it does not change anything.
Any ideas?
Please try to create new class :
public class InputData{
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
Then
public void downloadByCode(#RequestBody InputData stringRequest, final HttpServletResponse response)
And
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
try #RequestBody final Map<String, String> stringRequest
also you will need consumes = "application/json" on the #RequestMapping because you have that in your AJAX call
You will get 400 if spring doesn't like the format in which you send your ajax - I've had so much trouble with this in the past and it seems better to just ignore header types and content types unless necessary
You might try sending your response back as a ResponseEntity instead of using the HttpServletResponse directly. My hunch is that second argument, the HttpServletRequest argument, is what is causing the problem. I've never used that. I've always send my response back using the spring mvc api.
With Jersey api you can try just:
#POST
public void downloadByCode(String stringRequest)
and I think you'll find the body of your post in stringRequest.
You can take request body as string with usage of org.springframework.http.HttpEntity<String> as request type, here is example with your code as base:
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public void downloadByCode(final HttpEntity<String> request, final HttpServletResponse response)
{
try
{
final ObjectMapper objectMapper = new ObjectMapper();
final JsonNode jsonRequest = objectMapper.readValue(request.getBody(), JsonNode.class);
// ...
// some processings here to create the result
// ....
final ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result);
// Flush the result
outputStream.flush();
}
catch (final Exception exception)
{
LOG.debug("Exception Thrown [downloadByCode]", exception);
}
}
But maybe it will be better to use also String as return type, if you are planning to return result as string value, like this:
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public String downloadByCode(HttpEntity<String> request) {
String requestBody = request.getBody();
String result;
// ...
// some processings here to create the result text
// ....
return result;
}
I made simple application using Spring Boot with usage of proposed solutions using HttpEntity and also additional example of usage POJO, to run application you need to have Maven and JDK >= 1.7.
#clonning repository with sample
git clone git#github.com:mind-blowing/samples.git
#change current folder to sample application root
cd samples/spring-boot/rest-handler-for-plain-text
#running application using maven spring-boot plugin
mvn spring-boot:run
After application will be started you can open http://localhost:8080 and you will see html page with simple usage of JQuery to send POST requests, text of request and response will visible on html page, in controller I added two handlers, first with usage of HttpEntity and second with usage of POJO.
Controller: SampleRestController.java
HTML page: index.html
Project: https://github.com/mind-blowing/samples/tree/master/spring-boot/rest-handler-for-plain-text
First of all If you are using maven you should add dependency for jackson
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1</version>
</dependency>
or you can download the jar and put it in our project class path (you can use other mapper as well)
then you should create a model or DTO class where you can map your json
public class Data{
private String name;
pubilc Data(){}
//getter and setter
}
THEN you controller
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public Data downloadByCode(#RequestBody final Data data, final HttpServletResponse response)
{
//your code
return data;
}
AJAX CALL
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
(Optional)You can override behavior by telling object mapper not to fail on missing properties by defining the bean as follows:
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false));
return converter;
}
http://websystique.com/springmvc/spring-mvc-requestbody-responsebody-example/
Looking at your errors, it's clear that you have configured 'Jaxb2RootElementHttpMessageConverter' or similar XML converter in your spring configuration. And since you have registerned an XML converter, the #RequestBody and #ResponseBody work based on the registered message converters.
So, to solve your problem, go with a JSON message converter such as 'MappingJacksonHttpMessageConverter'. Once you register a JSON message converter, create a bean class to hold your json data and use it with RequestBody as below:
// It has to meet the json structure you are mapping it with
public class YourInputData {
//properties with getters and setters
}
Update 1:
Since you have defined multiple message converters, Spring tries to use the first one available by default. In order to use specific message converter(in this case Jackson converter), you should specify 'Accept' header from client like below:
$.ajax({
headers: {
"Accept" : "application/json; charset=utf-8",
"Content-Type": "application/json; charset=utf-8"
}
data: "data",
success : function(response) {
...
} })
The final answer is a combination of a number of answers/comments in this question that I am going to summarize them here:
1- You have to make sure you have an appropriate json converter in your spring config such as MappingJacksonHttpMessageConverter (credits to #java Anto)
2- You have to create a POJO class with same structure as your json object (see #Vinh Vo answer)
3- Your POJO class cannot be an inline class unless it is a static class. It means it should have its own java file or it should be static. (credits to #NTyler)
4- Your POJO class can miss parts of your json object if you set it appropriately in your object mapper (see #Aman Tuladhar answer)
5- Your ajax call requires contentType:"application/json", and you should send your data with JSON.stringify
Here is the Final code that is working perfectly:
public static class InputData
{
private String name
public String getName()
{
return name;
}
public void setName(final String name
{
this.name = name;
}
}
#RequestMapping(value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public void downloadByCode(#RequestBody final InputData request, final HttpServletResponse response)
{
try
{
String codes = request.getName();
// ...
// some processings here to create the result
// ....
final ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result);
// Flush the result
outputStream.flush();
}
catch (final Exception exception)
{
LOG.debug("Exception Thrown [downloadByCode]", exception);
}
}
And it is the jquery Ajax request:
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
Delete the #ResponseBody on your downloadByCode method
Change your method downloadByCode() return type to String and then return the String
Response body will automatically convert the returned String to JSON and then use the data appropriately
I am not that well versed with java but as much as I know your java code must be something like this.
public class downloadByCode{
#GET
#Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response downloadByCode(#QueryParam("paramater1") final String parameter 1, #Context HttpServletRequest httpRequest) {
If this not helps you can keep you code somewhere and share it.

Serializing an XML Payload with JAXB in a jax-rs Rest End Point

Ok, So i've genetrated my java classes from my xsd file using jaxb. I've also written the following code as an endpoint which recieves a request (XML). Now I'd like to read the request into my java objects I can then use these to insert into my DB. Is this the correct way i should be implementing this? If so, how is it done? Thanks
#POST
#Consumes("application/xml")
#Produces("application/xml")
public String registerPost(#Context HttpServletRequest req) {
try {
//update DB
} catch (DatabaseException e) {
return "Fail";
}
}
Hopefully this helps a little :
Your jax-rs end point can accept your JAXB class directly (providing your server has been configured with a JAXB provider). It will handle the conversion from incoming text to appropiate Java objects for you.
You don't specifically need the Servlet Request Object.
You may need to convert your JAXB entities into JPA entities before you can save them to the database (or add both JAXB and JPA annotations to the same classes).
Your return type should be text/plain rather than XML (or you should return XML).
#POST
#Consumes("application/xml")
#Produces("text/plain")
public String registerPost(MyEntityClass payload) {
try {
//update DB
myService.save(payload);
return "success";
} catch (DatabaseException e) {
return "Fail";
}
}

How to consume json parameter in java restful service

How can i consume json parameter in my webservice, I can able to get the parameters using #PathParam but to get the json data as parameter have no clue what to do.
#GET
#Path("/GetHrMsg/json_data")
#Consumes({ MediaType.APPLICATION_JSON })
#Produces(MediaType.APPLICATION_JSON)
public String gethrmessage(#PathParam("emp_id") String empid) {
}
What to use in place of #PathParam and how to parse it later.
I assume that you are talking about consuming a JSON message body sent with the request.
If so, please note that while not forbidden outright, there is a general consensus that GET requests should not have request bodies. See the "HTTP GET with request body" question for explanations why.
I mention this only because your example shows a GET request. If you are doing a POST or PUT, keep on reading, but if you are really doing a GET request in your project, I recommend that you instead follow kondu's solution.
With that said, to consume a JSON or XML message body, include an (unannotated) method parameter that is itself a JAXB bean representing the message.
So, if your message body looks like this:
{"hello":"world","foo":"bar","count":123}
Then you will create a corresponding class that looks like this:
#XmlRootElement
public class RequestBody {
#XmlElement String hello;
#XmlElement String foo;
#XmlElement Integer count;
}
And your service method would look like this:
#POST
#Path("/GetHrMsg/json_data")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public void gethrmessage(RequestBody requestBody) {
System.out.println(requestBody.hello);
System.out.println(requestBody.foo);
System.out.println(requestBody.count);
}
Which would output:
world
bar
123
For more information about using the different kinds of HTTP data using JAXB, I'd recommend you check out the question "How to access parameters in a RESTful POST method", which has some fantastic info.
Bertag is right about the comment on the GET. But if you want to do POST request that consumes json data, then you can refer to the code below:
#POST
#Path("/GetHrMsg/json_data")
#Consumes(MediaType.APPLICATION_JSON)
public Response gethrmessage(InputStream incomingData) {
StringBuilder crunchifyBuilder = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new InputStreamReader(incomingData));
String line = null;
while ((line = in.readLine()) != null) {
crunchifyBuilder.append(line);
}
} catch (Exception e) {
System.out.println("Error Parsing: - ");
}
System.out.println("Data Received: " + crunchifyBuilder.toString());
// return HTTP response 200 in case of success
return Response.status(200).entity(crunchifyBuilder.toString()).build();
}
For referencing please click here
#PathParam is used to match a part of the URL as a parameter. For example in an url of the form http:/example.com/books/{bookid}, you can use #PathParam("bookid") to get the id of a book to a method.
#QueryParam is used to access key/value pairs in the query string of the URL (the part after the ?). For example in the url http:/example.com?bookid=1, you can use #QueryParam("bookid") to get the value of `bookid.
Both these are used when the request url contains some info regarding the parameters and you can use the data directly in your methods.
Please specify the problem in detail if this post doesn't help you.

Http 415 on file Upload using jersey

My code for RESTful file upload :
#Path("/upload")
#POST
#Consumes("multipart/form-data")
public String post(
#FormDataParam("part") String s,
#FormDataParam("part") FormDataContentDisposition d) {
return s + ":" + d.getFileName();
}
When I try to upload a file using curl
curl -X POST --form part=#file.txt url
I am getting a HTTP 415-Unsupported Media Type Error. What is wrong ?
After trying a lot of examples finaly find the realy working example on http://iambigd.blogspot.com/2011/06/java-upload-file-using-jersey.html
#POST
#Path("/simpleupload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public void simpleUpload(
//#Context UriInfo ui,
#Context HttpServletRequest request
){
String fileRepository = "D:\\";
if (ServletFileUpload.isMultipartContent(request)) {
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = null;
try {
items = upload.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
}
if (items != null) {
Iterator<FileItem> iter = items.iterator();
while (iter.hasNext()) {
FileItem item = iter.next();
if (!item.isFormField() && item.getSize() > 0) {
System.out.println("File is found.");
String fileName = processFileName(item.getName());
try {
String savePath = fileRepository + fileName;
System.out.println("savePath:" + savePath);
item.write(new File(savePath));
} catch (Exception e) {
e.printStackTrace();
}
}else{
System.out.println("getFieldName:" + item.getFieldName());
System.out.println(item.getString());
}
}
}
}
}
(need the servlet-api.jar, (apache) commons-oi.jar and (apache) commons-fileupload.jar)
This can happen due to a couple of reasons. I managed to narrow down some of them.
Your Content-Type header does not match with the one provided by the #Consumes header. Verify this with a proxy.
You managed to stumble upon a bug that was fixed in Jersey 1.4 related to the FormDataParam annotation.
You included jersey-bundle and jersey-server et all in the same binary and they are competing against each other.
You are using #FormParam instead of #FormDataParam.
Your #FormDataParam is unrecognized by the introspection API because of conflicts with jersey-multipart and other jersey jars. If one jar is of version 1.x make sure the other jars are on the same version. While debugging the jersey API code I noticed that these method annotations turn up blank (on jersey's code) if the jar versions are not uniform. All method parameters on the REST service are replaced by the body content of the POST request irrespective of which FormDataParam they are supposed to contain.
Please make sure you have mimepull.jar on the classpath
You may need to register the MultipartFeature as described in the Jersey documentation, chapter 8.3.1.2 Registration.
Create a class something like this:
/**
*
*/
package com.verico.multipart.app;
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
#ApplicationPath("/")
public class MultiPartApp extends ResourceConfig {
public MultiPartApp() {
super(MultiPartFeature.class);
}
}
And add the following init-param to your Jersey servlet in web.xml:
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.verico.multipart.app.MultiPartApp</param-value>
</init-param>
Have you tried with an Input stream ?
Like :
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response post(
#Context HttpServletRequest request,
#Context HttpHeaders headers,
#FormDataParam("file") InputStream fileStream,
Works fine for me.

Categories

Resources