SGApps Server - very fast NodeJS WebServer
A network solution for web applications.
Since this application is fully compatible with nodejs-mvc, I decided to replace nodejs-mvc with this new approach. SGApps Server is completely new solution, that will be improved continuously thats why I will work on this project instead of nodejs-mvc
by Sergiu Gordienco < sergiu.gordienco@sgapps.io >
Features
- 🚀 Much Faster with common used Interface
- 💻 Pretty Logger Integrated
- 🏗️ AccessLogs ( Combined )
- 📈 GoAccess Statistics Support ( v1.5.6 )
- 📈 AWSTats Statistics Support
- 📑 TypeScript Typings ( Intellisense Support )
- 📚 support with MVC Framework
Authors
- Gordienco Sergiu < sergiu.gordienco@sgapps.io >
License
the license is Apache-2.0, so one of the requirements is to include reference to this project
Samples
Simple Integration ( Similar to ExpressJS )
const { SGAppsServer } = require('sgapps-server');
const app = new SGAppsServer();
app.get('/', function (request, response) {
response.send('hello world')
});
app.server().listen(8080, () => {
app.logger.log('Server is running on port 8080');
});
Example of Integration with SessionSupport 🍦
// ========================================
// Start your 🚀 Web-Server app Extended
// ========================================
const { SGAppsServer } = require('sgapps-server');
const app = new SGAppsServer();
app.get('/', function (request, response) {
response.send('hello world session#' + request.session.id);
})
app.whenReady.then(() => {
app.SessionManager.cookie = 'ssid';
app.SessionManager.SESSION_LIFE = 120; // seconds
app.server().listen(8080, () => {
app.logger.log('Server is running on port 8080');
});
}, app.logger.error);
Example of Integration with AccessLogs for AWStats or GoAccess
const { SGAppsServer } = require('sgapps-server');
const app = new SGAppsServer({
decorators: [
require('sgapps-server/decorators/access-logger')
]
});
app.AccessLoggerPaths['default'] = {
isEnabled: true,
path: configuration.database.filesystem.logs + 'default/{year}/{month}/data-{worker-id}.log'
};
app.whenReady.then(() => {
app.server().listen(8080, () => {
app.logger.log('Server is running on port 8080');
});
}, app.logger.error);
Example Advanced of Integration with AccessLogs for AWStats or GoAccess
const { SGAppsServer } = require('sgapps-server');
const app = new SGAppsServer({
decorators: [
require('sgapps-server/decorators/access-logger')
]
});
app.AccessLoggerPaths['default'] = {
isEnabled: true,
// modify the row
waitAllHandlers: true,
path: configuration.database.filesystem.logs + 'default/{year}/{month}/data-{worker-id}.log',
handle: function (data) {
// used for updating of filtering data
console.info("LOGGER Data", data);
return data.match(/\.txt\"/) ? null : data;
}
};
app.get(/^\/api\//, function () {
// log all request from api path into separate file
request.AccessLoggerPaths['api'] = {
isEnabled: true,
path: 'api/access.log'
}
})
app.whenReady.then(() => {
app.server().listen(8080, () => {
app.logger.log('Server is running on port 8080');
});
}, app.logger.error);
Full API documentation can be found on
API
Table of Contents
- SGAppsServerRequestSession
- TemplateManager
- SGAppsServerRequest
- routeMatch
- SGAppsServerResponse
- TemplateManagerRenderOptions
- TemplateManagerViewer
- TemplateManagerTemplate
- SGAppsServerEmail
- SGAppsSessionManagerOptions
- SGAppsServerRequestSessionCache
- MountUpdatedURL
- FaceboxTemplate
- SGAppsServerRequestCookie
- SGAppsServerDecoratorsLibrary
- SGAppsServerErrorCallBack
- SGAppsServerErrorOnlyCallback
- FSLibrary
- SGAppsServerShared
- SGAppsServerRequestFile
- SGAppsServerRequestPostDataItem
- SGAppsServerDecorator
-
SGAppsServer
- CookiesManager
- _server
- _decorators
- TemplateManager
- _options
- STATUS_CODES
- shared
- logger
- mountPath
- SessionManager
- _fs
- _path
- EXTENSIONS
- _requestListeners
- MAX_POST_SIZE
- whenReady
- handleRequest
- handleErrorRequest
- handleStaticRequest
- handle
- server
- use
- post
- get
- head
- put
- trace
- delete
- options
- connect
- patch
- all
- finalHandler
- handlePostData
- LoggerBuilder
- RequestPathStructureMap
- SGAppsServerDictionary
- RequestPathStructure
- RequestHandler
- SGAppsServerOptions
- SGAppsSessionManager
- RequestSessionDecorator
- SGAppsServerDictionaryRunCallBack
- request
- request
- LoggerBuilderPrompt
- SGAppsServerHandlerPostData
SGAppsServerRequestSession
Type: function (request, options)
-
request
SGAppsServerRequest -
options
SGAppsSessionManagerOptions
_created
Type: number
_ip
Type: string
_confirmed
Session was received from previously saved cookie
Type: boolean
_id
Type: string
_options
Type: SGAppsSessionManagerOptions
data
Type: object
destroy
Type: function ()
TemplateManager
Type: function (options)
_options
Type: object
-
_fs
FSLibrary
_viewer
Type: TemplateManagerViewer
_env
templateExists
Type: function (templateName): boolean
-
templateName
string
remove
Type: function (templateName)
-
templateName
string
add
Type: function (templateName, filePath)
addList
Type: function (templates)
get
Type: function (templateName): TemplateManagerTemplate
-
templateName
string
render
Type: function (response, templateName, vars)
-
response
SGAppsServerResponse -
templateName
string -
vars
Object<string, any>?
SGAppsServerRequest
Type: function (request, server)
-
request
IncomingMessage -
server
SGAppsServer
request
Type: IncomingMessage
_postDataBuffer
post data buffer cache
Type: Buffer
getMountUpdatedUrl
Type: function (url): MountUpdatedURL
-
url
string
urlInfo
Type: object
-
original
string -
origin
string -
domain
string full domain of url -
domain_short
string domain without "www." -
pathname
string url's pathname -
reqQuery
string url's query from '?' -
protocol
string url.split('://')[0] -
url
string -
url_p
string -
isIp
string domain or Ip
query
Type: object
mountPath
Type: string
body
Type: object
bodyItems
Type: Array<SGAppsServerRequestPostDataItem>
cookies
Type: SGAppsServerRequestCookie
MAX_POST_SIZE
Type: number
Examples:
// changing max post size to 4Mb
request.MAX_POST_SIZE = 4 * 1024 * 1024;
// reset max post size to global value
request.MAX_POST_SIZE = -1;
files
Type: Object<string, Array<SGAppsServerRequestFile>>
fileItems
Type: Array<SGAppsServerRequestFile>
_destroy
Array of functions to be called on response end
params
Array of functions to be called on response end
Type: SGAppsServerRequest.RequestParams
_flags
Array of functions to be called on response end
Type: object
-
complete
boolean The message.complete property will be true if a complete HTTP message has been received and successfully parsed. -
aborted
boolean The message.aborted property will be true if the request has been aborted. -
closed
boolean Indicates that the underlying connection was closed. -
_DEBUG_MAX_HANDLER_EXECUTION_TIME
number? define a bigger request timeout
_parseDeepFieldName
Automatically used procedure for parsing formData field name if option server._options._REQUEST_FORM_PARAMS_DEEP_PARSE = true
. it's by default enabled but can be disabled when needed
Type: function (container, fieldName, fieldData, options)
-
container
object -
fieldName
string -
fieldData
any -
options
object?-
options.transform2ArrayOnDuplicate
boolean (optional, defaultfalse
)
-
Examples:
paramsContainer = {};
request._parseDeepFieldName(paramsContainer, 'test[arr][data]', 2);
request._parseDeepFieldName(paramsContainer, 'test[arr][]', new Date());
request._parseDeepFieldName(paramsContainer, 'test[arr][]', 2);
request._parseDeepFieldName(paramsContainer, 'test[data]', 2);
// if _debug enabled warns will be emitted
// [Warn] [Request._parseDeepFieldName] Writing Array field "test[arr][]" into a object
// [Warn] [Request._parseDeepFieldName] Overwriting field "test[data]" value
console.log(paramsContainer)
{
"test": {
"arr": {
"1": "2021-02-12T21:23:01.913Z",
"2": 2,
"data": 2
},
"data": 2
}
}
paramsContainer = {};
request._parseDeepFieldName(paramsContainer, 'test[arr][]', new Date());
request._parseDeepFieldName(paramsContainer, 'test[arr][]', 2);
request._parseDeepFieldName(paramsContainer, 'test[arr][data]', 2);
request._parseDeepFieldName(paramsContainer, 'test[data]', 2);
// if _debug enabled warns will be emitted
// [Warn] [Request._parseDeepFieldName] Converting array to object due incorrect field "test[arr][data]" name
console.log(paramsContainer)
{
"test": {
"arr": {
"0": "2021-02-12T21:34:47.359Z",
"1": 2,
"data": 2
},
"data": 2
}
}
paramsContainer = {};
request._parseDeepFieldName(paramsContainer, 'test[arr][]', new Date());
request._parseDeepFieldName(paramsContainer, 'test[arr][]', 2);
request._parseDeepFieldName(paramsContainer, 'test[data]', 2);
console.log(paramsContainer)
{
"test": {
"arr": [
"2021-02-12T21:26:43.766Z",
2
],
"data": 2
}
}
session
Type: SGAppsServerRequestSession
postData
request's post received data
RequestParams
Type: (Object<(string | number), string> | Array<string>)
routeMatch
Type: function (route, url, strictRouting, _cache)
-
route
RequestPathStructure -
url
string -
strictRouting
boolean -
_cache
object
SGAppsServerResponse
Type: function (response, server)
-
response
ServerResponse -
server
SGAppsServer
response
Type: ServerResponse
pipeFile
Type: function (filePath, callback)
-
filePath
string -
callback
SGAppsServerErrorOnlyCallback represents aFunction(Error)
send
Type: function (data, options)
sendError
Type: function (error, options)
_destroy
Array of functions to be called on response end
redirect
if it returns false
than the action was not possible
Type: function (url, options)
_flags
Array of functions to be called on response end
Type: object
-
finished
boolean will be true if response.end() has been called. -
sent
boolean Is true if all data has been flushed to the underlying system, immediately before the 'finish' event is emitted. -
closed
boolean Indicates that the the response is completed, or its underlying connection was terminated prematurely (before the response completion).
pipeFileStaticCallback
Type: Function
-
error
Error
pipeFileStatic
Type: function (filePath, fileName, callback, options)
-
filePath
string -
fileName
string -
callback
SGAppsServerResponse.pipeFileStaticCallback -
options
object?
sendStatusCode
Type: function (statusCode)
-
statusCode
number
TemplateManagerRenderOptions
Type: object
TemplateManagerViewer
Type: function (options)
_facebox
Type: FaceboxTemplate
_debug
Type: boolean
_env
renderCode
Type: function (code, vars, virtualFilePath, callback)
render
Type: function (response, view, vars)
-
response
SGAppsServerResponse -
view
TemplateManagerTemplate -
vars
Object<string, any>
TemplateManagerTemplate
Type: object
SGAppsServerEmail
Type: function (config)
-
config
SGAppsServerEmail.Config optional configuration object
Example:
Example:
var Email = require('path/to/email').Email
var myMsg = new Email(
{ from: 'me@example.com'
, to: 'you@example.com'
, subject: 'Knock knock...'
, body: "Who's there?"
})
myMsg.send(function(err){
...
})
send
Send email
Type: function (callback)
-
callback
SGAppsServerEmail.Callback
options
get message options
Type: object
-
timeout
number
encodedBody
getter generate encoded body
Type: string
msg
getter generate all email structure
Type: string
valid
check if email is valid
Type: function (callback)
-
callback
SGAppsServerEmail.Callback
Config
Email : Sends email using the sendmail command.
Note: sendmail must be installed: see http://www.sendmail.org/
Type: object
-
to
(Array<string> | string) Email address(es) to which this msg will be sent -
debug
boolean? -
from
string? Email address from which this msg is sent. If not set defaults to theexports.from
global setting. -
replyTo
string? Email address to which replies will be sent. If not set defaults tofrom
-
cc
(string | Array<string>)? Email address(es) who receive a copy -
bcc
(string | Array<string>)? Email address(es) who receive a blind copy -
subject
string The subject of the email -
body
string The message of the email -
bodyType
string? Content type of body. Only valid option is 'html' (for now). Defaults to text/plain. -
altText
string? IfbodyType
is set to 'html', this will be sent as the alternative text. -
timeout
number? Duration in milliseconds to wait before killing the process. If not set, defaults toexports.timeout
global setting. -
path
string? Optional path to the sendmail executable.
from
Email address from which messages are sent. Used
when from
was not set on a message.
Type: function (email): string
-
email
string
isValidAddress
Type: function (email): boolean
-
email
string
timeout
Duration in milliseconds to wait before
killing the process. Defaults to 3000. Used when timeout
is not set
on a message.
Type: function (milliseconds): number
-
milliseconds
number
Callback
Type: Function
-
err
Error
SGAppsSessionManagerOptions
Type: object
SGAppsServerRequestSessionCache
Type: object
MountUpdatedURL
Type: string
FaceboxTemplate
Type: function (options)
_debug
Type: boolean
_env
_cachedFiles
INCLUDE_LEVEL
Type: number
render
Type: function (text, vars, env)
renderFile
Type: function (filePath, vars, callback)
renderCode
Type: function (code, vars, callback, virtualFilePath)
SGAppsServerRequestCookie
Type: function ()
get
Type: function (name, options): string
set
Type: function (name, value, options, skipErrors): string
-
name
string -
value
string -
options
object?-
options.secure
boolean (optional, defaultfalse
) -
options.secureProxy
boolean? -
options.signed
boolean? -
options.path
string (optional, default"/"
) -
options.expires
Date? -
options.domain
string? -
options.httpOnly
boolean (optional, defaulttrue
) -
options.sameSite
boolean (optional, defaultfalse
) -
options.secure
boolean (optional, defaultfalse
) -
options.overwrite
boolean (optional, defaultfalse
)
-
-
skipErrors
boolean (optional, defaultfalse
)
SGAppsServerDecoratorsLibrary
Type: function ()
SGAppsServerErrorCallBack
Type: Function
-
err
Error -
request
SGAppsServerRequest -
response
SGAppsServerResponse -
server
SGAppsServer
SGAppsServerErrorOnlyCallback
Type: Function
-
err
Error
FSLibrary
Type: function ()
SGAppsServerShared
Type: function ()
SGAppsServerRequestFile
Type: object
SGAppsServerRequestPostDataItem
Type: object
SGAppsServerDecorator
Type: Function
-
request
SGAppsServerRequest -
response
SGAppsServerResponse -
server
SGAppsServer -
callback
function
SGAppsServer
HTTP Server for high performance results
Type: function (options)
-
options
object?-
options.server
Server? -
options.strictRouting
boolean (optional, defaulttrue
) -
options.debug
boolean (optional, defaulttrue
) -
options._DEBUG_MAX_HANDLER_EXECUTION_TIME
object console shows an warn if handler is executed more than ( works in debug mode ) (optional, default500
) -
options._DEBUG_REQUEST_HANDLERS_STATS
object console shows an warn if handler is executed more than ( works in debug mode ) (optional, defaultfalse
) -
options._REQUEST_FORM_PARAMS_DEEP_PARSE
boolean parse formData field names to create deep object request.body (optional, defaulttrue
) -
options.decorators
Array<SGAppsServerDecorator>?
-
Examples:
// ================================
// Start your 🚀 Web-Server app
// ================================
const { SGAppsServer } = require('@sgapps.io/server');
const app = new SGAppsServer();
app.get('/', function (req, res) {
res.send('hello world')
})
app.server().listen(8080, () => {
app.logger.log('Server is running on port 8080');
})
// ========================================
// Start your 🚀 Web-Server app Extended
// ========================================
const { SGAppsServer } = require('@sgapps.io/server');
const app = new SGAppsServer();
app.get('/', function (req, res) {
res.send('hello world')
})
app.whenReady.then(() => {
app.SessionManager.cookie = 'ssid';
app.SessionManager.SESSION_LIFE = 120; // seconds
app.server().listen(8080, () => {
app.logger.log('Server is running on port 8080');
})
}, app.logger.error);
CookiesManager
Type: object
-
COOKIES_KEY
string -
_enabled
boolean? if is changed to false server will not decorate requests with cookie manager -
handle
function (SGAppsServerRequest, SGAppsServerResponse): object
_server
Type: Server
_decorators
Type: Array<SGAppsServerDecorator>
TemplateManager
Type: TemplateManager
_options
Type: SGAppsServerOptions
STATUS_CODES
shared
Type: SGAppsServerShared
logger
Type: LoggerBuilder
Type: function (config): SGAppsServerEmail
-
config
SGAppsServerEmail.Config
mountPath
Type: string
SessionManager
Type: SGAppsSessionManager
_fs
Type: object
_path
Type: object
EXTENSIONS
Type: ResourcesExtensions
_requestListeners
Type: Object<string, SGAppsServerDictionary>
MAX_POST_SIZE
default value is 16 Kb
» 16 * 1024
Type: number
whenReady
Type: Promise<SGAppsServer>
handleRequest
Type: function (request, response, callback)
-
request
SGAppsServerRequest -
response
SGAppsServerResponse -
callback
SGAppsServerDictionaryRunCallBack
handleErrorRequest
Type: function (request, response, err)
-
request
SGAppsServerRequest -
response
SGAppsServerResponse -
err
Error?
handleStaticRequest
Type: function (request, response, path, callback, options)
-
request
SGAppsServerRequest -
response
SGAppsServerResponse -
path
string -
callback
SGAppsServerErrorCallBack -
options
object? (optional, default{timeout:0,autoIndex:[]}
)
handle
Type: function (request, response, callback)
-
request
IncomingMessage -
response
ServerResponse -
callback
SGAppsServerDictionaryRunCallBack?
server
Type: function (): Server
use
Type: function (path, handlers): SGAppsServer
-
path
(string | RequestHandler) -
handlers
...RequestHandler?
post
The POST
method is used to submit an entity to the specified resource, often causing a change in state or side effects on the server.
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
get
The GET
method requests a representation of the specified resource. Requests using GET should only retrieve data.
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
head
The HEAD
method asks for a response identical to that of a GET request, but without the response body.
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
put
The PUT
method replaces all current representations of the target resource with the request payload.
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
trace
The TRACE
method performs a message loop-back test along the path to the target resource.
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
delete
The DELETE
method deletes the specified resource.
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
options
The OPTIONS
method is used to describe the communication options for the target resource.
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
connect
The CONNECT
method establishes a tunnel to the server identified by the target resource.
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
patch
The PATCH
method is used to apply partial modifications to a resource.
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
all
add handler to all methods
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
finalHandler
add final handler to all methods, last added is first
Type: function (path, handlers): SGAppsServer
-
path
RequestPathStructure -
handlers
...RequestHandler
handlePostData
Type: function (options): SGAppsServerHandlerPostData
-
options
object?
LoggerBuilder
Pretty CLI Logger, with possibility to replace default nodejs' console logger
Type: function ()
Examples:
// =============================
// Use Logger as 💻 instance
// =============================
const { LoggerBuilder } = require('@sgapps.io/server');
const logger = new LoggerBuilder();
logger.log("Hello world");
// replace default console
const { LoggerBuilder } = require('@sgapps.io/server');
const logger = new LoggerBuilder();
logger.decorateGlobalLogger();
console.log("Console Messages are decorated now");
_format
this parameter may be changed if you decide to change decoration schema
Type: string
Example:
// Insert an message in VT100 format
logger._format = "\x1b[7m {{timestamp}} [{{TYPE}}] <{{title}}> {{file}}:{{line}} ({{method}}){{stack}}\x1b[7m";
_debug
Type: boolean
_headerFormatters
Type: Array<headerFormatter>
prettyCli
Type: function (ref, indent, separator)
log
Type: function (messages)
-
messages
...any
info
Type: function (messages)
-
messages
...any
warn
Type: function (messages)
-
messages
...any
error
Type: function (messages)
-
messages
...any
prompt
Type: function (callback, message)
-
callback
LoggerBuilderPrompt -
message
(string | Buffer)
Example:
logger.prompt("rerun tests? [y/n]: ", function (err, buffer) {
// trim spaces from response
var response = buffer.toString().replace(/^\s*(.*?)\s*$/, '$1');
if (response === 'y') {
// write your code
}
});
decorateGlobalLogger
Type: function ()
headerFormatterInfo
Type: object
headerFormatter
Type: Function
-
info
headerFormatterInfo
RequestPathStructureMap
Type: object
-
key
string -
path
RequestPathStructure -
handlers
Array<RequestHandler>
SGAppsServerDictionary
a dictionary for storing
Type: function (options)
-
options
object?
_paths
Type: Array<RequestPathStructureMap>
_dictionary
Type: Object<string, Array<RequestHandler>>
generatePathKey
Type: function (path): string
-
path
RequestPathStructure
push
Type: function (path, handlers)
-
path
RequestPathStructure -
handlers
Array<RequestHandler>
Example:
server.get('/', (req, res) => {
res.send('root');
})
// will match "test" "best", everything with est
server.get(/.*est/, (req, res) => {
res.send('root');
})
server.get('/:name/:surname', (req, res) => {
const { name, surname } = req.params;
res.send(`Hi ${name} ${surname}`);
})
// apply rules with regexp emulation, they are marked with "^" in the start
server.get('^/:name([a-z]+)/:age(\d+)', (req, res, next) => {
const { name, age } = req.params;
if (age < 18) {
res.send(`Hi ${name}, you are not allowed`);
} else {
next()
}
})
// apply rules with regexp emulation, they are marked with "^" in the start
server.get('^/([a-z]+)/', (req, res, next) => {
const { name, age } = req.params;
if (age < 18) {
res.send(`Hi ${name}, you are not allowed`);
} else {
next()
}
})
// add regular expression with group names
server.get('^/(?<test>[a-z]+)/', (req, res, next) => {
const { test } = req.params;
res.send(`param: ${test}`);
})
server.get('/', (req, res) => {
res.send('root');
})
run
Type: function (request, response, server, callback)
-
request
SGAppsServerRequest -
response
SGAppsServerResponse -
server
SGAppsServer -
callback
SGAppsServerDictionaryRunCallBack
RequestPathStructure
RequestHandler
Type: Function
-
request
SGAppsServerRequest -
response
SGAppsServerResponse -
next
function
SGAppsServerOptions
Type: object
-
server
Server? -
strictRouting
boolean? -
_DEBUG_MAX_HANDLER_EXECUTION_TIME
number? -
_DEBUG_REQUEST_HANDLERS_STATS
boolean? -
_REQUEST_FORM_PARAMS_DEEP_PARSE
boolean? parse formData field names to create deep object request.body
SGAppsSessionManager
Type: function (server, options)
-
server
SGAppsServer -
options
SGAppsSessionManagerOptions?
_options
Type: SGAppsSessionManagerOptions
_enabled
Type: boolean
_sessions
Type: Object<string, SGAppsServerRequestSessionCache>
removeExpiredSessions
Type: function ()
handleRequest
Type: function (request)
-
request
SGAppsServerRequest
RequestSessionDecorator
Type: function (request, response, server, callback)
-
request
SGAppsServerRequest -
response
SGAppsServerResponse -
server
SGAppsServer -
callback
function
SGAppsServerDictionaryRunCallBack
Type: Function
-
request
SGAppsServerRequest -
response
SGAppsServerResponse -
server
SGAppsServer
request
Type: function (request, response)
-
request
IncomingMessage -
response
ServerResponse
request
Type: function (request, socket, data)
-
request
IncomingMessage -
socket
Duplex -
data
Buffer
LoggerBuilderPrompt
Type: Function
-
message
Buffer
SGAppsServerHandlerPostData
Type: Function
-
request
SGAppsServerRequest -
response
SGAppsServerResponse -
next
function