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!
Related
I am trying to do a $batch request in Java using OData v2.
An example request from the browser would be something like below between the double quotes.
But how can I make this request programatically? Is there a sample call somewhere? Any help is appreciated.
Request URL: https://someUrl/project/odata/project/FOLDER/$batch
Request Method: POST
Status Code: 202 Accepted
Remote Address: 1.2.3.4:1234
Referrer Policy: no-referrer-when-downgrade
content-encoding: gzip
content-length: 5256
content-type: multipart/mixed; boundary=E828EB257B134AC6F567C8D3B67E666E1
dataserviceversion: 2.0
Accept: multipart/mixed
Accept-Encoding: gzip, deflate, br
Accept-Language: en
Connection: keep-alive
Content-Length: 595
Content-Type: multipart/mixed;boundary=batch_4edb-a2cd-948d
Cookie: project-usercontext=project-language=EN&project-client=100;
--Some cookie content--
DataServiceVersion: 2.0
Host: host.myClient.com:1234
MaxDataServiceVersion: 2.0
Origin: https://host.myClient.com:1234
Referer: https://host.myClient.com:1234/project/index.html
project-cancel-on-close: true
project-contextid-accept: header
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/1.2.3.4 Safari/537.36
x-csrf-token: 8Fd53yy2vuCjnaFKrZNuLg==
--batch_4edb-a2cd-948d
Content-Type: application/http
Content-Transfer-Encoding: binary
GET MyEntityDetailsSet HTTP/1.1
project-contextid-accept: header
Accept: application/json
Accept-Language: en
DataServiceVersion: 2.0
MaxDataServiceVersion: 2.0
project-cancel-on-close: true
> --batch_4edb-a2cd-948d
Content-Type: application/http
Content-Transfer-Encoding: binary
GET MyObjectSet HTTP/1.1
project-contextid-accept: header
Accept: application/json
Accept-Language: en
DataServiceVersion: 2.0
MaxDataServiceVersion: 2.0
project-cancel-on-close: true
--batch_4edb-a2cd-948d--
You can use Olingo V2 as an OData client (although a rather ugly one in my opinion). There is a full tutorial dedicated to this usage on the official Olingo site: How to use Apache Olingo as client library.
Olingo knows to build requests and parse responses, but you need an underlying mechanism to execute the HTTP calls. My recommendation would be to not rely on manually opening HttpURLConnections like in the above example, but to use something like Apache Http Client or some other dedicated library instead (in order to reduce the amount of code you write and also to have access to more advanced concepts like connection polling).
In a nutshell, you must first read and parse the metadata of the service that you want to consume:
// content = read the metadata as an InputStream
Edm dataModel = EntityProvider.readMetadata(content, false);
You can build a batch request via a fluent-style API:
BatchQueryPart part = BatchQueryPart.method("GET")
.uri("/Employees('1')")
.build();
// here you could have a larger list of parts, not just a singleton list
InputStream payload = EntityProvider.writeBatchRequest(
Collections.singletonList(part), "batch_boundary");
Then you have to just execute it using your HTTP request execution mechanism of choice (method = "POST" and body = the payload variable). Afterwards, you can parse the obtained response using Olingo:
// body = the response body received
// contentType = the Content-Type header received
List<BatchSingleResponse> responses =
EntityProvider.parseBatchResponse(responseBody, contentType);
// you can obtain the body for each request from the response list
String partBody = responses.get(0).getBody();
InputStream partStream = new ByteArrayInputStream(partBody.getBytes());
String partType = responses.get(0).getHeader(HttpHeaders.CONTENT_TYPE);
Lastly, using the Edm from the first step you can also parse each individual body based on the type of request that you build. For example you could use the readEntry method to de-serialize a single entity read:
// first we have to find the entity set you used to make the request
EdmEntitySet entitySet = edm.getDefaultEntityContainer()
.getEntitySet("Employees");
ODataEntry entry = EntityProvider.readEntry(partType, entitySet,
partStream, EntityProviderReadProperties.init().build())
Lastly, you can use the entry methods to get e.g. the properties.
I have developed a raw http post in java. I am trying to post a file to the post request dump website http://www.posttestserver.com/. But it shows and error
400 Bad Request. Pleas let me know what need to be done to avoid this error.
In this code , output => Stream to write on server.
filename -> path on server, here filename is initated to post.php
output.println("POST"+" "+filename+" HTTP/1.1\r");
//output.println("Content-Length: "+data.length());
output.println("Content-Type: multipart/form-data, boundary=AaB03x\r");
output.println("Content-length: 100\r");
//As http1.1 is by default keep-alive , close the connection explicitly
output.println("Connection: Close");
// blank line
output.println();
output.println("--AaB03x");
output.print(
"--AaB03x Content-Disposition: form-data; name=\"fileID\"; filename=\"temp.txt\" Content-Type: text/plain "
+"/nHello How are you?"
+ "/n--AaB03x--");
output.flush();
Error is
HTTP/1.1 400 Bad Request
Date: Wed, 18 Mar 2015 02:22:00 GMT
Server: Apache
Vary: Accept-Encoding
Content-Length: 226
Connection: close
Content-Type: text/html; charset=iso-8859-1
400 Bad Request
Bad Request
Your browser sent a request that this server could not understand.
This might be the issue of Content type. Server is expecting a request having header of content type text/HTML but your request content type is multipart/form data.
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
I'm trying to extract some information from raw HTTP Request messages (like the one below) and store them into instances of the org.apache.http.message.BasicHttpRequest (https://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/index.html) class.
I was able to employ org.apache.http.message.BasicLineParser class and its method parseHeader(String value, LineParser parser) to process (parse + store) the headers, but I don't know how to deal with the parameters passed with the form.
POST https://gist.github.com:443/gists HTTP/1.1
Host: gist.github.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,fr;q=0.8,it-it;q=0.6,it;q=0.4,en-us;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Referer: https://gist.github.com/
Connection: keep-alive
Content-Type: multipart/form-data; boundary=-------------------------10314229756379422411656878663
Content-Length: 1242
-----------------------------10314229756379422411656878663
Content-Disposition: form-data; name="parameter_name"
parameter_value
Do you know an utility which can parse the content starting after the header above (i.e. after the first empty line)? What I am interest in collecting are all the pairs <"parameter_name","parameter_value"> present in the request body.
I have already read similar answers/questions such as Parsing raw HTTP Request but I did not find anything helpful on the form-data component
Thanks in advance for your help and time
What you are seeing is MIME encoded content body. HttpClient is content agnostic and therefore does not provide a means of parsing such content. One can however use Apache Mime4J to do so.
I'm having a weird issue where if my servlet sends a 200 ok http status, it works as intended, and the client gets a 200 status as expected. But, if my servlet returns a 404 status, tomcat seems to change it into a 403 status. This doesn't happen if I use the http get method. I haven't tested put or post.
I want to make it very clear that my servlets doDelete method gets executed just fine. It's just that the status code returned to the browser gets changed.
I'll provide a minimal testcase demonstrating the issue.
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#WebServlet("/api/test403/*")
public class Test403 extends HttpServlet {
public void doDelete(HttpServletRequest request, HttpServletResponse response) {
try {
String p = request.getParameter("send404");
if (p != null && "1".equals(p)) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "not found.");
} else {
response.sendError(HttpServletResponse.SC_OK, "ok.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static final long serialVersionUID = 1L;
}
then i test via the following urls
myproject/api/test403?send404=1
myproject/api/test403?send404=0
What could cause this behavior? I'm not overly familiar the whole servlet/container architecture. I'm only experiencing this issue on 1 server which uses tomcat 7.0.41. I tried another server, which didn't exhibit this behavior.
edit - Per request, here's some output from the network panel in chrome. I used ajax to initiate this particular request:
Request Headers
DELETE /xxxxx HTTP/1.1
Host: xxxxx
Connection: keep-alive
Origin: xxx
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: */*
Referer: xxx
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: xxx
Response Headers
HTTP/1.1 403 Forbidden
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Date: Wed, 16 Apr 2014 02:30:32 GMT
I didn't remove any headers, although I anonymized some values.
A combination of...
an http DELETE request
calling HttpServletResponse.sendError(status, message) to send the 404 from your servlets doDelete() method
configuring a custom 404 error handler page(eg, via the <error-page> directive in web.xml)
Keeping the default value of readonly = true for your context
will cause the client to receive a 403 status instead of the 404 you though you sent.
A request to a servlet can service an http delete request without needing readonly to be false, but a request to a file cannot. What happens is that when you call sendError(), tomcat will try to find a custom error page that matches up with whatever http status you specified. In this case, it found one (/my404.html), and so in order to process it, it basically restarts the entire request routing/dispatching process, including running all the filters on the request. But, this time, since it's a static file request, it comes across a built in filter that looks for http the DELETE method, and then checks if readonly = false. If it's not, the request is rejected, and it changes the response status to 403 forbidden because you're not allowed to delete the static file named /my404.html.
A sensible workaround is to use HttpServletResponse.setStatus(status) instead of HttpServletResponse.sendError(status, message), so that tomcat doesn't try to find an error page. As mentioned by #BogdanZurac , you may also need to send a brief response body (i.e., "oops Error 404") in addition to setting the status to prevent it from seeking the custom error page.