How to calculate MD5 hash of multipart request body using Jersey - java

I am using Jersey and I need to calculate the MD5 hash of the body of a multipart post request. My current resource method signature looks like this:
#POST
#Consumes("multipart/form-data")
#Produces("application/json")
public String post(
#FormDataParam("name") String name,
#FormDataParam("description") String description,
#FormDataParam("iconfile") FormDataBodyPart part,
#Context HttpServletRequest hsr) {
// ...
}
I did not find a way to get the raw request body, that I need to calculate the MD5 hash. When my resource method is invoked the input stream from the HttpServletRequest (hsr.getInputStream()) is already consumed and I can not read it again.
I tried changing my method signature to the following:
#POST
#Consumes("multipart/form-data")
#Produces("application/json")
public String test(byte[] bytes) {
// ...
}
This way I get the raw bytes of the request body and I can successfully calculate the MD5 hash but I don't know how to handle the multipart request from there (split the parts, get each part, etc.). Do I have to resort to handle the raw request myself? Or can I let Jersey do the dirty job and extract the FormDataParams for me and let me calculate the MD5 hash somehow?
Thanks,

This is what I ended up doing:
I created a container request filter that consumes the entity input stream, calculates the MD5 checksum and sets the entity input stream again so it can be consumed by Jersey to handle the multipart request and extract the FormDataParams for me.
I also injected the HttpServletRequest both in my filter and my resource method to communicate data between the two.
This is the filter class:
public class MD5CheckFilter implements ContainerRequestFilter {
#Context HttpServletRequest hsr;
public ContainerRequest filter(ContainerRequest request) {
byte[] bytes = request.getEntity(byte[].class); // this consumes the entity input stream
String contentMD5 = calculateMD5(bytes);
hsr.setAttribute("contentMD5", contentMD5);
// set the entity input stream so it can be consumed again
request.setEntityInputStream(new ByteArrayInputStream(bytes));
return request;
}
}
This is the relevant section of my web.xml within the servlet section:
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>path.to.MD5CheckFilter</param-value>
</init-param>
This way I don't need to change the original method signature:
#POST
#Consumes("multipart/form-data")
#Produces("application/json")
public String post(
#FormDataParam("name") String name,
#FormDataParam("description") String description,
#FormDataParam("iconfile") FormDataBodyPart part,
#Context HttpServletRequest hsr) {
// ...
}

Related

Why do I get Error method has more than one entity. You must use only one entity parameter

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.

How to receive 2 binary files and JSON in Jersey jax-rs?

I need to build a service that can receive 2 binary files (~100k each) and some metadata, preferably in json.
I found this, but it only seems to provide one InputStream to one of the parts. But I'd need two.. so what to do?
You have a few options
Simply add another parameter(s) with a different part annotation
#POST
#Consumes("multipart/form-data")
public Response post(#FormDataParam("file1") InputStream file1,
#FormDaraParam("file2") InputStream file2) {
}
The parts can have the same part name, so you could do
#POST
#Consumes("multipart/form-data")
public Response post(#FormDataParam("file") List<FormDataBodyPart> files) {
for (FormDataBodyPart file: files) {
FormDataContentDisposition fdcd = file.getFormDataContentDisposition();
String fileName = fdcd = getFileName();
InputStream is = file.getValueAs(InputStream.class);
}
}
You could traverse the entire multipart body youself
#POST
#Consumes("multipart/form-data")
public Response post(FormDataMultiPart mulitPart) {
Map<String, List<FormDataBodyPart>> fields = multiPart.getFields();
}
See Also:
Sending multiple files with Jersey: MessageBodyWriter not found for multipart/form-data, for a complete example
File upload along with other object in Jersey restful web service, for how the handle the JSON as a POJO.

Spring Jersey Corrupts Binary Data

I have a controller like that:
#POST
#Path("/{mon}/update")
public Response update(#PathParam("mon") String mon, #QueryParam("er") String er, #Context HttpServletRequest httpServletRequest, String data) {
...
}
When I send XML or JSON data there is not a problem. However when I send binary data I see that data is corrupted. I had a character encoding chain for UTF-8 and I removed it but problem did not resolved.
Any ideas?
EDIT: I've detect the problem. I've changed my controller as like that and converted my variable into String if needed:
#POST
#Path("/{mon}/update")
public Response update(#PathParam("mon") String mon, #QueryParam("er") String er, #Context HttpServletRequest httpServletRequest, byte[] data) {
...
}
How can I solve this problem?

multipart/mixed and application/octet-stream

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

Changing content type in jax-rs REST service

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.

Categories

Resources