Jersey throws ParseException when uploading file in multipart/form-data request - java

I am trying to send an mp3 file along with some metadata to my Jersey REST service. For this I am trying to use multipart/form-data content type, however I always get java.text.ParseException: Next event is not a Separator when I send the request. The whole response:
{"restResponse":{
"responseCode":"INVALID_PARAMETER",
"i18nMessage":"Invalid Parameter: Next event is not a Separator",
"responseDescription":"The request contains an invalid parameter"}}
If I remove the mp3 file from the request, it is working properly so I assume there is a problem with how my request is constructed. I am using Paw, which generates request like this:
POST /rest/myapi/recording/multipart HTTP/1.1
Accept: application/json
Accept-Language: en-US
Content-Type: multipart/form-data; boundary=EI6FArOacJKf5JCY5BAA2sbl2IAfN8ty
Cookie: JSESSIONID=a5f5cfa7329142158766a6182645; JSESSIONIDSSO=BAFA3371F6D14A179B0BA6216DD6C119
Host: localhost:8181
Connection: close
User-Agent: Paw/2.2.2 (Macintosh; OS X/10.10.5) GCDHTTPRequest
Content-Length: 44504
--EI6FArOacJKf5JCY5BAA2sbl2IAfN8ty
Content-Disposition: form-data; name="queue"
qMultipart
--EI6FArOacJKf5JCY5BAA2sbl2IAfN8ty
Content-Disposition: form-data; name="datetime"
20151029-021807
--EI6FArOacJKf5JCY5BAA2sbl2IAfN8ty
Content-Disposition: form-data; name="recording"; filename="test4_multiform.mp3"
Content-Type: audio/mpeg
ID3ETT2test4_multiformCOMengiTunPGAP0TENiTunes 12.3.0.44COMhengiTunNORM 00000152 00000152 000013BE 000013BE 0000023E 0000023E 00003F10 00003F10 000002A7 000002A7COMengiTunSMPB 00000000 00000210 00000A30 0000000000015BC0 00000000 0
--- the rest of the recording ---
The serverside handler:
#Path(value = "multipart")
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
public Response receiveFileAndMetadataAsMultipart(
#FormDataParam(RECORDING) InputStream recordingIinputStream,
#FormDataParam(RECORDING) FormDataContentDisposition callRecordingDispositionHeader,
#FormDataParam(QUEUE) String queue,
#FormDataParam(DATETIME) String datetime) throws JSONException, ParseException, IOException {
//do stuff with my recording and metadata
//...
JSONObject response = new JSONObject();
return Response.status(Response.Status.OK).entity(response).build();
}
When I try to send just the file and the content type is not multipart/form-data but application/octet-stream, then it works fine. (I mean not with this handler but one which accepts octet-stream) although the request is very similar. I'll post the request and code for octet stream as well, maybe it helps:
PUT /rest/myapi/recording HTTP/1.1
Accept: application/json
Accept-Language: en-US
Content-Type: application/octet-stream
Cookie: JSESSIONID=a5f5cfa7329142158766a6182645; JSESSIONIDSSO=BAFA3371F6D14A179B0BA6216DD6C119
Host: localhost:8181
Connection: close
User-Agent: Paw/2.2.2 (Macintosh; OS X/10.10.5) GCDHTTPRequest
Content-Length: 55414
ID3BTT2test3_2callsCOMengiTunPGAP0TENiTunes 12.3.0.44COMhengiTunNORM 0000038A 0000038A 00001B3D 00001B3D
--- the rest of the recording ---
Method:
#Path(value = "recording")
#PUT
#Consumes(MediaType.APPLICATION_OCTET_STREAM)
#Produces(MediaType.APPLICATION_JSON)
public Response receiveRecording(InputStream callRecordingInputStream) throws IOException, JSONException {
//do stuff with my recording
//...
JSONObject response = new JSONObject();
return Response.status(Response.Status.OK).entity(response).build();
}
Does anyone have any idea what is wrong? I found this approach with multipart on several places online so I guess it should work doing it like that.
Btw, I also tried removing all of the metadata and just send the recording but the problem was the same.

I have found the issue thanks to #peeskillet's comment, so just in case this exact same thing happens to someone else:
I had a bad import for FormDataContentDisposition.
Wrong:
import com.sun.jersey.core.header.FormDataContentDisposition;
Correct:
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;

Related

apache cxf MultiPart request has no Content-Length header

I'm having below code to send a multipart/form-data request.
List<Attachment> multipartData = new ArrayList<>();
ContentDisposition cd1 = new ContentDisposition("form-data; name=\"file\";
filename="+fileObj.getName());
FileInputStream inputStream = new FileInputStream(fileObj);
multipartData.add(new Attachment("file",inputStream, cd1));
MultipartBody multipart = new MultipartBody(multipartData);
In my RestClient class, I'm using the below lines of code to send a POST request using JAX-RS Client object
if ("POST".equals(method)) {
response = this.client.getBuilder().post(Entity.entity(entity,MediaType.MULTIPART_FORM_DATA));
I checked the HTTP request body using Wiremock and is as below:
Transfer-Encoding: [chunked]
Accept: [*/*]
Cache-Control: [no-cache]
User-Agent: [Apache-CXF/3.2.5]
Connection: [keep-alive]
Host: [127.0.0.1:9990]
Pragma: [no-cache]
Content-Type: [multipart/form-data; boundary="uuid:04b491f5-50de-4f4f-b7c0-cd745136d3d1"]
--uuid:04b491f5-50de-4f4f-b7c0-cd745136d3d1
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <file>
Content-Disposition: form-data; name="file"; filename=sample.txt
<File content goes here>
I want to know how the content-length header is missing in the request payload. Is there any way to set the content-length header to the request?
Please help me.
I used the apache cxf WebClient to unset the transfer encoding as chunked.
if ("POST".equals(method)) {
Invocation.Builder builder = this.client.getBuilder();
WebClient.getConfig(builder).getHttpConduit().getClient().setAllowChunking(false);
response = builder.post(Entity.entity(entity,MediaType.MULTIPART_FORM_DATA));
}
With this, the client is able to send the request with content-length header.

Parsing Multipart/mixed in Spring

I want to parse requests like the following in my RestController
POST http://#.#.#.#:#/report HTTP/1.1
User-Agent: Android
Accept: text/html,application/xml,application/json,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Content-Type: multipart/mixed; boundary=%&REPORT_DIVIDER&%
Authorization: Basic ***
Content-Length: 23236
Host: #.#.#.#:#
Connection: Keep-Alive
Accept-Encoding: gzip
--%&REPORT_DIVIDER&%
Content-Type: application/json
{"content-excluded":true}
--%&REPORT_DIVIDER&%
Content-Disposition: attachment; filename="INSTALLATION"
Content-Type: application/octet-stream
609903cf-fcc0-460c-87db-958e031ac156
--%&REPORT_DIVIDER&%--
This message is conform to rfc1341, yet I cannot find a way to parse this in Spring.
Note that the number of files may vary.
I've already tried using CommonsMultipartResolver or StandardServletMultipartResolver, but both only support multipart/form-data (rfc1867).
Is there any way to parse these request in Spring besides writing my own parser?
While it is not a very nice solution, it was the only one I found:
I essentially reimplemented ServletFileUpload with this class and then loaded it into the apache library with this simple subclass of CommonsMultipartResolver
#Path("api")
#PUT
#Consumes("multipart/mixed")
#Produces("multipart/mixed")
public MultiPart twelve( MultiPart multiPart) throws IOException {
List<BodyPart> bodyParts = multiPart.getBodyParts();
BodyPartEntity bpe = (BodyPartEntity) bodyParts.get(1).getEntity();
}
This is for jersey based spring boot project.

POST request to a REST server

So, I'm trying to create a simple (and I mean simple) POST request. Here is the class on the server-side.
#Stateless
#Path("cards")
public class CardsFacadeREST extends AbstractFacade<Cards> {
#POST
#Path("test")
#Consumes({"text/plain"})
public void createTestCard() {
Cards card = new Cards();
card.setName("Test Card");
super.create(card);
}
#GET
#Path("count")
#Produces("text/plain")
public String countREST() {
return String.valueOf(super.count());
}
}
The GET method works just fine, but the POST method isn't working for me. I'm using Chrome's Advanced Rest Client.
The URL is http://localhost:8080/dc-rest/webresources/cards/test
The type is POST
My header is like so: Content-Type: text/plain
And that's it.
I keep getting a "400: Bad Request. The request sent by the client was syntactically incorrect."
When I open the response in the JSON window, all it says is "Unexpected token <"
Here are the request headers if that makes any difference.
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.103 Safari/537.36
Origin: chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo
Content-Type: text/plain
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: JSESSIONID=f4c746a32b46244d422800192f04; treeForm_tree- hi=treeForm:tree:applications
Body is empty.
And the response:
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)
Server: GlassFish Server Open Source Edition 4.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Allow: GET,DELETE,OPTIONS,PUT,POST
Access-Control-Allow-Headers: content-type
Content-Language:
Content-Type: text/html Date: Thu, 11 Feb 2016 20:48:12 GMT
Connection: close Content-Length: 1105
Body:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>GlassFish Server Open Source Edition 4.0 - Error report</title><style type="text/css"><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 400 - Bad Request</h1><hr/><p><b>type</b> Status report</p><p><b>message</b>Bad Request</p><p><b>description</b>The request sent by the client was syntactically incorrect.</p><hr/><h3>GlassFish Server Open Source Edition 4.0 </h3></body></html>
The 400-Bad request response is likely because the server has expressed that it expects a specific content type
#Consumes({"text/plain"}
However the client is not indicating that the post body is of this type.
To solve this, make sure that the POST request from your client contains the following HTTP header:
Content-Type: text/plain
Or, perhaps it is the case that you are not POSTing plain text and you intend to POST XML or JSON. Whatever the intended type, you just need to make sure the client and server are in agreement about it.
If an HTTP request has a body, it must have either Content-Length or Transfer-Encoding header.
With neither, the request has no body -- not even a body of length 0. If you want to send an empty body, the request should have header Content-Length: 0.
There is a semantic difference between having no body and having an empty body. The server apparently rejects the POST request with no body. (Though the request is actually syntactically valid according to RFC)
Actually, this part is not quite clear (discussion thread (don't read)). And some implementations set Content-Length: 0 for GET requests; some implementations omit Content-Length: 0 for empty POST bodies; both are wrong... Sometimes they work sometimes they don't. Welcome to the chaotic world of HTTP.
I hate it when this happens. It was something else entirely, my persistence bean is messed up. When I commented out "super.create(card)" in my POST method, everything worked (request body or no).
Still have no idea why that resulted in "400: Bad Request. The request sent by the client was syntactically incorrect."
Seems like I have another problem to figure out, but this one at least is solved.
Thank y'all for helping!

Resteasy always get application/json content type on request

I have a web application that use Angularjs on frontend och Resteasy + Jackson on the back end. I'm sending a file from Angular component to a REST method, receiving method looks like this :
#POST
#Path("/upload/attachment/for/{eventId}")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(MultipartFormDataInput input,
final #PathParam("eventId") Long eventId,
#Context HttpServletRequest request) {
Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
...my awesome stuff...
return Response.ok().build();
}
And request has following headers when sent :
Accept application/json, text/plain, */*
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Content-Length 347085
Content-Type multipart/form-data; boundary=---------------------------12164806981346771846716776342
Cookie JSESSIONID=aoBd1hgzR3GM8bSG5P-9g-vQ; csrftoken=ziQ7kN7TlMehR2aURDrmaMLYAroMsSpu
Host localhost:9000
Referer http://localhost:9000/local/myapp/index.html
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:32.0) Gecko/20100101 Firefox/32.0
The problem is that THIS request ALWAYS has application/json as a Content-Type instead for multipart/form-data as it says in the headers. And I get :
20:12:00,490 WARN [org.jboss.resteasy.core.SynchronousDispatcher] (http-/127.0.0.1:8080-2) Failed executing POST /events/upload/attachment/for/null: org.jboss.resteasy.spi.UnsupportedMediaTypeException: Cannot consume content type
Already in HttpServletDispatcher the content is wrong.
I can't get if this is JBOSS that set Content-Type to wrong value or some thing else.
Ok, it was that I have an extra layer between angular and server which runs on Node.js and it just tunel requests to the server. I had to add following :
var r = null;
r = request.put({uri: url, json: inReq.body, headers: inReq.headers, form: inReq.form});
inReq.pipe(r).pipe(inRes);
This set all the headers from outgoing request and tunnel the request
Now I have right content-type

Jersey Client set content-type header as text/plain

Using jersey client sending HTTP request. Content-Type header is automatically set as "application/json" (as a nature), but i want to changing "content-type" header with "text/plain" regardless of any specification, standards etc. Jersey version is 2.4.1.
Code
String target = "http://192.168.1.2:10000";
String path = "test3";
Client c = ClientBuilder.newClient ();
WebTarget target = c.target (target).path (path);
Entity<SubscriberBean> json = Entity.json (subscriber);
Builder request = target.request ();
String response = request.post(json, String.class);
Request
POST /test3 HTTP/1.1
Accept: text/html
Content-Type: application/json
User-Agent: Jersey/2.4.1 (HttpUrlConnection 1.6.0_17)
Host: 192.168.1.2:10000
Connection: keep-alive
Content-Length: 278
///**** Some json data ***///
instead of
request.post(json, String.class);
try to use
request.type(MediaType.TEXT_PLAIN).post(json, String.class);
Use Entity.text(entityData) or Entity.entity(entityData, mediaType) methods instead of Entity.json() in your example.

Categories