POST request on arduino with ESP8266 using WifiESP library - rest

I am attempting to make RESTful POST request using the WifiESP library (https://github.com/bportaluri/WiFiEsp). I'm able to successfully make the request with curl, but consistently get an error using the Arduino and ESP. I suspect the problem is related to the manual formatting of the POST request the library requires, but I don't see anything wrong. Here my sanitized code:
if (client.connect(server, 80)) {
Serial.println("Connected to server");
// Make a HTTP request
String content = "{'JSON_key': 2.5}"; // some arbitrary JSON
client.println("POST /some/uri HTTP/1.1");
client.println("Host: http://things.ubidots.com");
client.println("Accept: */*");
client.println("Content-Length: " + sizeof(content));
client.println("Content-Type: application/json");
client.println();
client.println(content);
}
The error I get (via serial monitor) is this:
Connected to server
[WiFiEsp] Data packet send error (2)
[WiFiEsp] Failed to write to socket 3
[WiFiEsp] Disconnecting 3
My successful curl requests looks like this:
curl -X POST -H "Content-Type: application/json" -d 'Some JSON' http://things.ubidots.com/some/uri

After some experimentation, here is the solution to the multiple problems.
The JSON object was not correctly formatted. Single quotes were not accepted, so I needed to escape the double quotes.
The host does not need "http://" in a POST request; POST is a HTTP method.
The sizeof() method returns the size, in bytes, of the variable in memory rather than the length of the string. It needs to be replaced by .length().
Appending an integer to a string requires a cast.
This is the corrected code:
if (client.connect(server, 80)) {
Serial.println("Connected to server");
// Make the HTTP request
int value = 2.5; // an arbitrary value for testing
String content = "{\"JSON_key\": " + String(value) + "}";
client.println("POST /some/uri HTTP/1.1");
client.println("Host: things.ubidots.com");
client.println("Accept: */*");
client.println("Content-Length: " + String(content.length()));
client.println("Content-Type: application/json");
client.println();
client.println(content);
}

The code explained by Troy D is right and it's working .I think the error in posting the data to the server is due to this line
client.println("Content-Length: " + sizeof(content));
and the correct way is
client.println("Content-Length: " + String(content.length()));
Now coming to this error
Connected to server
[WiFiEsp] Data packet send error (2)
[WiFiEsp] Failed to write to socket 3
[WiFiEsp] Disconnecting 3
This is the error of library you can ignore it.

The problem with "Data packet send error (2)", "Failed to write to socket 3" and "Disconnecting 3" is not a problem within the WifiEsp library as far as I can see, believe it's more likely to be within the AT firmware. By default the http headers contain a "Connection: close" parameter which in normal cases should be correct. However with this bug the server will get disconnected before the reply is received on the client side and any response from the server will be identified as garbage data. Using the value "Connection: keep-alive" as a workaround will make it possible to receive the acceptance from the server in a proper way.
I'm running my Arduino + ESP8266-07 against a MVC based Web Api that I created on one of my servers and in the controllers Post-method I use a single string as return value, the value I return if everything is ok is simply one of the strings that WifiEsp keeps track of (It will still include the http status code in the response header that it returns)
public async Task<string> Post([FromBody]JObject payload)
{
//Code to handle the data received, in my case I log unit ip, macaddress, datetime and sensordata into a db with entity framework
return "SEND OK";
}
So in your Arduino code try following instead:
String PostHeader = "POST http://" + server + ":" + String(port) + "/api/values HTTP/1.1\r\n";
PostHeader += "Connection: keep-alive\r\n";
PostHeader += "Content-Type: application/json; charset=utf-8\r\n";
PostHeader += "Host: " + server + ":" + String(port) + "\r\n";
PostHeader += "Content-Length: " + String(jsonString.length()) + "\r\n\r\n";
PostHeader += jsonString;
client.connect(server.c_str(), port);
client.println(PostHeader);
client.stop();
In the file debug.h located in the library source code you could alter a define and get more output to your serial console. Open the file and change
#define _ESPLOGLEVEL_ 3
to
#define _ESPLOGLEVEL_ 4
Save the file and recompile/deploy your source code to your Arduino and you will get extensive information about all AT commands the library sends and what the library receives in return.

Related

RESTful client in Unity - validation error

I have a RESTful server created with ASP.Net and am trying to connect to it with the use of a RESTful client from Unity. GET works perfectly, however I am getting a validation error when sending a POST request. At the same time both GET and POST work when sending requests from Postman.
My Server:
[HttpPost]
public IActionResult Create(User user){
Console.WriteLine("***POST***");
Console.WriteLine(user.Id+", "+user.sex+", "+user.age);
if(!ModelState.IsValid)
return BadRequest(ModelState);
_context.Users.Add(user);
_context.SaveChanges();
return CreatedAtRoute("GetUser", new { id = user.Id }, user);
}
My client:
IEnumerator PostRequest(string uri, User user){
string u = JsonUtility.ToJson(user);
Debug.Log(u);
using (UnityWebRequest webRequest = UnityWebRequest.Post(uri, u)){
webRequest.SetRequestHeader("Content-Type","application/json");
yield return webRequest.SendWebRequest();
string[] pages = uri.Split('/');
int page = pages.Length - 1;
if (webRequest.isNetworkError || webRequest.isHttpError){
Debug.Log(pages[page] + ":\nReceived: " + webRequest.downloadHandler.text);
}
else{
Debug.Log(pages[page] + ":\nReceived: " + webRequest.downloadHandler.text);
}
}
}
I was trying both with the Json conversion and writing the string on my own, also with the WWWForm, but the error stays.
The error says that it's an unknown HTTP error. When printing the returned text it says:
"One or more validation errors occurred.","status":400,"traceId":"|b95d39b7-4b773429a8f72b3c.","errors":{"$":["'%' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0."]}}
On the server side it recognizes the correct method and controller, however, it doesn't even get to the first line of the method (Console.WriteLine). Then it says: "Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.ValidationProblemDetails'".
Here're all of the server side messages:
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 POST http://localhost:5001/user application/json 53
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'TheNewestDbConnect.Controllers.UserController.Create (TheNewestDbConnect)'
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[3]
Route matched with {action = "Create", controller = "User"}. Executing controller action with signature Microsoft.AspNetCore.Mvc.IActionResult Create(TheNewestDbConnect.Data.Entities.User) on controller TheNewestDbConnect.Controllers.UserController (TheNewestDbConnect).
info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1]
Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.ValidationProblemDetails'.
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
Executed action TheNewestDbConnect.Controllers.UserController.Create (TheNewestDbConnect) in 6.680400000000001ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'TheNewestDbConnect.Controllers.UserController.Create (TheNewestDbConnect)'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 11.3971ms 400 application/problem+json; charset=utf-8
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
I have no idea what is happening and how to solve it. Any help will be strongly appreciated!
Turned out I was just missing an upload handler. Adding this line solved it: webRequest.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(JsonObject));

How to HTTP POST in Lua without using Lua Socket

I am trying to send a HTTP POST command to an external server from a Lua scripted piece of hardware. In order to do so I have to create a tcpConnection and then do a tcpSend() command. I have confirmed the TCP Connection using wireshark and I have seen it send the HTTP GET command but I do not know how to send a post. I have tried to just change the GET to POST in the code but no avail.
debugConsole = -20
while true do
--Opening connection to remote host
debugConsole = tcpConnect("192.168.1.1", 80, 100)
print("TCP connect " .. debugConsole)
debugConsole = tcpSend("GET /api/v1/profiles/active_profiles.json?profile=5&expiry=1&duration=0&auth_token=2e608b72390a866f4bc7bbb6db63a1aa HTTP/1.1 \r\n\r\n")
print("TCP Send = " .. debugConsole)
--Printing response from remote host
print(responseSubstr(0, 500))
debugConsole = tcpClose()
print("TCP Close = " .. debugConsole)
debugConsole = httpRequest("http://192.168.1.1/api/v1/profiles/active_profiles.json?profile=5&expiry=1&duration=0&auth_token=2e608b72390a866f4bc7bbb6db63a1aa", 10)
print("HTTP Request = " .. debugConsole)
print(" ")
sleep (10000)
end

What can cause Rust's TcpSocket::write() to return "invalid input"?

For a little fun I wanted to make a simple HTTP request in Rust. I threw this together and it works great:
use std::io::TcpStream;
fn main() {
// This just does a "GET /" to www.stroustrup.com
println!("Establishing connection...");
let mut stream = TcpStream::connect("www.stroustrup.com:80").unwrap();
println!("Writing HTTP request...");
// unwrap() the result to make sure it succeeded, at least
let _ = stream.write(b"GET / HTTP/1.1\r\n\
Host: www.stroustrup.com\r\n\
Accept: */*\r\n\
Connection: close\r\n\r\n").unwrap();
println!("Reading response...");
let response = stream.read_to_string().unwrap();
println!("Printing response:");
println!("{}", response);
}
Response is:
Establishing connection...
Writing HTTP request...
Reading response...
Printing response:
HTTP/1.1 200 OK
...and the rest of the long HTTP response with all the HTML as I'd expect...
However, if I change the request to be /C++.html instead of /:
use std::io::TcpStream;
fn main() {
// The only change is to "GET /C++.html" instead of "GET /"
println!("Establishing connection...");
let mut stream = TcpStream::connect("www.stroustrup.com:80").unwrap();
println!("Writing HTTP request...");
// unwrap() the result to make sure it succeeded, at least
let _ = stream.write(b"GET /C++.html HTTP/1.1\r\n\
Host: www.stroustrup.com\r\n\
Accept: */*\r\n\
Connection: close\r\n\r\n").unwrap();
println!("Reading response...");
let response = stream.read_to_string().unwrap();
println!("Printing response:");
println!("{}", response);
}
The socket returns "invalid input":
Establishing connection...
Writing HTTP request...
Reading response...
thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: invalid input', /Users/rustbuild/src/rust-buildbot/slave/nightly-dist-rustc-mac/build/src/libcore/result.rs:746
Why does the socket return "invalid input"? The TCP socket isn't aware of the HTTP protocol (and I've tested my request with telnet and netcat: it's correct), so it can't be complaining about HTTP request/response.
What does "invalid input" even mean here? Why doesn't this work?
My rust version (I'm on OS X 10.10.1):
$ rustc --version
rustc 1.0.0-nightly (ea6f65c5f 2015-01-06 19:47:08 +0000)
The "invalid input" error isn't coming from the socket. It's coming from String. If the read_to_string() call is changed to read_to_end(), then the response is successful. Apparently the response isn't valid UTF-8.
More explicitly, the code:
println!("Reading response...");
let response = stream.read_to_end().unwrap();
println!("Printing response:");
println!("{}", String::from_utf8(response));
returns:
Err(invalid utf-8: invalid byte at index 14787)
So the HTTP response is bad. Looking at the web page, the error is here (the � characters are the problem):
Lang.Next'14 Keynote: What � if anything � have we learned from C++?
The offending characters are 0x96, indeed invalid utf-8. It should be U+2013 –
The document is either iso-8859-1 or windows 1252. There are a number of other problems with that HTML, such as unescaped &'s.

Windows Azure REST API Update Role Doesn't Take Effect

I'm doing some proof of concept work on azure, trying to get a role using the Get Role URL:
https://management.core.windows.net/<subscription-id>/services/hostedservices/<cloudservice-name>/deployments/<deployment-name>/roles/<role-name>
And then update the role using the Update Role URL:
https://management.core.windows.net/<subscription-id>/services/hostedservices/<cloudservice-name>/deployments/<deployment-name>/roleinstances/<role-name>
Both of those URLs are straight from the msdn pages. The GET request works and I get XML that matches what I see in the management console.
When I then add an element to the xml and send that back with a PUT on the update URL, I get a 200 response, but I never see the change in the management console. I also don't see any error message when I send gibberish. I'm connecting from C#, and a coworker suggested I could get the response with this:
var response = (HttpWebResponse)request.GetResponse();
Console.WriteLine(response.ToString());
But that gets me a 404 error.
Is there an extra step to commit the update? And how can I see the response that msdn mentions?
2 suggestions:
When I am just doing quick SMAPI work I use AzureTools (http://blogs.msdn.com/b/kwill/archive/2013/08/26/azuretools-the-diagnostic-utility-used-by-the-windows-azure-developer-support-team.aspx). Specifically, look in the Misc Tools section under "Service Management REST API". This will show you the full response.
To answer your question about how to get the response (txtSMAPIResponse is where AzureTools puts the response info):
System.IO.Stream receiveStream;
System.IO.StreamReader readStream;
Encoding encode;
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch (WebException ex)
{
txtSMAPIRequest.Text = request.Headers.ToString();
txtSMAPIResponse.Text = ex.Message + Environment.NewLine + Environment.NewLine + ex.Response.Headers.ToString();
try
{
receiveStream = ex.Response.GetResponseStream();
encode = System.Text.Encoding.GetEncoding("utf-8");
// Pipes the stream to a higher level stream reader with the required encoding format.
readStream = new System.IO.StreamReader(receiveStream, encode);
txtSMAPIResponse.Text += readStream.ReadToEnd();
// Releases the resources of the response.
response.Close();
// Releases the resources of the Stream.
readStream.Close();
}
catch
{
}
return;
}
txtSMAPIRequest.Text = request.Method + " " + request.RequestUri + " " + request.ProtocolVersion + Environment.NewLine + Environment.NewLine;
txtSMAPIRequest.Text += request.Headers.ToString();
txtSMAPIResponse.Text = (int)response.StatusCode + " - " + response.StatusDescription + Environment.NewLine + Environment.NewLine;
txtSMAPIResponse.Text += response.Headers + Environment.NewLine + Environment.NewLine;
receiveStream = response.GetResponseStream();
encode = System.Text.Encoding.GetEncoding("utf-8");
// Pipes the stream to a higher level stream reader with the required encoding format.
readStream = new System.IO.StreamReader(receiveStream, encode);
txtSMAPIResponse.Text += readStream.ReadToEnd();
// Releases the resources of the response.
response.Close();
// Releases the resources of the Stream.
readStream.Close();
}
I've got the same problem. In my case EndPointACL is not getting updated. Very painful thing is for every update , we have to send the entire ConfigurationSet; There is no way to update the ACL for particular end point.
A typical update looks like this:
<?xml version="1.0"?>
<PersistentVMRole xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ConfigurationSets>
<ConfigurationSet>
<ConfigurationSetType>NetworkConfiguration</ConfigurationSetType>
<InputEndpoints>
<InputEndpoint>
<LocalPort>100</LocalPort>
<Name>TCP-100</Name>
<Port>100</Port>
<Protocol>tcp</Protocol>
<EndpointACL>
<Rules>
<Rule>
<Order>1</Order>
<Action>deny</Action>
<RemoteSubnet>108.239.229.0/24</RemoteSubnet>
<Description>test-rule</Description>
</Rule>
</Rules>
</EndpointACL>
</InputEndpoint>
</InputEndpoints>
<SubnetNames>
<SubnetName>Subnet-1</SubnetName>
</SubnetNames>
</ConfigurationSet>
</ConfigurationSets>
</PersistentVMRole>

asynchronous client socket closing?

hey i want to ask a question about asynchronous socket communication on c#. everything is working well for now apart from closing clients. server doesnot close immediately the worker socket for client when a client close its conneciton. it closes a few time later. how can i resolve this problem??
check this link http://msdn.microsoft.com/en-us/library/fx6588te.aspx#2 my problem is that I can't keep the connection open after I receive a message from the client.
If I do as said on that sample the connection is closed immediately after receiving the message.
If I don't close the connection I can only receive one message and nothing more.
If you have any solution to this throw it this way.
I got it!
If anybody else has this problem they should do the following.
Change this code:
content = state.sb.ToString()
to this:
content = state.sb.ToString().TrimEnd(New Char() {ChrW(13)})
then you should change this:
If content.IndexOf("<EOF>") > -1 Then
to this:
If content.IndexOf(New Char() {ChrW(13)}) > -1 Then
this will receive Enter (chrw(13)) as the end of line.
then here:
Console.WriteLine("Read {0} bytes from socket. " + vbLf + " Data : {1}", content.Length, content)
' Echo the data back to the client.
Send(handler, content)
you should do this:
Console.WriteLine("Read {0} bytes from socket. " + vbLf + " Data : {1}", content.Length, mid(content,1,content.length -2))
' Echo the data back to the client.
'Send(handler, content)
content = String.Empty
state.sb.Clear()
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)
And your done.