restServerEndpointAdd (Function)

Top  Previous  Next

Architecture:

NX32L

Device support:

ALL

Firmware version:

2.10.00


Create an endpoint on the REST server.

 

Endpoints are used to provide services to the clients.

Endpoints specifies which URLs should be mapped to different callbacks.

It is possible to add endpoints to a running server, but the server must have at least one endpoint to start.

A server can have up to 128 endpoints.

 

Note that the request provided to the callback can only be read and may not be freed or used outside the callback.

 

Incoming requests are limited to a maximum of 20 kB per post parameter and a max body size of 1 MB.

 

Multiple prioritized endpoints for similar URLs:

If there is shared code that should be run for multiple requests, e.g. for validate the user, it is possible to configure it so the request calls multiple endpoints in a sequence.

The url_prefix and url_format parameters are used to control which endpoints will be called depending on the URL. The variables specified in url_format will be extracted from the URL and made available as part of the query parameters.

The prio parameter is used to control the priority of the endpoint, which controls the order the matching endpoints are called in.

When a endpoint callback does not use 0 as return code, no more callbacks will be called for the request, skipping the endpoints with lower priority.

 

An example uses a server is configured to use the following endpoints:

 

ID

url_prefix

url_format

prio

Description

 

1

/test

/*

0

Matches everything, called as the first because prio=0. Suitable for access control.

 

2

/test

/*

5

Same as #1, called last as prio=5. Suitable for a default response.

 

3

/test

/:a

2

Matches URL with 1 segment after the prefix. Variable "a" will be set to the value of it.

 

4

/test

/:a/:b

2

Matches URL with 2 segments after the prefix. Variables "a" and "b" will be set to the values of them.

 

5

/test

/:a/:b/*

2

Matches URL with 2 or more segments after the prefix. Variables "a" and "b" will be set to the value of the first two.

 

6

/test

/:a/4

1

Matches URL with 2 segments after the prefix, the second one must be "4". Variable "a" will be set to the value of the first segment. Higher priority so it is called before the more generic endpoints.

 

The callbacks all return 0, so all the matching endpoints will be called.

Accessing the following URLs will trigger the endpoints in the shown order:


URL

Endpoints


/test

1 2


/test/1

1 3 2


/test/1/2

1 4 5 2


/test/1/2/3

1 5 2


/test/1/4

1 4 5 6 2


/test/1/4/5

1 5 2

As can be seen, endpoint 1 and 2 are called for all the requests, while e.g. endpoint 6 is only called when there are two segments after the prefix and the last segment is "4".

 

 

 

Input:

handle : SYSHANDLE

A handle to the server.

 

method : STRING (default "*")

The HTTP method to handle with this endpoint. Typical methods are "GET", "POST "and "PUT". Use "*" to handle any method.

 

url_prefix : STRING

This is an optional fixed part of the URL that must match the start of the request URL.

 

url_format : STRING

The format of the endpoint URL, which can include variables that will be extracted.

Separate words with "/". Prefix variables with "@" or ":".

End url_format with "*" to not test the rest of the URL.

 

The variables can be read using restReqQueryGet.

 

prio : INT

The priority of the endpoint. 0 is the highest priority.

This is needed when multiple endpoints might match the same URL and to call multiple callbacks for the same request, to e.g. authenticate the user in one callback and then generate the data in another.

 

cb_func : CALLBACK

Callback to call when the endpoint is called.

It is called with a handle to the request from the client, a handle to an empty response to fill out and with the value of the user argument.

A positive return value is used as the HTTP status code, e.g. 200 (OK) for success, 404 (Not Found) if something is not found.

A return value of 0 will cause it to continue to the next callback for the same endpoint.

A return value of -1 will cause it to complete the transaction and send status code 401 (Unauthorized) to the client, requesting basic authentication from the client, see restReqGetBasicAuthenthication.

A return value of -2 will cause it to complete the transaction and send status code 500 (Internal Server Error) to the client.

As the response can not be sent before this callback has returned, it should try to return as quickly as possible.

 

cb_arg : DINT

User argument to pass on to the callback.

 

 

Returns: INT

1


- Success

0


- Not supported

-1


- Invalid server.

-2


- The maximum number of endpoints is already in use.

-3


- Failed to create endpoint.

 

Declaration:

FUNCTION restServerEndpointAdd : INT;
VAR_INPUT
  handle     : SYSHANDLE;
  method     : STRING := "*";
  url_prefix : STRING;
  url_format : STRING;
  prio       : INT;
  cb_func   : CALLBACK;
  cb_arg     : DINT;
END_VAR;

 

Callback declaration:

FUNCTION CALLBACK restServerCallback : INT;
VAR_INPUT
  req     : SYSHANDLE;
  resp    : SYSHANDLE;
  arg     : DINT;
END_VAR;

 

Example:

//-----------------------------------------------------------------------------
//
// Starts a server with multiple prioritized endpoints.
//
//-----------------------------------------------------------------------------
INCLUDE rtcu.inc
 
//  These are the global variables of the program
VAR
  serv: SYSHANDLE;
END_VAR;
 
 
FUNCTION CALLBACK devicesGetCallback: INT;
VAR_INPUT
  req         : SYSHANDLE; // Request
  resp        : SYSHANDLE; // Response
  arg         : DINT;     // User argument
END_VAR;
VAR
  str, name, url:STRING;
  body  : STRING := "";
  i     : INT;
  rc    : INT;
END_VAR;
  // Show client address
  restReqClientAddressGet (req:=req, address:=str);
  DebugFmt(message:="Request type \4 from "+str, v4:=arg);
 
  // Read body content from previous callback, if it is present
  restRespBodyGetString(resp:=resp, str:=body);
 
  // Add header for this callback to body
  body := body+" -- EP "+dintToStr(v:=arg)+" -- $N";
 
  // Get request URL
  rc := restReqUrlGet(req:=req, method:=str, url:=url);
  IF rc > 0 THEN
    DebugFmt(message:=" "+str+" "+url);
    body:=body+str+" "+url+"$N";
  ELSE
    DebugFmt(message:="restReqUrlGet: \1", v1:=rc);
  END_IF;
 
 
  // Read query parameters  
  DebugFmt(message:=" Query:");
  body:=body+"Query:$N";
  i:=0;
  rc := restReqQueryKey(req:=req, idx:=i, name:=name);
  WHILE rc > 0 DO
    restReqQueryGet(req:=req, name:=name, value:=str);
    body:=body+name+"="+str+"$N";
    DebugMsg(message:=" "+name+"="+str);
    i:=i+1;
    rc := restReqQueryKey(req:=req, idx:=i, name:=name);
  END_WHILE;
 
  body:=body+"$N";
 
  // Set response body
  restRespBodySetString(resp:=resp, str := body);
  // Set content-type
  restRespHeaderSet(resp:=resp, name:="Content-Type", value:="text/plain");
 
  // Return 0 to continue with the next callback.
  // Return status code to stop and return the response.
  devicesGetCallback := 0;
 
END_FUNCTION;
 
 
 
PROGRAM ep_ex;
// These are the local variables of the program block
VAR
  iface : SINT := 2;
  port  : DINT := 80;
  rc    : INT;
END_VAR;
// The next code will only be executed once after the program starts
  netOpen(iface:=iface);
 
  rc := restServerCreate(handle:=serv, port:=port);
  DebugFmt(message:="restServerCreate: \1", v1:=rc);
 
  rc := restServerEndpointAdd(handle:=serv, method:="*", cb_func:=@devicesGetCallback,
  url_prefix:="/test",url_format:= "/*", cb_arg:=1, prio:=0);
  DebugFmt(message:="restServerEndpointAdd: \1", v1:=rc);
 
  rc := restServerEndpointAdd(handle:=serv, method:="*", cb_func:=@devicesGetCallback,
  url_prefix:="/test",url_format:= "/*", cb_arg:=2, prio:=5);
  DebugFmt(message:="restServerEndpointAdd: \1", v1:=rc);
 
  rc := restServerEndpointAdd(handle:=serv, method:="*", cb_func:=@devicesGetCallback,
  url_prefix:="/test",url_format:= "/:a", cb_arg:=3, prio:=2);
  DebugFmt(message:="restServerEndpointAdd: \1", v1:=rc);
 
  rc := restServerEndpointAdd(handle:=serv, method:="*", cb_func:=@devicesGetCallback,
  url_prefix:="/test",url_format:= "/:a/:b", cb_arg:=4, prio:=2);
  DebugFmt(message:="restServerEndpointAdd: \1", v1:=rc);
 
  rc := restServerEndpointAdd(handle:=serv, method:="*", cb_func:=@devicesGetCallback,
  url_prefix:="/test",url_format:= "/:a/:b/*", cb_arg:=5, prio:=2);
  DebugFmt(message:="restServerEndpointAdd: \1", v1:=rc);
 
  rc := restServerEndpointAdd(handle:=serv, method:="*", cb_func:=@devicesGetCallback,
  url_prefix:="/test",url_format:= "/:a/4", cb_arg:=6, prio:=1);
  DebugFmt(message:="restServerEndpointAdd: \1", v1:=rc);
 
 
  WHILE NOT netConnected(iface:=iface) DO
    Sleep(delay:=1000);
     
  END_WHILE;
 
  rc := restServerStart(handle:=serv);
  DebugFmt(message:="restServerStart: \1", v1:=rc);
 
  DebugFmt(message:="http://"+sockIPToName(ip := sockGetLocalIP(iface:=iface))+":\4/test", v4:=port);
 
 
 
BEGIN
// Code from this point until END will be executed repeatedly
 
END;
 
END_PROGRAM;