In this tutorial you will learn how to handle incoming HTTP requests in your app service.
For that we build a simple REST API. A very simple one as you will see. It stores a single text, that can be accessed using the URL http://<app-platform-addr>/<app-name>/value. We implement GET, PUT and DELETE operations for reading, storing and deleting the text.
The used file and class names in this example are based on a newly created App with the name NewApp1 and the company name innovaphone. Your filenames might be different according to your settings. The IP address of the app platform in the example is "172.16.13.100". Please change to the correct IP address of your setup.
For testing the app we need to generate the HTTP requests. The command line tool CURL is a good choice for that. However you can use any tool that does the job. Let's take a look how this can be done and what's the expected behaviour on the network on the individual operations.
curl -i -X PUT http://172.16.13.100/newapp1/value -d "some text" -H "Content-Type: text/plain"
PUT /newapp1/value HTTP/1.1 Content-Type: text/plain Content-Length: 9 some text HTTP/1.1 200 OK Content-Length: 0
curl -i -X GET http://172.16.13.100/newapp1/value
GET /newapp1/value HTTP/1.1 HTTP/1.1 200 OK Content-Length: 9 Content-Type: text/plain; charset=utf-8 some text
curl -i -X DELETE http://172.16.13.100/newapp1/value
DELETE /newapp1/value HTTP/1.1 HTTP/1.1 200 OK Content-Length: 0
Let's start with the unchanged app and understand what it does with incoming HTTP requests. For that we compile and run it from Visual Studio. Then we do a GET request.
curl -i -X GET http://172.16.13.100/newapp1/value
GET /newapp1/value HTTP/1.1 HTTP/1.1 307 Temporary Redirect Location: ./13A000/value ...
The code that receives the GET and does the redirect can be found in NewApp1.cpp in the function NewApp1::WebserverPluginHttpListenResult
.
Let's add a debug printf to see what's going on and try again.
void NewApp1::WebserverPluginHttpListenResult(IWebserverPlugin * plugin, ws_request_type_t requestType, char * resourceName, const char * registeredPathForRequest, ulong64 dataSize)
{
if (requestType == WS_REQUEST_GET) {
debug->printf("GET %s", resourceName); // added this line
if (plugin->BuildRedirect(resourceName, _BUILD_STRING_, strlen(_BUILD_STRING_))) {
return;
}
}
plugin->Cancel(WSP_CANCEL_NOT_FOUND);
}
After building and restarting the app we send another GET request. The console window of visual studio shows our trace.
06-14 15:28:13.351 GET /value
Explanations
Let's start coding our REST API. We could do that by adding all the functionality to NewApp1::WebserverPluginHttpListenResult.
But in this tutorial we want to create our own UWebserverPlugin class, that just handles the requests to our path /value
.
RestApi
implementing the interface UWebserverPlugin
.
You only need to implement the interface function WebserverPluginHttpListenResult
for our purposes.
rest
and register it for the path value
using HttpListen.
Now we can start implementing the actual REST operations. For the text to be stored we first add a member char * value
to our RestApi class.
class RestApi : public UWebserverPlugin {
public:
RestApi();
~RestApi();
void WebserverPluginHttpListenResult(IWebserverPlugin * plugin, ws_request_type_t requestType, char * resourceName, const char * registeredPathForRequest, ulong64 dataSize) override;
char * value; // added this line
};
RestApi::RestApi()
{
value = 0; // added this line
}
RestApi::~RestApi()
{
if (value) free(value); // added this line
}
Then we can implement our PUT operation that stores the text. For that we need to create another class of type UWebserverPut.
#define DATA_SIZE_MAX 63
class RestApiPut : public UWebserverPut {
public:
RestApiPut(class RestApi * rest);
~RestApiPut();
void WebserverPutRequestAcceptComplete(IWebserverPut * const webserverPut) override;
void WebserverPutRecvResult(IWebserverPut * const webserverPut, void * buffer, size_t len) override;
void WebserverPutRecvCanceled(IWebserverPut * const webserverPut, void * buffer) override;
void WebserverPutSendResult(IWebserverPut * const webserverPut) override;
void WebserverPutCloseComplete(IWebserverPut * const webserverPut) override;
class RestApi * rest;
char data[DATA_SIZE_MAX + 1];
};
RestApiPut::RestApiPut(class RestApi * rest)
{
this->rest = rest;
}
RestApiPut::~RestApiPut()
{
}
void RestApiPut::WebserverPutRequestAcceptComplete(IWebserverPut * const webserverPut)
{
// start receiving data
webserverPut->Recv(data, DATA_SIZE_MAX);
}
void RestApiPut::WebserverPutRecvResult(IWebserverPut * const webserverPut, void * buffer, size_t len)
{
// store received value in REST API
data[len] = 0;
if (rest->value) free(rest->value);
rest->value = len ? _strdup(data) : nullptr;
// send 202 OK response with no content data
webserverPut->SetResultCode(WEBDAV_RESULT_OK, 0);
webserverPut->Send(0, 0);
}
void RestApiPut::WebserverPutRecvCanceled(IWebserverPut * const webserverPut, void * buffer)
{
}
void RestApiPut::WebserverPutSendResult(IWebserverPut * const webserverPut)
{
// response is sent, close
webserverPut->Close();
}
void RestApiPut::WebserverPutCloseComplete(IWebserverPut * const webserverPut)
{
// clean-up
delete webserverPut;
delete this;
}
Additionally we need to use our RestApiPut class to handle incoming PUT requests. Therefore we change our RestApi::WebserverPluginHttpListenResult
like follows.
void RestApi::WebserverPluginHttpListenResult(IWebserverPlugin * plugin, ws_request_type_t requestType, char * resourceName, const char * registeredPathForRequest, ulong64 dataSize)
{
if (requestType == WS_REQUEST_GET) {
debug->printf("RestApi GET");
plugin->Cancel(WSP_CANCEL_NOT_FOUND);
}
else if (requestType == WS_REQUEST_PUT) {
debug->printf("RestApi PUT");
plugin->Accept(new RestApiPut(this));
}
else if (requestType == WS_REQUEST_DELETE) {
debug->printf("RestApi DELETE");
plugin->Cancel(WSP_CANCEL_NOT_FOUND);
}
else {
plugin->Cancel(WSP_CANCEL_NOT_FOUND);
}
}
Now we need a way to read the stored values using GET requests.
RestApiGet
implementing the interface UWebserverGet
.
RestApi::WebserverPluginHttpListenResult
.
Now only the DELETE operation is missing.
RestApiDelete
implementing the interface UWebserverDelete
.
RestApi::WebserverPluginHttpListenResult
.