Tag Archives: webservice

Créer un WebService en C++ avec Qt

Un petit article pour évoquer la possibilité de faire un webservice, en C++/Qt.

Mon besoin est simple, je veux fournir une interface web pour réaliser des lancer de dés avec le systÚme de lancement de dés DiceParser, issu de Rolisteam.

DiceParser est écrit en C++ avec Qt5. A partir de ce constat, je pouvais soit faire un serveur web en C++, soit faire un site web en python/php/whatever pour lancer une commande systÚme et utiliser dice, le client en ligne de commande de DiceParser.

La deuxiĂšme solution est  la plus facile mais clairement un peu «sale» et niveau sĂ©curitĂ© ce n’est pas idĂ©al. Le «challenge» se trouve dans la premiĂšre mĂ©thode.
Je n’avais pas envie de rĂ©inventer la roue donc j’ai cherchĂ© une solution technique pour satisfaire mon besoin. Un composant C++/Qt qui permet de crĂ©er un serveur http avec possibilitĂ© d’ĂȘtre notifier Ă  chaque requĂȘte. J’ai trouvĂ© un composant qui rĂ©alise cela.

Le dépÎt git du composant : https://github.com/azadkuh/qhttp.git

Implémentation du serveur

[pastacode lang=”cpp” manual=”%20%20m_server%20%3D%20new%20qhttp%3A%3Aserver%3A%3AQHttpServer(this)%3B%0A%20%20%20%20m_server-%3Elisten(%20%2F%2F%20listening%20on%200.0.0.0%3A8080%0A%20%20%20%20%20%20%20%20%20%20%20%20QHostAddress%3A%3AAny%2C%20port%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%3D%5D(qhttp%3A%3Aserver%3A%3AQHttpRequest*%20req%2C%20qhttp%3A%3Aserver%3A%3AQHttpResponse*%20res)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20req-%3EcollectData(1024)%3B%0A%0A%09%09%2F%2F%20Ici%20mettre%20le%20code%20d’analyse%20de%20la%20requ%C3%AAte%0A%0A%20%20%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20if%20(%20!m_server-%3EisListening()%20)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20qDebug()%20%3C%3C%20%22failed%20to%20listen%22%3B%0A%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20else%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20qDebug()%3C%3C%20%22Server%20is%20On!!%22%3B%0A%20%20%20%20%7D” message=”Instancier le serveur web” highlight=”” provider=”manual”/]

Nous crĂ©ons une instance du serveur, nous dĂ©marrons l’Ă©coute sur le port (80 par dĂ©faut pour le protocole http).
L’Ă©coute est rĂ©alisĂ© par une fonction lambda, cela n’a rien d’obligatoire, mais c’est plus minimaliste.

Notre fonction est notifiĂ©e Ă  la rĂ©ception d’une requĂȘte http.

RĂ©cupĂ©rer les informations d’une requĂȘte

[pastacode lang=”cpp” manual=”%2F%2F%20R%C3%A9cup%C3%A8re%20les%20donn%C3%A9es%0Areq-%3EcollectData(1024)%3B%0A%0A%2F%2F%20R%C3%A9cup%C3%A8re%20l’url%20(les%20donn%C3%A9es%20du%20GET)%0AQString%20getArg%20%3D%20req-%3Eurl().toString()%3B%0A%0A%2F%2F%20D%C3%A9coupage%20des%20arguments%0AgetArg%3DgetArg.replace(%22%2F%3F%22%2C%22%22)%3B%0AQStringList%20args%20%3D%20getArg.split(‘%26’)%3B%0AQHash%3CQString%2CQString%3E%20m_hashArgs%3B%0Afor(%20auto%20argument%20%3A%20args)%0A%7B%0A%20%20%20%20QStringList%20keyValue%20%3D%20argument.split(‘%3D’)%3B%0A%20%20%20%20if(keyValue.size()%3D%3D2)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20m_hashArgs.insert(keyValue%5B0%5D%2CkeyValue%5B1%5D)%3B%0A%20%20%20%20%7D%0A%7D%0A%0A%2F%2F%20recherche%20dans%20la%20table%20des%20arguments%20la%20pr%C3%A9sence%20de%20fonction.%0Aif(m_hashArgs.contains(%22cmd%22))%0A%7B%0A%09%2F%2F%20r%C3%A9pondre%20%C3%A0%20la%20requ%C3%AAte%20reconnue.%0A%7D” message=”DĂ©coupage” highlight=”” provider=”manual”/]

Ici, nous analysons la requĂȘte pour identifier les actions Ă  rĂ©aliser. Nous cherchons le paramĂštre cmd, pour trouver la commander Ă  identifier. Le travail prĂ©alable est des crĂ©er une tableau associatif (dans une QHash) pour conserver les paramĂštres et leur valeur.

Une fois la commande de dĂ©s identifiĂ©s, il faut l’exĂ©cuter.

Exécuter la commande et répondre

[pastacode lang=”cpp” manual=”%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20QString%20result%20%3D%20startDiceParsing(QUrl%3A%3AfromPercentEncoding(m_hashArgs%5B%22cmd%22%5D.toLocal8Bit()))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20res-%3EsetStatusCode(qhttp%3A%3AESTATUS_OK)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20res-%3EaddHeader(%22Access-Control-Allow-Origin%22%2C%20%22*%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20res-%3EaddHeader(%22Access-Control-Allow-Methods%22%2C%20%22POST%2C%20GET%2C%20OPTIONS%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20res-%3EaddHeader(%22Access-Control-Allow-Headers%22%2C%20%22x-requested-with%22)%3B%0A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20QString%20html(%22%3C!doctype%20html%3E%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%3Chtml%3E%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%3Chead%3E%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20%20%3Cmeta%20charset%3D%5C%22utf-8%5C%22%3E%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20%20%3Ctitle%3ERolisteam%20Dice%20System%20Webservice%3C%2Ftitle%3E%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20%20%3Cstyle%3E.dice%20%7Bcolor%3A%23FF0000%3Bfont-weight%3A%20bold%3B%7D%3C%2Fstyle%3E%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%3C%2Fhead%3E%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%3Cbody%3E%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%251%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%3C%2Fbody%3E%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%3C%2Fhtml%3E%5Cn%22)%3B%0A%0Ares-%3Eend(html.arg(result).toLocal8Bit())%3B” message=”” highlight=”” provider=”manual”/]

La premiÚre action est de convertir la commande pour la transformer en données lisibles. Elle est actuellement encodé en mode pourcent. Qt fournit une méthode statique pour faire la conversion. Le resultat de cette conversion est ensuite envoyé à une fonction qui exécute la commande et donne le résultat.

Il faut ensuite générer la réponse. Le premier truc à définir est le code réponse du protocole http. Le module qhttp propose des raccourcis pour cela.

Ensuite, la rĂ©ponse doit contenir 3 paramĂštres dans les headers afin d’ĂȘtre accessible par des frameworks Ajax/javascript/Web 2.0 (vous ressentez mon dĂ©dain envers ces techno ?).
AprÚs la définition des headers, il faut créer le code html (ou autre) de la réponse.

Créer une page web cliente du service

[pastacode lang=”markup” manual=”%3C!DOCTYPE%20html%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%3Chtml%3E%20%20%20%20%20%20%0A%3Chead%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%3Cmeta%20charset%3D%22utf-8%22%3E%20%0A%3Cmeta%20name%3D%22Author%22%20content%3D%22Renaud%20GUEZENNEC%22%2F%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%3Ctitle%3ERolisteam%20Dice%20System%20Webservice%3C%2Ftitle%3E%20%20%0A%20%20%3Cstyle%3E.dice%20%7Bcolor%3A%23FF0000%3Bfont-weight%3A%20bold%3B%7D%3C%2Fstyle%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%3Cscript%20type%3D%22text%2Fjavascript%22%20src%3D%22js%2Fjquery-3.1.1.min.js%22%20%3E%3C%2Fscript%3E%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%3Cscript%20type%3D%22text%2Fjavascript%22%3E%20%20%0A%20%20%20%20function%20displayResult()%20%7B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20var%20request%20%3D%20%24.ajax(%7B%20%20%20%0A%20%20%20%20%20%20%20%20method%3A%20%22get%22%2C%20%20%20%20%0A%20%20%20%20%20%20%20%20url%3A%20%22http%3A%2F%2F127.0.0.1%3A8085%2F%22%2C%20%20%0A%20%20%20%20%20%20%20%20dataType%3A%20%22html%22%2C%20%20%0A%20%20%20%20%20%20%20%20async%3A%20false%2C%0A%20%20%20%20%20%20%20%20data%3A%20%7Bcmd%3A%20%24(‘%23cmd’).val()%7D%20%0A%20%20%20%20%20%20%7D)%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20request.done(function(data)%7B%20%20%0A%20%20%20%20%20%20%20%20var%20str%20%3D%22%3Cp%3E%22%20%0A%20%20%20%20%20%20%20%20str%20%3D%20str.concat(data%2C%22%3C%2Fp%3E%22)%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%24(‘.diceresult’).prepend(str)%3B%20%20%20%20%0A%20%20%20%20%20%20%7D)%3B%20%0A%20%20%20%20%7D%20%20%20%0A%20%20%3C%2Fscript%3E%20%20%0A%3C%2Fhead%3E%20%20%20%20%20%20%20%0A%3Cbody%3E%20%20%20%20%0A%3Cform%20method%3D%22POST%22%20action%3D%22%22%2F%3E%20%0A%3Cinput%20id%3D%22cmd%22%20name%3D%22cmd%22%2F%3E%20%20%0A%3Cinput%20id%3D%22roll%22%20name%3D%22roll%22%20type%3D%22button%22%20onclick%3D%22javascript%3AdisplayResult()%3B%20return%200%3B%22%20value%3D%22Roll%22%2F%3E%0A%3C%2Fform%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%3Ca%20href%3D%22index.html%22%3EClear%3C%2Fa%3E%20%0A%3Cdiv%20class%3D%22diceresult%22%3E%0A%3C%2Fdiv%3E%20%20%20%20%20%20%0A%3C%2Fbody%3E%20%0A%3C%2Fhtml%3E” message=”Page cliente” highlight=”” provider=”manual”/]

Le travail ici est assez simple. Il faut crĂ©er un formulaire pour permettre Ă  l’utilisateur de saisir la commande de dĂ©s. J’ai utilisĂ© JQuery comme framework javascript pour rĂ©cupĂ©rer la commande et envoyer la requĂȘte de mon webservice. Quand la requĂȘte s’est bien passĂ©e, le rĂ©sultat s’affiche dans la page sans recharger la page.

Le lien vers le code source: https://github.com/Rolisteam/DiceParser/tree/master/webserver
Le webservice fait partie de projet DiceParser: https://github.com/Rolisteam/DiceParser