I'd like to expose a REST API on my application, using the Mongoose web server and providing handlers for different queries.
An example of query would be like this (I'm only using GET for the moment, the rest of HTTP verbs will come later):
GET /items -> returns a list of all items in JSON
GET /item/by/handle/123456789 -> returns item that has handle 123456789
GET /item/by/name/My%20Item -> returns item(s) that have the name "My Item"
What I'm curious is how I should implement the parsing of these queries. I can easily parse the first, since it is simply a matter of if( query.getURI() == "/items") return ...
.
But for the next two queries, I have to manipulate std::
strings in a whole different way, using some std::string::find()
magic and offsets to get to the argument.
As an example, this is the implementation I have for the second query:
size_t position = std::string::npos;
std::string path = "/item/by/handle/";
if( (position = query.getURI().find(path) ) != std::string::npos )
{
std::string argument = query.getURI().substr( position + path.size() );
// now parse the argument to an integer, find the item and return it
}
What if I want to "templatize" this; meaning: I describe the path and the arguments I expect afterwards (an integer, a string, ....); and the code is automatically generated to handle this?
Tl;Dr: I want to be able to handle REST queries in C++ with something along these lines:
registerHandler( "/item/by/handle/[INTEGER]", myHandlerMethod( int ));
Is this possible?
A rather un-sexy, yet simple approach would be to simply use sscanf. Pardon the rather C-like code. Note, this doesn't provide the sort of syntax you're looking for, but it doesn't require any libraries, extensions, or boost.
For example,
int param;
int a, b;
char c[255];
/* recall that sscanf returns the number of variables filled */
if( 1 == sscanf( query.getURI(), "/item/by/handle/%d", ¶m ) ) {
handler( param );
} else if ( 3 == sscanf( query.getURI(), "/more/params/%d/%d/%s", &a, &b, &c ) ) {
anotherHandler( a, b, c );
} else {
// 404
}
While foraging through some Python code, I found that the Flask web framework has a particular way of parsing REST paths:
You declare a path like this:
@app.route('/post/<int:post_id>')
def show_post(post_id):
# show the post with the given id, the id is an integer
return 'Post %d' % post_id
And Flask builds up what's needed around to give you the integer you need.
That's exactly what I need, so I guess I'll have to do it by myself in C++.
I'll get Tom's answer since it's relevant, and I guess my implementation will look a bit like he suggests (altough I tend to prefer iostreams).
I'll leave this answer here to anybody lurking around.
The project drogon on github (project Drogon,a c++11 web framework)may be helpful to you. In this framework, you can register your handler method like this:
drogon::HttpAppFramework::registerHttpApiMethod("/api/v1/handle/{1}/{2}",yourMethod...);
the method maybe any callable object;
The HttpApiController class is a simple wrapper class that allows you to register functions with macros. There are examples in the project that demonstrate the use of these functions;
Hope it helps you..