HTTP
Introduction
There are two ways of doing routing in Starcounter:
With the built-in
Handle
API that handles HTTPWith the routing API found in the Authorization library
In most cases, it's recommended to use the API in the Authorization library 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
Requests
Catching Incoming Requests
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
Catching Common HTTP Verbs
The basic HTTP methods GET
, POST
, PUT
, DELETE
and PATCH
can be caught by methods with the same name in the Handle
class.
Accepting Parameters in Requests
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
Database object as parameter in handler
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:
Catching Other Verbs
The CUSTOM
verb in the Handle
class makes it possible to register other HTTP methods or even catch all methods and URIs.
The Request
Object
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.
Handler Options
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 caseSkipHandlersPolicy
flag allows to register any URI handler.SelfOnly
: registered handler is going to be accessible only inside codehost usingSelf
interface.SelfOnly
handlers are not registered in gateway, in comparison with normal handlers.
Examples:
Registering a handler that skips request filters:
Exception Propagation Within Applications
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.
Unregistering HTTP Handlers
After an HTTP handler is created - it can be unregistered with the Handle.UnregisterHttpHandler
method.
Size Limit on Payloads
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 Administrator log:
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 sending large files, we recommend to use WebSocket instead of HTTP. That pattern is demonstrated in the FileUploadPage
(code-behind, HTML) in the sample app KitchenSink.
Responses
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 requestAccept
header)byte[]
(mime type will be the first one in theAccept
header)Json
object (mime type will beapplication/json
)int
,uint
,decimal
,bool
,double
,long
,ulong
,DateTime
returns a Javascript literal (JSON)null
(no content)
Returning Different Types
Response Object
When creating a Response
object, you have the choice of setting the body to a byte[]
, a string
.
JSON Object
When returning an instance of the Json
class, the mime type will application/json
and the body will contain a JSON string.
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.
Status Code and Status Description
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.
Null
When null is returned from the handler, it's equal to returning the 404 Not found
status code.
Streamed Body
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.
Serving Static Resources
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 "/{?}").
Delayed or Explicitly Handled Responses
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:
Please refer to the External HTTP calls and Node usage article for more information.
Summary of Adjustable Response Fields
StatusCode
: 404, 501, etcStatusDescription
: "Not Found", "Service Unavailable", etcContentType
: "text/html", "application/json", etcContentEncoding
: "gzip", etcCookies
: a list of entries like "MyCookie1=123", "MyCookie2=456", etcBody
: "Here is response body!", etcBodyBytes
: Byte[] bodyBytes = { 1, 2, 3, 4, 5};, etcConnFlags
: 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 200StatusDescription
default value is "OK"Server
default value isServer: Starcounter/#starcounter_version (Windows)
, for example:Server: Starcounter/2.3.1.7779 (Windows)
.To access certain HTTP headers, use the
Headers
accessor on theResponse
orRequest
object:
Examples:
Encode complex HTTP header values, such as those containing line separators, using base64 or similar encoding to reduce the chance of headers that are parsed the wrong way.
Setting Properties on Outgoing Response
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
:
Last updated