Response to plugin from Orthanc API DELETE endpoint

I am trying to develop an Orthanc plugin against Orthanc version 1.12.8.

I notice that, when deleting an instance, series, or study, the API returns to an HTTP client some JSON indicating the deleted resources’ “RemainingAncestor”:

{
    "RemainingAncestor": {
        "ID": "11231bb6-426dbf6b-69897722-7ebab601-bd2c626c",
        "Path": "/studies/11231bb6-426dbf6b-69897722-7ebab601-bd2c626c",
        "Type": "Study"
    }
}

However, I am unable to get this response when calling the Orthanc built-in API from a plugin.

  • OrthancPluginRestApiDelete does not produce a body
  • OrthancPluginCallRestApi mutates answerBody, but answerBody.size is 0.

Sorry I am unable to provide a minimal reproduction example because I don’t know C/C++. The plugin is written in Rust, and the same code is able to successfully make GET requests and produce the body. I can share that code if it’s useful:

Hello,

Thanks for your question. I confirm that what you are trying to achieve is not possible with Orthanc <= 1.12.8.

As you observed, OrthancPluginRestApiDelete() is a “simplified” call that does not produces a body. So, you should indeed use OrthancPluginCallRestApi() to access the body of DELETE requests. But, in the current official releases of Orthanc, the body is simply not returned in the case of DELETE requests.

I have just committed a changeset that fixes this behavior, which will be part of forthcoming release 1.12.9. In the meantime, you’ll have to use the mainline builds.

With this fix, here is a minimal sample C++ plugin, using our official C++ SDK wrapper for conciseness:

// g++ -g -fPIC -shared Plugin.cpp OrthancPluginCppWrapper.cpp -DHAS_ORTHANC_EXCEPTION=OFF -o Plugin.so -I/usr/include/jsoncpp/ -I.

#include "OrthancPluginCppWrapper.h"

#include <string.h>
#include <json/value.h>

OrthancPluginErrorCode Callback(OrthancPluginRestOutput* output,
                                const char* url,
                                const OrthancPluginHttpRequest* request)
{
  try
  {
    OrthancPlugins::MemoryBuffer buffer;
    buffer.RestApiGet("/instances", false);

    Json::Value instances;
    buffer.ToJson(instances);

    if (instances.size() > 0)
    {
      std::string uri = "/instances/" + instances[0].asString();

      OrthancPlugins::MemoryBuffer answerBody;
      uint16_t httpStatus;
      OrthancPluginCallRestApi(OrthancPlugins::GetGlobalContext(), *answerBody, NULL, &httpStatus,
                               OrthancPluginHttpMethod_Delete, uri.c_str(), 0 /* no header */, NULL, NULL, NULL, 0 /* no body */, 0);

      Json::Value json;
      answerBody.ToJson(json);
      std::cout << json.toStyledString();  // This will print the "RemainingAncestor" information
    }

    OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, "ok\n", 3, "text/plain");
        
    return OrthancPluginErrorCode_Success;
  }
  catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
  {
    return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
  }
}


extern "C"
{
  ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c)
  {
    OrthancPlugins::SetGlobalContext(c);

    if (OrthancPluginCheckVersion(c) == 0)
    {
      char info[256];
      sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin",
              c->orthancVersion,
              ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
              ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER,
              ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER);
      OrthancPluginLogError(c, info);
      return -1;
    }

    OrthancPluginRegisterRestCallback(c, "/remove", Callback);
    return 0;
  }

  ORTHANC_PLUGINS_API void OrthancPluginFinalize()
  {
  }

  ORTHANC_PLUGINS_API const char* OrthancPluginGetName()
  {
    return "sample";
  }

  ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion()
  {
    return "0.0";
  }
}

The DELETE call applied to one DICOM instance stored in Orthanc can then be triggered by typing at the command line:

$ curl http://localhost:8042/remove

Kind Regards,
Sébastien-

1 Like