I have an HTML form with several fields and a file upload.
On the Java side, I'm receiving the form as follows, and it works:
#Component("org.phenotips.metabolites.FileUploaderResource")
#Path("/metabolites")
public class FileUploaderResource extends XWikiResource
{
#GET
#Path("/test")
public Response test() {
String response = "<html>Successful</html>";
return Response.ok(response).build();
}
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(
#FormDataParam("filepath") InputStream uploadedInputStream
)
{
try {
String errorMessage = "This is an error response";
URI redirect = new URI("http://localhost:8080/");
return Response.temporaryRedirect(redirect).header("error_msg", errorMessage).build();
} catch (Exception ex) {
return Response.serverError().build();
}
}
}
But that's not what I need, as exemplified by what uploadedInputStream contains (example)
-----------------------------184640265716083967621914753489
Content-Disposition: form-data; name="id"
0000002
-----------------------------184640265716083967621914753489
Content-Disposition: form-data; name="column_order"
yes, no,
-----------------------------184640265716083967621914753489
Content-Disposition: form-data; name="date"
05/02/2015
-----------------------------184640265716083967621914753489
Content-Disposition: form-data; name="filepath"; filename="phenotips_2014-08-28_10-35.csv"
Content-Type: text/csv
"History (code)","History","History (code and name)","observed"
"","","","HP:0001622"
-----------------------------184640265716083967621914753489--
As you can see there are more fileds to the form than just the file.
But if I change the uploadFile's signature to
public Response uploadFile(
#FormDataParam("date") String date,
#FormDataParam("filepath") InputStream uploadedInputStream
)
I get the following error:
The root resource class org.phenotips.metabolites.FileUploaderResource is not a valid root resource class: The entity is already read. The 1. parameter requires one of the following annotations: [interface javax.ws.rs.core.Context, interface javax.ws.rs.HeaderParam, interface javax.ws.rs.MatrixParam, interface javax.ws.rs.QueryParam, interface javax.ws.rs.PathParam, interface javax.ws.rs.CookieParam]
Changing to #FormParam("date") also does not help, as it does not end up being found and returns a NullPointerException
EDIT. I like the guess proposed in the answer below.
I did indeed decide not to mention something directly (you can see it in the code though) - I am using a custom framework, XWiki. It is entirely possible that the body is read, and then there's nothing left to read.
This is not an answer: Just seeing if maybe it's something the OP is not showing us or telling us
I have tested this with Postman, and I have no problem. Maybe it's something else you're not showing us.
#Path("/multipart")
public class MutlipartResource {
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response postMultiPart(#FormDataParam("date") String date,
#FormDataParam("filepath") InputStream stream)
throws Exception {
ImageIO.read(stream);
return Response.ok(date).build();
}
}
Here is my interpretation of the error, though I may be wrong, it's just a guess. It seems like something is reading the body part prior to reaching the method, so there is nothing left to be read. In which case the error maybe saying that since it is not a readable part, it should not be defined as a multipart part put instead some other form, and as you can only have one entity body, the date cannot be a body, but instead must be a query param, path param, etc (something that's not a entity body). Again this is just a guess. But something maybe you can look into.
Related
I am developing a servlet for JAVA EE and keep getting this error "Error Viewerpage.index method has more than one entity. You must use only one entity parameter."
#ApplicationPath("REST2")
#Path("/viewer")
public class Viewerpage extends Application {
private GlobalConfiguration globalConfiguration;
private ViewerService viewerService;
#GET
#Path(value = "/viewer")
public Response index(String filename, String page, HttpServletResponse response) throws IOException {
// set headers before we write to response body
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(MediaType.TEXT_HTML);
// render a page of a file based on a parameters from request
renderPage(filename, response.getOutputStream());
// complete response
response.flushBuffer();
String value = "redirect:index";
return Response.status(Response.Status.OK).entity(value).build();
}
private void renderPage(String filename, OutputStream outputStream) {
String filepath = "storage/" + filename;
// render first page
MemoryPageStreamFactory pageStreamFactory = new MemoryPageStreamFactory(outputStream);
HtmlViewOptions viewOptions = HtmlViewOptions.forEmbeddedResources(pageStreamFactory);
Viewer viewer = new Viewer(filepath);
viewer.view(viewOptions);
viewer.close();
}
}
Any ideas what cause this error?
When you declare a resource method, you can only have one parameter that is the request entity. The parameter without any annotations is considered the entity body. All other parameters must have some kind of annotation that specifies what it is and what should be injected. If they are query parameters, use #QueryParam. If it is a path parameter, use #PathParam. If it some other non-Param injectable (that is supported) e.g. HttpServletRequest, then use #Context. Other supported "Param" injectable types are #HeaderParam, #FormParam, #CookeParam, #MatrixParam, etc.
Think of the HTTP response that gets streamed to the client. You are sending it with
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(MediaType.TEXT_HTML);
renderPage(filename, response.getOutputStream());
response.flushBuffer();
But then, afterwards (when the response stream at most should be closed), you try to do something that looks like building a second response:
Response.status(Response.Status.OK).entity(value).build();
As every response can have only one set of header and body you cannot go back setting headers or sending a second response entity. That is what the error is about.
Tackling the problem of versioning an API that wasn't versioned previously, I'm running into an issue: I've got two resources set on the same URL endpoint:
#Path("/api/foo/")
public class FooResourceOne {
#GET
public Response getFoo () {
return Response.ok("Hello from One").build();
}
}
#Path("/api/foo/")
#Consumes("application/vnd.example.v2+json")
public class FooResourceTwo {
#GET
public Response getFoo () {
return Response.ok("Hello from Two").build();
}
}
The issue is, if you send a GET request with a Content-Type header (e.g. Content-Type: application/json) it goes to FooResourceOne for handling. And if you specify Content-Type: application/vnd.example.v2+json, it goes to FooResourceTwo properly. That's all well and good.
However, if you don't specify a Content-Type header at all, it goes to FooResourceTwo, which is not correct?
Not having a #Consumes annotation means "accepts any", but it seems "any" doesn't include "not specified"? Or rather "not specified" means handled by whomever (and load ordering is important)?
Is there a way to specify FooResourceOne is the Resource that should respond when no Content-Type header is given at all?
I managed a workaround that doesn't fix the underlying issue, but at least alleviates the symptom. The Resteasy Filter and PreProcessInterceptor classes are able to detect when a request comes in with no Content-Type header, but can't then do anything about it other than throw an error. So, my workaround is for FooResourceTwo to first check and see if there is no header set:
#Path("/api/foo/")
public class FooResourceOne {
#GET
public Response getFoo () {
return Response.ok("Hello from One").build();
}
}
#Path("/api/foo/")
#Consumes("application/vnd.example.v2+json")
public class FooResourceTwo {
#Inject
private FooResourceOne oldResource;
#GET
public Response getFoo (#Context HttpServletRequest request) {
if (request.getHeader("content-type") == null) {
// No, no, Resteasy; go this way...
return oldResource.getFoo();
}
return Response.ok("Hello from Two").build();
}
}
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.
I have a jersey based web service which produces a "multipart/mixed" RESPONSE as follows:
The method reads a file, and should return it in octet format.
#GET
#Produces("multipart/mixed")
public byte[] getDocumentContents(#Context HttpHeaders header){
....
....
....
os = new ByteArrayOutputStream();
....
....
....
return os;
}
My question is: how do I make sure that the response is in OCTET-STREAM type? I know I could also just annotate the above method as:
#Produces("application/octet-stream")
But I specifically require to set the RESPONSE content-Type as "multipart/mixed" while sending a file in octet-stream format.
Does the above method do that ?
My assumption is it does but I have not a concrete reason on how.
thank you in advance!
I do not think "multipart/mixed" is a valid media type to be returned by a REST method
In my opinion, the correct way would be:
#GET
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getDocumentContents(#HeaderParam("your header param") final YourHeaderParamUserType headerParam) {
byte[] outByteArray = ... obtain byte array
return Response.ok()
.entity(outByteArray)
.build();
}
Pay attention to:
#Produces(MediaType.APPLICATION_OCTET_STREAM)
The param you might want to "extract" from the header could be getted using a param in the function like:
#HeaderParam("your header param") final YourHeaderParamUserType headerParam
The only thing you don't have to forget in "YourHeaderParamUserType" is to:
Include a constructor from a string
... or include a fromString(String) static method
... or include a valueOf(String) static method
Forgive me, but I may not be familiar with all the lingo necessary to ask this question properly.
I'm working on a fairly simple REST web service in Java using the org.apache.cxf.jaxrs.ext implementation of jax-rs. The method header is like this:
#GET
#Path("json/{fullAlias}")
#Produces({"application/json"})
public String json(#PathParam("fullAlias") String fullAlias, #Context MessageContext req)
where MessageContext is org.apache.cxf.jaxrs.ext.MessageContext.
There are two things I'm trying to accomplish that I can't seem to figure out:
Change the content-type if certain conditions are met (e.g. for an error)
Change the status code of the response
I've tried using changing the response by accessing it through the MessageContext:
HttpServletResponse response = req.getHttpServletResponse();
response.setContentType("text/plain")
response.setStatus("HttpServletResponse.SC_BAD_REQUEST);
But these changes have no bearing on the response sent; with or without the #Produces annotation, setting the content type inside the method doesn't affect the actual content type (With the annotation, it of course returns "application/json", without it defaults to "text/html").
I am returning a simple String as the body. I've entertained trying to return a javax.ws.rs.core.Response object to do what I want, but I don't know much about it.
How would I change the content type and/or the status codes from inside this method?
One approach is to throw a WebApplicationException, as described by Pace, which will work if you are looking to specifically handle an error condition. If you are looking to be able to change your content at any time for any reason, then you will want to take a look at returning a Response as the result of your service method rather than a String. Returning a Response gives you the greatest amount of control over how your service responds to the client request (it does require more code than returning a simple string).
Here is an example of how you would can make use of the Response object:
#GET
#Path("json/{fullAlias}")
public Response json(#PathParam("fullAlias") String fullAlias, #Context MessageContext req) {
...
if (success) {
ResponseBuilder rBuild = Response.ok(responseData, MediaType.APPLICATION_JSON);
return rBuild.build();
}
else {
ResponseBuilder rBuild = Response.status(Response.Status.BAD_REQUEST);
return rBuild.type(MediaType.TEXT_PLAIN)
.entity("error message")
.build();
}
}
I'm not sure if it's the best approach but I've done the following to solve your question #1.
public WebApplicationException createStatusException(String statusMessage) {
ResponseBuilder rb = Response.noContent();
rb = rb.type(MediaType.TEXT_PLAIN);
rb = rb.status(Status.BAD_REQUEST);
rb = rb.entity(statusMessage);
return new WebApplicationException(rb.build());
}
EDIT: I then threw the resulting WebApplicationException.
You can write your own Response Filter to change the content-type header.
#Provider
public class MimeAddingFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("Content-Type", "image/png");
}
}
This filter will add the "image/png" content-type header. You can also change or remove headers in JAX-RS response filters.