Adding HttpAPI to a Server (.NET)
This topic will run you through the steps of adding HttpAPI support to a Remoting SDK server. If you don't have a server project yet, create a new CodeFirst Remoting SDK application using the corresponding template.
Then add the HttpApiDispatcher to it:
static class Program
{
public static int Main(string[] args)
{
ApplicationServer server = new ApplicationServer("HttpAPI Sample");
HttpApiDispatcher dispatcher = new HttpApiDispatcher();
dispatcher.ApiHost = "localhost:8099";
dispatcher.Server = server.NetworkServer.ServerChannel as IHttpServer;
server.Run(args);
return 0;
}
}
NotInheritable Class Program
Public Shared Function Main(args As String()) As Int32
Dim server As ApplicationServer = New ApplicationServer("HttpAPI Sample")
Dim dispatcher As HttpApiDispatcher = New HttpApiDispatcher
dispatcher.ApiHost = "localhost:8099"
dispatcher.Server = CType(server.NetworkServer.ServerChannel, IHttpServer)
server.Run(args)
Return 0
End Function
End Class
Program = class
public
class method Main(args: array of String): Integer;
begin
var server: ApplicationServer := new ApplicationServer('HttpAPI Sample');
var dispatcher: HttpApiDispatcher := new HttpApiDispatcher();
dispatcher.ApiHost := 'localhost:8099';
dispatcher.Server := IHttpServer(server.NetworkServer.ServerChannel);
server.Run(args);
exit 0;
end;
end;
import RemObjects.SDK.Server
let server = ApplicationServer("HttpAPI Sample")
let dispatcher = HttpApiDispatcher()
dispatcher.ApiHost = "localhost:8099"
dispatcher.Server = (server.NetworkServer.ServerChannel as? IHttpServer)
server.Run(C_ARGV.nativeArray)
package HttpAPISample;
import RemObjects.SDK.Server.*;
public class Program
{
public static int Main(String []args)
{
ApplicationServer server = new ApplicationServer("HttpAPI Sample");
HttpApiDispatcher dispatcher = new HttpApiDispatcher();
dispatcher.ApiHost = "localhost:8099";
dispatcher.Server = (IHttpServer)server.NetworkServer.ServerChannel;
server.Run(args);
return 0;
}
}
You can now run the server and go to http://localhost:8099/api
and have a look. The server will expose its meta-information there as JSON, e.g.:
{
"swagger": "2.0",
"info": {
"version": 1.0.0,
"title": "HttpAPI_Sample"
},
"basePath": /api/,
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
}
}
As an exersise modify the dispatcher's properties to expose base hostname, custom server name and version.
Now let's expose some server methods to play with. Add a new service to the server defined as such:
using System.Collections.Generic;
using System.Net;
using RemObjects.SDK.Server;
using RemObjects.SDK.Server.HttpApi;
namespace HttpAPISample
{
[Service]
public class SampleService : Service
{
private static IDictionary<string, string> _dataCache = new Dictionary<string, string>();
[ApiMethod(HttpApiPath = "convert/{value}")]
public string ConvertToString(int value)
{
return "The value is " + value.ToString();
}
[ApiMethod(HttpApiPath = "calculate", HttpApiMethod = ApiRequestMethod.Get)]
public int[] Calculate([ApiQueryParameter] int a, [ApiQueryParameter] int b)
{
int[] result = new int[4];
result[0] = a * b;
// This code line will result in the DivideByZero exception
// if the b parameter value is not provided or its value is 0
// Client will receive a generic 500 Internal Server Error response code
result[1] = a / b;
result[2] = a + b;
result[3] = a - b;
return result;
}
[ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Post, HttpApiResult = HttpStatusCode.Created)]
public void AddToCache(string key, [ApiQueryParameter]string value)
{
if (_dataCache.ContainsKey(key))
{
throw new ApiMethodException(HttpStatusCode.Conflict);
}
_dataCache.Add(key, value);
}
[ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Get)]
public string ReadFromCache(string key)
{
if (!_dataCache.ContainsKey(key))
{
throw new ApiMethodException(HttpStatusCode.NotFound);
}
return _dataCache[key];
}
[ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Put)]
public void UpdateCache(string key, [ApiQueryParameter]string value)
{
if (!_dataCache.ContainsKey(key))
{
throw new ApiMethodException(HttpStatusCode.NotFound);
}
_dataCache[key] = value;
}
[ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Delete)]
public void DeleteFromCache(string key)
{
if (!_dataCache.Remove(key))
{
throw new ApiMethodException(HttpStatusCode.NotFound);
}
}
}
}
Imports System.Collections.Generic
Imports System.Net
Imports RemObjects.SDK.Server
Imports RemObjects.SDK.Server.HttpApi
<Service()>
Public Class SampleService
Inherits Service
Private Shared _dataCache As IDictionary(Of String, String) = New Dictionary(Of String, String)
<ApiMethod(HttpApiPath:="convert/{value}")>
Public Function ConvertToString(ByVal value As Integer) As String
Return ("The value is " + value.ToString)
End Function
<ApiMethod(HttpApiPath:="calculate", HttpApiMethod:=ApiRequestMethod.Get)>
Public Function Calculate(ByVal a As Integer, ByVal b As Integer) As Integer()
Dim result() As Integer = New Integer((4) - 1) {}
result(0) = (a * b)
' This code line will result in the DivideByZero exception
' if the b parameter value is not provided or its value is 0
' Client will receive a generic 500 Internal Server Error response code
result(1) = (a / b)
result(2) = (a + b)
result(3) = (a - b)
Return result
End Function
<ApiMethod(HttpApiPath:="cache/{key}", HttpApiMethod:=ApiRequestMethod.Post, HttpApiResult:=HttpStatusCode.Created)>
Public Sub AddToCache(ByVal key As String, ByVal value As String)
If _dataCache.ContainsKey(key) Then
Throw New ApiMethodException(HttpStatusCode.Conflict)
End If
_dataCache.Add(key, value)
End Sub
<ApiMethod(HttpApiPath:="cache/{key}", HttpApiMethod:=ApiRequestMethod.Get)>
Public Function ReadFromCache(ByVal key As String) As String
If Not _dataCache.ContainsKey(key) Then
Throw New ApiMethodException(HttpStatusCode.NotFound)
End If
Return _dataCache(key)
End Function
<ApiMethod(HttpApiPath:="cache/{key}", HttpApiMethod:=ApiRequestMethod.Put)>
Public Sub UpdateCache(ByVal key As String, ByVal value As String)
If Not _dataCache.ContainsKey(key) Then
Throw New ApiMethodException(HttpStatusCode.NotFound)
End If
_dataCache(key) = value
End Sub
<ApiMethod(HttpApiPath:="cache/{key}", HttpApiMethod:=ApiRequestMethod.Delete)>
Public Sub DeleteFromCache(ByVal key As String)
If Not _dataCache.Remove(key) Then
Throw New ApiMethodException(HttpStatusCode.NotFound)
End If
End Sub
End Class
namespace HttpAPISample;
uses
System.Collections.Generic,
System.Net,
RemObjects.SDK.Server,
RemObjects.SDK.Server.HttpApi;
type
[Service]
SampleService = public class(Service)
private
class var _dataCache: IDictionary<String,String> := new Dictionary<String,String>();
public
[ApiMethod(HttpApiPath := 'convert/{value}')]
method ConvertToString(value: Integer): String;
begin
exit 'The value is ' + value.ToString();
end;
[ApiMethod(HttpApiPath := 'calculate', HttpApiMethod := ApiRequestMethod.Get)]
method Calculate(a: Integer; b: Integer): array of Integer;
begin
var &result: array of Integer := new Integer[4];
&result[0] := a * b;
// This code line will result in the DivideByZero exception
// if the b parameter value is not provided or its value is 0
// Client will receive a generic 500 Internal Server Error response code
&result[1] := a / b;
&result[2] := a + b;
&result[3] := a - b;
exit &result;
end;
[ApiMethod(HttpApiPath := 'cache/{key}', HttpApiMethod := ApiRequestMethod.Post, HttpApiResult := HttpStatusCode.Created)]
method AddToCache(key: String; value: String);
begin
if _dataCache.ContainsKey(key) then begin
raise new ApiMethodException(HttpStatusCode.Conflict);
end;
_dataCache.Add(key, value);
end;
[ApiMethod(HttpApiPath := 'cache/{key}', HttpApiMethod := ApiRequestMethod.Get)]
method ReadFromCache(key: String): String;
begin
if not _dataCache.ContainsKey(key) then begin
raise new ApiMethodException(HttpStatusCode.NotFound);
end;
exit _dataCache[key];
end;
[ApiMethod(HttpApiPath := 'cache/{key}', HttpApiMethod := ApiRequestMethod.Put)]
method UpdateCache(key: String; value: String);
begin
if not _dataCache.ContainsKey(key) then begin
raise new ApiMethodException(HttpStatusCode.NotFound);
end;
_dataCache[key] := value;
end;
[ApiMethod(HttpApiPath := 'cache/{key}', HttpApiMethod := ApiRequestMethod.Delete)]
method DeleteFromCache(key: String);
begin
if not _dataCache.Remove(key) then begin
raise new ApiMethodException(HttpStatusCode.NotFound);
end;
end;
end;
end.
import System.Collections.Generic
import System.Net
import RemObjects.SDK.Server
import RemObjects.SDK.Server.HttpApi
@Service
open class SampleService : Service {
private static var _dataCache: IDictionary<String,String>! = Dictionary<String,String>()
@ApiMethod(HttpApiPath = "convert/{value}")
public func ConvertToString(_ value: Int32) -> String! {
return "The value is " + value.ToString()
}
@ApiMethod(HttpApiPath = "calculate", HttpApiMethod = ApiRequestMethod.Get)
public func Calculate(_ a: Int32, _ b: Int32) -> Int32[] {
var result: Int32[] = Int32[](count: 4)
result[0] = a * b
// This code line will result in the DivideByZero exception
// if the b parameter value is not provided or its value is 0
// Client will receive a generic 500 Internal Server Error response code
result[1] = a / b
result[2] = a + b
result[3] = a - b
return result
}
@ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Post, HttpApiResult = HttpStatusCode.Created)
public func AddToCache(_ key: String!, _ value: String!) {
if _dataCache.ContainsKey(key) {
throw ApiMethodException(HttpStatusCode.Conflict)
}
_dataCache.Add(key, value)
}
@ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Get)
public func ReadFromCache(_ key: String!) -> String! {
if !_dataCache.ContainsKey(key) {
throw ApiMethodException(HttpStatusCode.NotFound)
}
return _dataCache[key]
}
@ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Put)
public func UpdateCache(_ key: String!, _ value: String!) {
if !_dataCache.ContainsKey(key) {
throw ApiMethodException(HttpStatusCode.NotFound)
}
_dataCache[key] = value
}
@ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Delete)
public func DeleteFromCache(_ key: String!) {
if !_dataCache.Remove(key) {
throw ApiMethodException(HttpStatusCode.NotFound)
}
}
}
package HttpAPISample;
import System.Collections.Generic.*;
import System.Net.*;
import RemObjects.SDK.Server.*;
import RemObjects.SDK.Server.HttpApi.*;
@Service
public class SampleService extends Service
{
private static IDictionary<String, String> _dataCache = new Dictionary<String, String>();
@ApiMethod(HttpApiPath = "convert/{value}")
public String ConvertToString(int value)
{
return "The value is " + value.ToString();
}
@ApiMethod(HttpApiPath = "calculate", HttpApiMethod = ApiRequestMethod.Get)
public int[] Calculate(@ApiQueryParameter int a, @ApiQueryParameter int b)
{
int[] result = new int[4];
result[0] = a * b;
// This code line will result in the DivideByZero exception
// if the b parameter value is not provided or its value is 0
// Client will receive a generic 500 Internal Server Error response code
result[1] = a / b;
result[2] = a + b;
result[3] = a - b;
return result;
}
@ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Post, HttpApiResult = HttpStatusCode.Created)
public void AddToCache(String key, @ApiQueryParameter String value)
{
if (_dataCache.ContainsKey(key))
{
throw new ApiMethodException(HttpStatusCode.Conflict);
}
_dataCache.Add(key, value);
}
@ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Get)
public String ReadFromCache(String key)
{
if (!_dataCache.ContainsKey(key))
{
throw new ApiMethodException(HttpStatusCode.NotFound);
}
return _dataCache[key];
}
@ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Put)
public void UpdateCache(String key, @ApiQueryParameter String value)
{
if (!_dataCache.ContainsKey(key))
{
throw new ApiMethodException(HttpStatusCode.NotFound);
}
_dataCache[key] = value;
}
@ApiMethod(HttpApiPath = "cache/{key}", HttpApiMethod = ApiRequestMethod.Delete)
public void DeleteFromCache(String key)
{
if (!_dataCache.Remove(key))
{
throw new ApiMethodException(HttpStatusCode.NotFound);
}
}
}
Note how several service methods are defined on the same HTTP path. Also take a look how custom error codes are returned to the client.
Restart the server and go to http://localhost:8099/api
again. If everything was done correct you'll see much longer service description JSON than in the first time.
You can open the online Swagger Editor and copy-paste the API description JSON from http://localhost:8099/api
. This tool not only allows to generate the client-side code to access the server, it also allows to try out the server methods:
Play with different methods and key values to see how the *cache method will return different results and Http result codes.