Voici une explication du principe, utilisable dans n'importe quel langage.
Vous avez une URL d'entrée, dite URL de récupération des entrées, qui, si elle est consultée, vous renvoie des données au format JSON. Le champ status, égal à OK ou NOK, sera systématiquement présent.
Pour répondre, vous devez consulter une URL de sortie, dite URL de soumission de la réponse, et envoyer à votre tour des données au format JSON, contenant le champ signature et les autres champs réponse qui sont demandés (selon le défi).
L'URL d'entrée doit être consultée avec la méthode GET (vous pouvez donc la tester avec un navigateur). L'URL de sortie doit être utilisée avec la méthode POST. Pour plus d'information sur ces méthodes, voir Hypertext Transfer Protocol sur Wikipédia.
Vous disposez d'un délai maximum pour répondre (généralement 5 secondes, cette durée est précisée dans le défi). Cela signifie que vous devez appeler l'URL de sortie en lui fournissant la réponse moins de 5 secondes après avoir consulté l'URL d'entrée. Si vous dépassez ce délai, il faudra récupérer un nouvel énoncé (en consultant à nouveau l'URL d'entrée).
Ce système est très simple à utiliser si on dispose des bons outils. Vous trouverez ci-dessous des exemples pour quelques langages courants.
Le code Python suivant, utilisant le module requests récupère un énoncé, ajoute les nombres a et b et renvoie la réponse en l'appelant c.
import requests
r = requests.get("https://pydefis.callicode.fr/defis/URLDEFI/get/votrelogin/code", verify=True)
print(r) # Pour contrôle
data = r.json()
print(data) # Pour contrôle
reponse = {'signature': data['signature'], 'c': data['a'] + data['b']}
r = requests.post("https://pydefis.callicode.fr/defis/URLDEFI/post/votrelogin/code", json=reponse, verify=True)
print(r) # Pour contrôle
data = r.json()
print(data) # Pour contrôle
Le code bash suivant s'appuie sur les outils curl (requêtes HTTP) et jq (décodage JSON) :
exo="ExempleURL"
login="toto"
code="12345"
geturl="https://.../$exo/get/$login/$code"
posturl="https://.../$exo/post/$login/$code"
# Contrôle de l'url :
echo "URL d'entrée : $geturl"
# On fait la requête et on parse le json
input=$(curl $geturl)
# Affichage du contenu pour contrôle
echo $input
# Récupération de la signature
signature=$(jq .signature <<< $input)
# Récupération des données
nba=$(jq .a <<< $input)
nbb=$(jq .b <<< $input)
# Calcul du résultat
nbc=$[ $nba + $nbb ]
rep="{\"signature\": $signature, \"c\": $nbc}"
echo $rep
curl "$posturl" -H "Content-Type: application/json" -d "$rep"
Le code Node.js suivant utilise le module got, mais il existe une quantité de clients HTTP équivalent en Javascript :
// Réglage des URLs
var urlget = "https://.../defis/URLDEFI/get/votrelogin/code
var urlpost = "https://.../defis/URLDEFI/post/votrelogin/code"
import got from 'got';
const defi = await got.get(urlget).json();
// Affichage de la requête
console.log(defi);
// Calcul de la réponse
var reponse = {
'signature': defi.signature,
'c': defi.a + defi.b,
}
console.log(reponse);
const verdict = await got.post(urlpost, { json: reponse }).json();
console.log(verdict);
Le même exemple, cette fois en Ruby.
# -*- coding: utf-8 -*-
require 'net/http'
require 'json'
uri = URI('https://.../defis/URLDEFI/get/votrelogin/code')
response = Net::HTTP.get uri
defi = JSON.parse response
puts defi
# Réponse
reponse = {
:signature => defi["signature"],
:c => defi["a"] + defi["b"],
}
puts reponse
# Envoi de la réponse
url = 'https://.../defis/URLDEFI/post/votrelogin/code'
res = Net::HTTP.post uri, reponse.to_json(), "Content-Type" => "application/json"
puts res.body
Voici un exemple en go, mais attention, il n'y a aucune gestion d'erreur.
package main
import (
"fmt"
"time"
"net/http"
"encoding/json"
"bytes"
)
const urlget = "https://.../defis/URLDEFI/get/votrelogin/code"
const urlpost = "https://.../defis/URLDEFI/post/votrelogin/code"
var myClient = &http.Client{Timeout: 10 * time.Second}
func main() {
r, _ := myClient.Get(urlget)
var question map[string]interface{}
json.NewDecoder(r.Body).Decode(&question)
fmt.Printf("%v\n", question)
reponse := map[string]interface{} {
"signature": question["signature"],
"c": int(question["a"].(float64)) + int(question["b"].(float64)),
}
jsonData, _ := json.Marshal(reponse)
payload := bytes.NewBuffer(jsonData)
fmt.Println(payload)
r, _ = myClient.Post(urlpost, "application/json", payload)
var verdict map[string]interface{}
json.NewDecoder(r.Body).Decode(&verdict)
fmt.Printf("%v\n", verdict)
}
La version C++ suivante utilise libcurl et liboost.
#include <iostream>
#include <string>
#include <sstream>
#include <cassert>
#include <curl/curl.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
//dependances : libcurl et liboost c++
//compilation
//g++ -std=c++11 -o ex_defi_web_json ex_defi_web_json.cpp -lcurl
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
std::stringstream requests_get(const std::string& url_get)
{
CURL *curl;
CURLcode res;
std::string readBuffer;
//init curl
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, url_get.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
//clean curl
curl_easy_cleanup(curl);
}
std::stringstream bufferstream(readBuffer);
return bufferstream;
}
//structure données d'entrée
//à adapter au defi
struct data_in
{
int a;
int b;
std::string signature;
std::string status;
};
//structure données résultat
//à adapter au defi
struct data_out
{
std::string signature;
int c;
};
void parse_json(std::stringstream& bufferstream,
data_in& d)
{
namespace pt = boost::property_tree;
pt::ptree root;
pt::read_json(bufferstream, root);
//récupération des champs/valeurs
//A adapter au défi
d.a = root.get<int>("a");
d.b = root.get<int>("b");
d.signature = root.get<std::string>("signature");
d.status = root.get<std::string>("status");
}
//calcul du résultat
//A adapter au défi
void calcul(const data_in& din,
data_out& dout)
{
dout.signature = din.signature;
dout.c = din.a + din.b;
}
//flux Json résultat
//A adapter au défi
std::stringstream data2json(const data_out& dout)
{
//champs/valeurs résultat
//à adapter au défi
std::stringstream ss;
ss <<"{\"signature\": \""<<dout.signature<<"\", \"c\": "<<int(dout.c)<<"}";
return ss;
}
void requests_post(const std::string& url_post,
const std::string& json_string)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, url_post.c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1L);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Expect:");
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_string.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(json_string.c_str()));
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
if(res != CURLE_OK)
{
std::cerr<<"curl_easy_perform() failed: "<<curl_easy_strerror(res)<<std::endl;
}
}
curl_easy_cleanup(curl);
}
int main(void)
{
std::string defi = "URLDEFI";
std::string login ="votrelogin";
std::string code = "code";
std::string url_get = "https://.../defis/"+defi+"/get/"+login+"/"+code;
std::string url_post = "https://.../defis/"+defi+"/post/"+login+"/"+code;
std::cout<<"GET data from "<<url_get<<std::endl;
std::stringstream bufferstream = requests_get(url_get);
//parse le flux json dans la structure data_in din
data_in din;
parse_json(bufferstream, din);
std::cout<<"json input data: "<<std::endl;
std::cout<<"a = "<<din.a<<std::endl;
std::cout<<"b = "<<din.b<<std::endl;
std::cout<<"signature = "<<din.signature<<std::endl;
std::cout<<"status = "<<din.status<<std::endl;
//effectue le calcul et met le resultat dans la structure data_out dout
data_out dout;
calcul(din, dout);
std::cout<<"resultat: "<<std::endl;
std::cout<<"c = "<<dout.c<<std::endl;
//transforme la structure data_out dout en flux json
std::stringstream json_stream = data2json(dout);
std::cout<<"json stream: "<<std::endl;
std::cout<<json_stream.str()<<std::endl;
//effectue une requête POST à url_post avec le flux json_stream
std::cout<<"POST json stream to "<<url_post<<std::endl;
requests_post(url_post, json_stream.str());
return 0;
}
On s'est senti un peu obligé de fournir un exemple PHP, mais pour être tout à fait honnête, on vous encourage plutôt à étudier d'autres langages, peut-être plus modernes et surtout mieux conçus. ;-)
<?php
// Les URLs
$urlget = "https://.../defis/URLDEFI/get/votrelogin/code";
$urlpost = "https://.../defis/URLDEFI/post/votrelogin/code";
/* Récupération de l'énoncé */
$json = file_get_contents($urlget);
echo $json;
$question = json_decode($json, true);
/* La réponse */
$reponse = json_encode(
array(
'signature' => $question["signature"],
'c' => $question["a"]+$question["b"],
)
);
echo $reponse . "\n";
/* On envoie */
$options = array(
'http' => array(
'header' => "Content-type: application/json\r\n",
'method' => 'POST',
'content' => $reponse,
),
);
$context = stream_context_create($options);
$verdict = file_get_contents($urlpost, false, $context);
echo $verdict;
[dependencies]
reqwest = { version = "0.13", features = ["blocking", "json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Fichier main.rs
use reqwest::blocking::Client;
use serde_json::{json, Value};
fn main() {
let client = Client::new();
// GET
let data: Value = client
.get("https://.../defis/URLDEFI/get/votrelogin/code")
.send().expect("Requête GET a échoué")
.json().expect("Donnée reçues pas en JSON");
println!("{:?}", data); // Pour contrôle
// Construction de la réponse
let reponse = json!({
"signature": data["signature"],
"c": data["a"].as_i64().unwrap() + data["b"].as_i64().unwrap()
});
// POST
let data: Value = client
.post("https://.../defis/URLDEFI/post/votrelogin/code")
.json(&reponse)
.send().expect("Requête POST a échoué")
.json().expect("Données reçues après le post pas en JSON");
println!("{:?}", data); // Pour contrôle
}