WebControls Commands
This document describes how to define and use RESTful commands within the HumanOS® UHAL WebControl plugin. REST calls are specified using CommandInfo nodes, each defining a unique command. These commands outline the following key elements:
The REST calls are specified as CommandInfo-nodes. Each command specifies:
- Command ID: A unique guid identifier for the command
- Command Name: The name of the command
- Address: The sub-URL, including any query parameters
- Arguments: A list of input, inout, and output arguments for the command
- Properties: Key attributes defining the REST call, including:
- Method: (Required) The HTTP method, such as
GET
,POST
, etc. - ContentType: (Optional) The content type of the request body
- Accept: (Optional) The accepted content type of the response
- CustomHeader: (Optional) Application-specific headers using the format
CustomHeader:<header>
- Method: (Required) The HTTP method, such as
Example Command Definition:
{
"Id": "56516249-915A-4B3E-9137-FBDF953B4342",
"Name": "GetAllNodes",
"Type": "CommandNode",
"Address": "nodes",
"Properties": [
{
"Name": "Method",
"Value": "GET"
},
{
"Name": "Accept",
"Value": "text/xml"
},
{
"Name": "CustomHeader:X-Restd-Session",
"Value": "close"
}
],
"Arguments": [
{
"Name": "Content",
"Type": "Output",
"DataType": "System.String"
}
]
}
Dynamic querying
Some REST endpoints require dynamic query parameters that can be specified at runtime.
These parameters can be passed to the command as arguments and referenced within the URL using the syntax $(<varname>)
.
Example of Dynamic Querying:
In this example, the id
parameter in the URL is dynamically replaced by the Id
argument passed to the command.
{
"Id": "56516249-915A-4B3E-9137-FBDF953B4342",
"Name": "GetNode",
"Type": "CommandNode",
"Address": "nodes/node?id=$(Id)",
"Properties": [
{
"Name": "Method",
"Value": "GET"
},
{
"Name": "Accept",
"Value": "text/xml"
}
],
"Arguments": [
{
"Name": "Id",
"Type": "Input",
"DataType": "System.Guid"
},
{
"Name": "Content",
"Type": "Output",
"DataType": "System.String"
}
]
}
Composing Requests and Parsing Responses
Certain commands require a request body to be sent to the web device. In these cases, dynamic C# scripting is used to construct the request body.
Script Location and Naming
Scripts are located in the \$(ConfigPath)\HumanOS.UHAL.WebControl
directory and must share the same name
as the corresponding command (e.g., GetAllNodes => GetAllNodes.cs).
If necessary, the script name can be customized by adding a "ScriptFile" property to the command definition.
Example with ScriptFile:
In this example: GetAllNodes =>
NodeHandling.cs
{
"Id": "56516249-915A-4B3E-9137-FBDF953B4342",
"Name": "GetAllNodes",
"Type": "CommandNode",
"Address": "nodes/node?id=$(Id)",
"Properties": [
{
"Name": "Method",
"Value": "GET"
},
{
"Name": "Accept",
"Value": "text/xml"
},
{
"Name": "ScriptFile",
"Value": "NodeHandling.cs"
}
],
"Arguments": []
}
Implementing the TAbstractHttpScriptObject Class
To handle custom request and response processing, create a class derived from TAbstractHttpScriptObject and override the necessary methods:
- composeRequest(): Composes the request body.
- parseResponse(): Parses the response content.
- parseError(): (Optional) Handles any errors encountered during the process.
Example Implementation:
using System;
using System.Collections;
using System.Collections.Generic;
using HumanOS.Kernel.Communication.Protocols;
using HumanOS.Kernel.UHAL.Script;
using Orion.Base; // Orion.Base is replaced by CyberTech for version 1.4 or newer
namespace HumanOS.UHAL.WebControl
{
/// <summary>
/// Content handler for composing and parsing the calls
/// </summary>
public class TContentHandler : TAbstractHttpScriptObject
{
///<see cref="TAbstractHttpScriptObject"/>
public override string composeRequest(ref string rstrAddress,
Dictionary<string, string> dicProperties,
Dictionary<string, object> dicInputArguments)
{
//check for a input value
if (!dicInputArguments.ContainsKey("MyType"))
{
throw new ArgumentException($"Argument ‘MyType’ not found in command Arguments.");
}
EMyEnum eMyType = (EMyEnum)Enum.Parse(typeof(EMyEnum), (string)dicInputArguments["MyType"]);
//add the associated url part
switch (eMyType)
{
case EMyEnum.Test1:
{
rstrAddress += "/testprofile";
break;
}
case EMyEnum.Test2:
{
rstrAddress += "/test2profile";
break;
}
default:
{
throw new ArgumentException($"Profile type {eMyType.ToString()} is unknown or not supported.");
}
}
return "";
}
///<see cref="TAbstractHttpScriptObject"/>
public override void parseResponse(string strAddress,
Dictionary<string, string> dicProperties,
string strContent,
EContentType eContentType,
Dictionary<string, object> dicInputArguments,
Dictionary<string, object> dicOutputArguments)
{
dicOutputArguments["Content"] = strContent;
return true;
}
///<see cref="TAbstractHttpScriptObject"/>
public override TCommandResult parseError(Dictionary<string, string> dicProperties,
Dictionary<string, object> dicInputArguments,
string strMethod,
string strAddress,
string strRequestBody,
THttpResponse Response)
{
TCommandResult nRetval;
try
{
nRetval = new TCommandResult(EProcessingState.BadProcessing, Response.Content);
}
catch (ThreadAbortException) { throw; }
catch (ThreadInterruptedException) { throw; }
catch (Exception)
{
nRetval = new TCommandResult(EProcessingState.BadProcessing, Response.ReasonPhrase);
}
return nRetval;
}
}
}
Working with Custom HTTP Headers
In certain scenarios, RESTful calls may require the inclusion of custom HTTP headers to meet specific application requirements, such as passing authentication tokens, setting content types, or including session information. The TAbstractHttpScriptObject class provides enhanced methods for handling custom headers, enabling dynamic composition and parsing of HTTP requests and responses.
Adding Custom Headers to Requests
Custom headers can be specified in the command's properties using the CustomHeader:<header>
format.
When composing the request, these headers are included directly with the request and are not added to the dicRequestHeaders dictionary.
Example with Custom Headers:
{
"Id": "CDE16249-915A-4B3E-9137-FBDF953B4342",
"Name": "GetNodeWithHeader",
"Type": "CommandNode",
"Address": "nodes/node?id=$(Id)",
"Properties": [
{
"Name": "Method",
"Value": "GET"
},
{
"Name": "Accept",
"Value": "application/json"
},
{
"Name": "CustomHeader:Authorization",
"Value": "Bearer $(AuthToken)"
}
],
"Arguments": [
{
"Name": "Id",
"Type": "Input",
"DataType": "System.Guid"
},
{
"Name": "AuthToken",
"Type": "Input",
"DataType": "System.String"
}
]
}
In the above example, the Authorization header is dynamically populated with the AuthToken argument.
C# Methods for Handling Headers
Composing the Request with Headers
The composeRequest
method includes a parameter for HTTP request headers:
public override string composeRequest(ref string rstrAddress,
Dictionary<string, string> dicProperties,
Dictionary<string, object> dicInputArguments,
Dictionary<string, IEnumerable<string>> dicRequestHeaders)
{
// Implement header handling logic here
return "";
}
In this method, dicRequestHeaders
contains the additional headers to be included in the HTTP request.
You can add headers as needed before finalizing the request.
Parsing the Response with Headers
Similarly, the parseResponse method accepts HTTP response headers:
public override void parseResponse(string strAddress,
Dictionary<string, string> dicProperties,
string strContent,
EContentType eContentType,
Dictionary<string, IEnumerable<string>> dicResponseHeaders,
Dictionary<string, object> dicInputArguments,
Dictionary<string, object> dicOutputArguments)
{
// Implement response parsing logic here
}
dicResponseHeaders provides access to the headers returned by the server, which can be used to handle pagination information, cookies, or other metadata included in the response.