I have a strange situation. I want to return the content type application/json; charset=utf-8 from an http handler.
func handleTest() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Accept") != "application/json" {
w.WriteHeader(http.StatusNotAcceptable)
return
}
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(map[string]string{"foo": "bar"})
}
}
When I check for this in my unit tests it is correct. This test does not fail.
func TestTestHandler(t *testing.T) {
request, _ := http.NewRequest(http.MethodGet, "/test", nil)
request.Header.Set("Accept", "application/json")
response := httptest.NewRecorder()
handleTest().ServeHTTP(response, request)
contentType := response.Header().Get("Content-Type")
if contentType != "application/json; charset=utf-8" {
t.Errorf("Expected Content-Type to be application/json; charset=utf-8, got %s", contentType)
return
}
}
But when I try with curl (and other clients) it comes out as text/plain; charset=utf-8.
$ curl -H 'Accept: application/json' localhost:8080/test -v
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /test HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: application/json
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Tue, 28 Dec 2021 13:02:27 GMT
< Content-Length: 14
< Content-Type: text/plain; charset=utf-8
<
{"foo":"bar"}
* Connection #0 to host localhost left intact
I have tried this with curl, insomnia and python. In all 3 cases the content type came out as text/plain; charset=utf-8.
What is causing this problem and how can I fix it?
From the http package docs:
WriteHeader sends an HTTP response header with the provided status code.
and
Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
So you are setting the "Content-Type" header after the header has already been sent out to the client. While mocking this likely works because the buffer where the headers are stored can be modified after the WriteHeader call. But when actually using a TCP connection you can't do this.
So simply move your w.WriteHeader(http.StatusOK) so it happens after the w.Header().Set(...)
I passed a form data using the Rest client. But it always shows an empty object in vs code console log.
This is the request.
POST {{Host_LOCAL}}/support/testFormData
Authorization: Bearer {{token}}
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="username"; name="email"
Content-Type: text/plain
{
"username": "John Doe",
"email": "123456"
}
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Where is the problem?
Thanks in advance
I am using my Arduino to do a multipart/form-data request. I am generating the request completely by myself as follows:
client.println(HTTP_METHOD + " " + PATH_NAME + " HTTP/1.1");
client.println("Host: " + String(HOST_NAME));
client.print(F("Content-Type: multipart/form-data; "));
client.print(F("boundary=\"AaB03x\"\r\n"));
client.print(F("Content-Length: "));
client.print(strlen("{json data here}")
+ strlen("{json data here}"));
client.print("\r\nConnection: close\r\n");
// First part
// Boundary
client.print(F("\r\n--AaB03x\r\n"));
// Headers
client.print(F("Content-Disposition: form-data; name=\"header\"\r\n"));
client.print(F("Content-Type: application/ld+json\r\n"));
client.print(F("Content-Length: "));
client.print(strlen("{json data here}"));
client.print(F("\r\n\r\n"));
// Content
client.print(F("{json data here}"));
// Second part
// Boundary
client.print(F("\r\n--AaB03x\r\n"));
// Headers
client.print(F("Content-Disposition: form-data; name=\"payload\"\r\n"));
client.print(F("Content-Type: application/ld+json\r\n"));//
client.print(F("Content-Length: "));
client.print(strlen("{json data here}"));
client.print(F("\r\n\r\n"));
// Content
client.print(F("{json data here}"));
// End of boundary
client.print(F("\r\n--AaB03x--\r\n\r\n"));
However the server returns "Incomplete multipart" which I don't understand since the multipart seems completely fine. I thought it was maybe due to incorrect newlines but I haven't been able to figure out a solution.
The output can be seen below and includes the request sent as well as the response from the server.
GET /router HTTP/1.1
Host: 192.168.178.147
Content-Type: multipart/form-data; boundary="AaB03x"
Content-Length: 4363
Connection: close
--AaB03x
Content-Disposition: form-data; name="header"
Content-Type: application/ld+json
Content-Length: 4143
{
some data here
}
--AaB03x
Content-Disposition: form-data; name="payload"
Content-Type: application/ld+json
Content-Length: 220
{
some data here
}
--AaB03x--
connected to 192.168.178.147
HTTP/1.1 500 Server Error
Connection: close
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html;charset=iso-8859-1
Content-Length: 597
Server: Jetty(9.4.41.v20210516)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Error 500 Server Error</title>
</head>
<body><h2>HTTP ERROR 500 Server Error</h2>
<table>
<tr><th>URI:</th><td>/router</td></tr>
<tr><th>STATUS:</th><td>500</td></tr>
<tr><th>MESSAGE:</th><td>Server Error</td></tr>
<tr><th>SERVLET:</th><td>org.apache.camel.component.jetty.CamelContinuationServlet-38cb1606</td></tr>
<tr><th>CAUSED BY:</th><td>java.io.IOException: Incomplete Multipart</td></tr>
</table>
<hr>Powered by Jetty:// 9.4.41.v20210516<hr/>
</body>
</html>
Your Content-Length calculations are suspect.
Drop the Content-Length headers for each sub-section in the multipart and try again, those are not needed for multipart, and your calculations are just wrong anyway.
You can see captures of various multipart requests from various libraries and browsers in the Jetty tests.
https://github.com/eclipse/jetty.project/tree/jetty-9.4.41.v20210516/jetty-http/src/test/resources/multipart
(Look at the ones ending in *.raw, which you can generally open in a text editor)
Tip: don't do this yourself, multipart mime is full of edge cases, traps, and ancient tricks. Go grab apache httpcomponents httpmime jar and just use it to generate your raw mime multipart section properly.
Artifacts - https://search.maven.org/artifact/org.apache.httpcomponents/httpmime
Javadoc - https://javadoc.io/doc/org.apache.httpcomponents/httpmime/latest/index.html
I am working on a project which I need to post some sensor data to my web service over tcp. I used StaticJsonDocument to hold these sensor data. The problem is that with the code below I can not be able to post any data. Currently I am using ENC28J60 for the ethernet connection.
void sendToApi (StaticJsonDocument<600> root) {
byte sd = stash.create();
String json_string;
serializeJson(root, json_string);
stash.print(json_string);
stash.save();
int stash_size = stash.size();
Serial.println(stash_size);
Stash::prepare(PSTR("POST /api/module HTTP/1.1" "\r\n"
"Host: 192.168.1.5:8181" "\r\n"
"Content-Type: application/json" "\r\n"
"Content-Length: $D" "\r\n"
"Authorization: Basic bWV0Ok1ldEF0czE4Kio=" "\r\n"
"\r\n"
"$H"),
stash_size, sd);
session = ether.tcpSend();
//Serial.println(session);
delay(2000);
}
There is no problem with the ethernet controller (I can ping Google). Also There is no problem with the server side. The postman HTTP request is below.
POST /api/module/ HTTP/1.1
Host: 192.168.1.5:8181
Content-Type: application/json
Authorization: Basic bWV0Ok1ldEF0czE4Kio=
Cache-Control: no-cache
Postman-Token: e160d927-7e05-414f-e0d8-102f3d039ce3
{"id":"0001","module_no":1,"m1":22.5625,"m2":22.5625,"m3":22.5625,"m4":22.5625,"m5":22.5625,"m6":22.5625,"m7":22.5625,"m8":22.5625,"m9":22.5625,"m10":22.5625,"m11":22.5625,"m12":22.5625,"m13":22.5625,"m14":22.5625,"m15":22.5625,"m16":22.5625,"t1":22.5625,"t2":22.5625,"t3":22.5625,"t4":22.5625,"t5":22.5625,"t6":22.5625,"t7":22.5625,"t8":22.5625,"t9":22.5625,"t10":22.5625,"t11":22.5625,"t12":22.5625,"t13":22.5625,"t14":22.5625,"t15":22.5625,"t16":22.5625,"af":22.5625,"uf":22.5625,"sg":22.5625,"sc":22.5625,"a1":946,"a2":946,"a3":946,"a4":946,"a5":946,"a6":32,"a7":32,"a8":32,"a9":946,"a10":946,"a11":946,"a12":946,"a13":32,"a14":32,"a15":32,"a16":32}
So what am I missing here?
I have written a POST route in Express.js: /api/file/upload. This route needs two parameters to work - a "file" parameter with the posted file and an "apiKey" parameter, which is a string. To test it, I am trying to create a successful request in Fiddler2 with the following data:
Headers:
Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
User-Agent: Fiddler
Host: localhost:8000
Content-Length: 178037
Request Body:
---------------------------acebdf13572468
Content-Disposition: form-data; name="file"; filename="4Byl64P (1).jpg"
Content-Type: image/jpeg
<#INCLUDE *C:\Users\patrick\Pictures\4Byl64P (1).jpg*#>
---------------------------acebdf13572468--
---------------------------acebdf13572468
Content-Disposition: form-data; name="apiKey"
Content-Type: application/json
{
"apiKey": "GKBG-QoNs-f74E-Z8Qn-zozm"
}
---------------------------acebdf13572468--
But when I try to log the parameters in Node.js, I get an empty object for request.body and undefined for request.files.
How do I successfully POST form data to Node.js using Fiddler2?
Your body is malformed (premature ending boundary). It should probably look more like this:
---------------------------acebdf13572468
Content-Disposition: form-data; name="apiKey"
Content-Type: application/json
{
"apiKey": "GKBG-QoNs-f74E-Z8Qn-zozm"
}
---------------------------acebdf13572468
Content-Disposition: form-data; name="file"; filename="4Byl64P (1).jpg"
Content-Type: image/jpeg
<#INCLUDE *C:\Users\patrick\Pictures\4Byl64P (1).jpg*#>
---------------------------acebdf13572468--