Search code examples
c++qthttprequesthl7-fhirhapi-fhir

RESTful API requests using Qt


I'm trying to get example found from this link to work.

GET and DELETE methods are working but I have some issue with PUT and POST. I get server replied: Bad Request. With qDebug() I get this error:

QNetworkReply::NetworkError(ProtocolInvalidOperationError)

To httprequestworker.cpp, I have changed request type to:

request_content.append("Content-Type: application/fhir+json");

Here is my function for getting contents of JSON file from computer and executing the whole input of request for the server:

void MainWindow::on_pushButton_clicked()
{
    QString url_str = "http://hapi.fhir.org/baseDstu3/Patient/4705560";

    HttpRequestInput input(url_str, "PUT");

    QString settings;
    QFile file;

    file.setFileName("C:/Users/Lauri/Desktop/FHIR/Omia testeja/themostsimplepatientJSON.json");
    file.open(QIODevice::ReadOnly | QIODevice::Text);
    settings = file.readAll();
    file.close();

    settings.remove(QRegExp("[\\n]"));
    qDebug() << settings;
    settings.toUtf8();
    input.add_var("key1", settings);


    HttpRequestWorker *worker = new HttpRequestWorker(this);
    connect(worker, SIGNAL(on_execution_finished(HttpRequestWorker*)), this, SLOT(handle_result(HttpRequestWorker*)));
    worker->execute(&input);
}

Here is the simple JSON I want to upload:

{
  "resourceType": "Patient",
  "text": {
    "status": "generated",
    "div": "<div xmlns='http://www.w3.org/1999/xhtml'>This is a test patient<a name='mm'/></div>"
  },
  "name": [
    {
      "use": "usual",
      "prefix": [
        "Mr"
      ],
      "given": [
        "Teppo",
        "Testi"
      ],
      "family": "Testinen"
    }
  ],
  "telecom": [
    {
      "value": "123456789",
      "system": "phone",
      "use": "home"
    }
  ],
  "gender": "male",
  "birthDate": "2018-08-21"
}

JSON file should be right because I have been able to POST and PUT with postman and other tools. Is there something obvious that I'm missing?

Captures from postman:

PUT Headers

PUT Body

POST Headers

POST Body

With qDebug() I can see that reading JSON is successful. I have been trying to find solution for ProtocolInvalidOperationError with no success.


Solution

  • the class HttpRequestWorker does not support the sending of json, so I will not use it in the example. For this case I will use QNetworkAccessManager directly:

    In the case of PUT you must add the id in the .json, so you can modify the file or do it by code, in this case use the second case:

    PUT:

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QNetworkAccessManager>
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private slots:
        void on_pushButton_clicked();
        void onManagerFinished(QNetworkReply *reply);
    
    private:
        Ui::MainWindow *ui;
        QNetworkAccessManager manager;
    };
    
    #endif // MAINWINDOW_H
    

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    #include <QFile>
    #include <QJsonDocument>
    #include <QJsonObject>
    #include <QMessageBox>
    #include <QNetworkReply>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        connect(&manager, &QNetworkAccessManager::finished, this, &MainWindow::onManagerFinished);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        QNetworkRequest request(QUrl("http://hapi.fhir.org/baseDstu3/Patient/4705560"));
        request.setRawHeader("Content-Type", "application/fhir+json");
        QFile file("/path/of/themostsimplepatientJSON.json");
        if(file.open(QIODevice::ReadOnly)){
            QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
            QJsonObject obj = doc.object();
            obj["id"] = "4705560"; // add ID
            doc.setObject(obj);
            manager.put(request, doc.toJson());
        }
    }
    
    void MainWindow::onManagerFinished(QNetworkReply *reply)
    {
        qDebug()<< reply->readAll();
    }
    

    output:

    "{\n  \"resourceType\": \"OperationOutcome\",\n  \"text\": {\n    \"status\": \"generated\",\n    \"div\": \"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\"><h1>Operation Outcome</h1><table border=\\\"0\\\"><tr><td style=\\\"font-weight: bold;\\\">INFORMATION</td><td>[]</td><td><pre>Successfully created resource \\\"Patient/4705560/_history/7\\\" in 5ms</pre></td>\\n\\t\\t\\t\\t\\t\\n\\t\\t\\t\\t\\n\\t\\t\\t</tr>\\n\\t\\t\\t<tr>\\n\\t\\t\\t\\t<td style=\\\"font-weight: bold;\\\">INFORMATION</td>\\n\\t\\t\\t\\t<td>[]</td>\\n\\t\\t\\t\\t\\n\\t\\t\\t\\t\\t\\n\\t\\t\\t\\t\\t\\n\\t\\t\\t\\t\\t\\t<td><pre>No issues detected during validation</pre></td>\\n\\t\\t\\t\\t\\t\\n\\t\\t\\t\\t\\n\\t\\t\\t</tr>\\n\\t\\t</table>\\n\\t</div>\"\n  },\n  \"issue\": [\n    {\n      \"severity\": \"information\",\n      \"code\": \"informational\",\n      \"diagnostics\": \"Successfully created resource \\\"Patient/4705560/_history/7\\\" in 5ms\"\n    },\n    {\n      \"severity\": \"information\",\n      \"code\": \"informational\",\n      \"diagnostics\": \"No issues detected during validation\"\n    }\n  ]\n}"
    

    POST:

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QFile>
    #include <QMessageBox>
    #include <QNetworkReply>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        connect(&manager, &QNetworkAccessManager::finished, this, &MainWindow::onManagerFinished);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        QNetworkRequest request(QUrl("http://hapi.fhir.org/baseDstu3/Patient")); // without ID
        request.setRawHeader("Content-Type", "application/fhir+json");
        QFile file("/path/of/themostsimplepatientJSON.json");
        if(file.open(QIODevice::ReadOnly)){
            QByteArray ba = file.readAll();
            manager.post(request, ba);
        }
    }
    
    void MainWindow::onManagerFinished(QNetworkReply *reply)
    {
        qDebug()<< reply->readAll();
    }
    

    output:

    "{\n  \"resourceType\": \"OperationOutcome\",\n  \"text\": {\n    \"status\": \"generated\",\n    \"div\": \"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\"><h1>Operation Outcome</h1><table border=\\\"0\\\"><tr><td style=\\\"font-weight: bold;\\\">INFORMATION</td><td>[]</td><td><pre>Successfully created resource \\\"Patient/4728838/_history/1\\\" in 3ms</pre></td>\\n\\t\\t\\t\\t\\t\\n\\t\\t\\t\\t\\n\\t\\t\\t</tr>\\n\\t\\t\\t<tr>\\n\\t\\t\\t\\t<td style=\\\"font-weight: bold;\\\">INFORMATION</td>\\n\\t\\t\\t\\t<td>[]</td>\\n\\t\\t\\t\\t\\n\\t\\t\\t\\t\\t\\n\\t\\t\\t\\t\\t\\n\\t\\t\\t\\t\\t\\t<td><pre>No issues detected during validation</pre></td>\\n\\t\\t\\t\\t\\t\\n\\t\\t\\t\\t\\n\\t\\t\\t</tr>\\n\\t\\t</table>\\n\\t</div>\"\n  },\n  \"issue\": [\n    {\n      \"severity\": \"information\",\n      \"code\": \"informational\",\n      \"diagnostics\": \"Successfully created resource \\\"Patient/4728838/_history/1\\\" in 3ms\"\n    },\n    {\n      \"severity\": \"information\",\n      \"code\": \"informational\",\n      \"diagnostics\": \"No issues detected during validation\"\n    }\n  ]\n}"
    

    The complete example can be found in the following link