HTTP
Last updated
Last updated
There are two ways of doing routing in Starcounter:
With the built-in Handle
API that handles HTTP
With the routing API found in the Authorization library
In most cases, it's recommended to use the API in the because of its ease of use. Although, there might be some cases where the Handle
API is needed.
This page describes the built-in Handle
API
Incoming HTTP 1.0/1.1 requests are caught using the static Handle
class.
Handlers can be registered anywhere and at any time. Though, in most cases, they are registered in the Main
method
The basic HTTP methods GET
, POST
, PUT
, DELETE
and PATCH
can be caught by methods with the same name in the Handle
class.
When matching incoming requests, some parts of the URI may contain dynamic data. This is handled by Starcounter by allowing you to define parameters in handlers. This is done by marking the dynamic part of the URI template with curly braces. The simplest use of the curly brace syntax is a single question mark {?}
. This indicates that there is a fragment of dynamic data in the URI. The type of the data is determined by the code delegate that follows.
In the above example, the delegate accepts string name
. This means that the parameter will be parsed as a string, for example: /hello/albert
, /hello/anna
. To accept an integer parameter, simply change the lambda parameter type.
The accepted URIs would, for example, be /squared?123
and /squared?-4321
To accept multiple dynamic fragments, add more curly braces. For each dynamic parameter there should be a parameter in the delegate. They are enumerated from left to right, so be careful to put the parameters in the right order.
The accepted URIs would be, for example: /serialnumbers/4534123
, /itemid/34321
One can also expect a database object as a parameter to a handler:
Such handler can be called with database object ID as a parameter or using special Self.GET
variant, which takes database class instance as a second parameter:
The handler above can be also called with object ID as parameter:
The CUSTOM
verb in the Handle
class makes it possible to register other HTTP methods or even catch all methods and URIs.
Request
ObjectA Request
parameter can be declared together with the enumerated parameters. It encapsulates the entire request.
To access certain request HTTP headers, use Headers[String]
accessor on a Request
object (same as for the Response
object):
Request cookies are accessible from Cookies
as a list of strings "name=value" (same as for Response object):
To obtain client IP address, use GetClientIpAddress()
on the Request
object.
Use the HandlerAppName
property to find out which application the request belongs to. This might be useful when working with request filters.
When creating (using the Handle
interface) and calling handlers (using the Self
interface), one can supply last HandlerOptions
parameter, which specifies certain options for handler calls or registration. Here are the notable handler options:
SkipRequestFilters
: used to declare a handler for which request filters will not be applied.
SkipResponsetFilters
: used to declare a handler for which response filters will not be applied.
SkipHandlersPolicy
: If the database flag "EnforceURINamespaces" is set to True, all application handlers are required to start with application name. In this case SkipHandlersPolicy
flag allows to register any URI handler.
SelfOnly
: registered handler is going to be accessible only inside codehost using Self
interface. SelfOnly
handlers are not registered in gateway, in comparison with normal handlers.
Examples:
Registering a handler that skips request filters:
Internal requests are requests made to handlers within same user application (sccode
instance) using Node
and X
. Internal requests and handlers can be nested and create a call hierarchy. Sometimes its useful to cast a specific exception deep down in the hierarchy and handle it on another level or let the system handle it (for example by automatically sending the response). This can be achieved using ResponseException
exception object. The following example illustrates this concept:
Handler /exc3
constructs and throws an instance of ResponseException
exception. Handler /exc2
catches the exception, modifies some data and re-throws the exception. Eventually, the ResponseException
is caught by outer system handler and ResponseObject
is automatically sent on the original Request req
. Note that ResponseException
mechanisms are working only within one user application (they are simple C# exceptions).
User can attach an arbitrary user object to ResponseException
by either constructor or UserObject
property.
After an HTTP handler is created - it can be unregistered with the Handle.UnregisterHttpHandler
method.
Attempt to HTTP upload of more than 1048576 bytes. Closing socket connection
The limit can be increased to a maximum of 2048576 bytes by changing the value of MaximumReceiveContentLength
in %userprofile%\Documents\Starcounter\Personal\scnetworkgateway.xml
.
When responding to a request from a handler such as Handle.GET
, a Response
object should be returned.
The Response
class has many implicit cast operators to make this convenient.
string
(mime type will be text/plain or text/html depending on the request Accept
header)
byte[]
(mime type will be the first one in the Accept
header)
Json
object (mime type will be application/json
)
int
, uint
, decimal
, bool
, double
, long
, ulong
, DateTime
returns a Javascript literal (JSON)
null
(no content)
When creating a Response
object, you have the choice of setting the body to a byte[]
, a string
.
When returning an instance of the Json
class, the mime type will application/json
and the body will contain a JSON string.
When returning a string, the returned mime type depends on the Accept
header of the request. If the request prioritizes text/html
or application\json
, the HTTP response will use this type accordingly. If no Accept header was provided, the mime-type text/plain
will be used.
Create a Response
object:
If an integer is returned from a delegate that will result in automatic response with a StatusCode
equal to the integer and default status description.
When null is returned from the handler, it's equal to returning the 404 Not found
status code.
When creating the Response
object, the body can be set to a byte[]
, string
, or Stream
.
As Starcounter schedules threads in a optimized way, it is recommended to allow Starcounter to handle streaming. This is done by assigning the stream
to the Response
object and then returning the response, relying on Starcounter to read the stream and send the network traffic on its own accord. Streamed object should allow getting length of the stream in bytes (Length
property).
Note that the stream
object is automatically closed when the stream data is sent completely or if the connection is dropped.
To resolve a static resource, there is a method Handle.ResolveStaticResource
which takes a resource URI and incoming request and returns a response representing this resource. Response however can be a 404, so to return a "nice" 404 page user has to do the following code, for example:
When the codehost starts, Starcounter adds a static resource resolver on the default user port (GET handler on URI "/{?}").
Sometimes, the Response
object cannot be returned immediately in the handler. One reason could be the access of third party resources or doing long-running jobs. By returning HandlerStatus.Handled
in the handler, the user indicates that the response will be returned later or that it already has been returned another way.
For example:
StatusCode
: 404, 501, etc
StatusDescription
: "Not Found", "Service Unavailable", etc
ContentType
: "text/html", "application/json", etc
ContentEncoding
: "gzip", etc
Cookies
: a list of entries like "MyCookie1=123", "MyCookie2=456", etc
Body
: "Here is response body!", etc
BodyBytes
: Byte[] bodyBytes = { 1, 2, 3, 4, 5};, etc
ConnFlags
: used to manipulate the connection with client.
The following Response.ConnectionFlags
values are available:
Response.ConnectionFlags.DisconnectImmediately
: immediately disconnects the associated connection with endpoint without sending any data first.
Response.ConnectionFlags.DisconnectAfterSend
: first sends given message to endpoint and then closes the corresponding connection.
Example:
Setting arbitrary HTTP headers on a Response
object is straightforward using the Headers
property:
Remarks:
StatusCode
default value is 200
StatusDescription
default value is "OK"
Server
default value is Server: Starcounter/#starcounter_version (Windows)
, for example: Server: Starcounter/2.3.1.7779 (Windows)
.
To access certain HTTP headers, use the Headers
accessor on the Response
or Request
object:
Examples:
Sometimes, when deep in the call hierarchy of Self.GET
, it's necessary to be able to set properties not on the current response, but directly on corresponding, not yet available, outgoing responses, like headers, status codes, messages, and cookies. To achieve that, the following static methods are available from the class Handle
:
The default limit on payloads in requests is 1048576 bytes, exceeding this limit will prevent the request from going through and this warning will be printed in the :
When sending large files, we recommend to use WebSocket instead of HTTP. That pattern is demonstrated in the FileUploadPage
(, ) in the sample app .
Please refer to the article for more information.