@themost/web
Version:
MOST Web Framework 2.0 - Web Server Module
1,144 lines (1,049 loc) • 33.8 kB
JavaScript
/**
* @license
* MOST Web Framework 2.0 Codename Blueshift
* Copyright (c) 2017, THEMOST LP All rights reserved
*
* Use of this source code is governed by an BSD-3-Clause license that can be
* found in the LICENSE file at https://themost.io/license
*/
///
var HttpError = require('@themost/common/errors').HttpError;
var HttpMethodNotAllowedError = require('@themost/common/errors').HttpMethodNotAllowedError;
var HttpNotFoundError = require('@themost/common/errors').HttpNotFoundError;
var HttpForbiddenError = require('@themost/common/errors').HttpForbiddenError;
var LangUtils = require('@themost/common/utils').LangUtils;
var SequentialEventEmitter = require('@themost/common/emitter').SequentialEventEmitter;
var HtmlWriter = require('@themost/common/html').HtmlWriter;
var sprintf = require('sprintf').sprintf;
var _ = require('lodash');
var xml = require('@themost/xml');
var path = require('path');
var fs = require('fs');
var crypto = require('crypto');
var Q = require('q');
var async = require('async');
var PassThrough = require('stream').PassThrough;
var ModuleLoaderStrategy = require('@themost/common/config').ModuleLoaderStrategy;
var Symbol = require('symbol');
const dataProperty = Symbol('data');
/**
* @private
* @param {Buffer} buffer
*/
function bufferToStream(buffer) {
var stream = new PassThrough();
stream.push(buffer);
stream.push(null);
return stream;
}
/**
* @class
* @constructor
*/
function HttpResult() {
this.contentType = 'text/html';
this.contentEncoding = 'utf8';
}
/**
* @deprecated This method is deprecated and it will be removed. Use HttpResult.status(number) method instead.
* @description Sets the status of the HTTP response when this result will be executed
* @param {Number=} status - A number which represents an HTTP status
* @returns {HttpResult}
*
* @example
*
\@httpController()
export default class HelloController extends HttpBaseController {
constructor(context) {
super(context);
}
\@httpGet()
\@httpAction('index')
getIndex() {
return this.content('<h2>This action is not allowed.<h2>').status(403);
}
}
*
*/
HttpResult.prototype.statusCode = function(status) {
this.responseStatus = status;
return this;
};
/**
* @description Sets the status of the HTTP response when this result will be executed
* @param {Number=} status - A number which represents an HTTP status
* @returns {HttpResult}
*
* @example
*
\@httpController()
export default class HelloController extends HttpBaseController {
constructor(context) {
super(context);
}
\@httpGet()
\@httpAction('index')
getIndex() {
return this.content('<h2>This action is not allowed.<h2>').status(403);
}
}
*
*/
HttpResult.prototype.status = function(status) {
return this.statusCode(status)
};
/**
* Executes an HttpResult instance against an existing HttpContext.
* @param {HttpContext} context
* @param {Function} callback
* */
HttpResult.prototype.execute = function(context, callback) {
callback = callback || function() {};
try {
var response = context.response;
if (typeof this.data === 'undefined' || this.data === null) {
response.writeHead(this.responseStatus || 204);
return callback();
}
response.writeHead(this.responseStatus || 200, {"Content-Type": this.contentType});
if (this.data) {
if (this.contentEncoding === 'binary') {
var source = bufferToStream(this.data);
source.on('end', function() {
return callback();
});
source.on('error', function(err) {
return callback(err);
});
return source.pipe(response);
}
else {
response.write(this.data, this.contentEncoding);
}
}
return callback();
}
catch(err) {
return callback(err);
}
};
/**
* @classdesc Represents a user-defined content that is a result of an action.
* @class
* @param {string|Buffer} content - A string or an instance of Buffer class which represents the action result.
* @augments HttpResult
*
* @example
import HttpBaseController from '@themost/web/controllers/base';
import {httpController,httpGet, httpAction} from '@themost/web/decorators';
import {HttpContentResult} from "@themost/web/mvc";
\@httpController()
export default class HelloController extends HttpBaseController {
constructor(context) {
super(context);
}
\@httpGet()
\@httpAction('index')
getIndex() {
return new HttpContentResult(`
<h2>
Hello World!
</h2>
`));
}
}
* @example
import HttpBaseController from '@themost/web/controllers/base';
import {httpController,httpGet, httpAction} from '@themost/web/decorators';
import {HttpContentResult} from "@themost/web/mvc";
\@httpController()
export default class HelloController extends HttpBaseController {
constructor(context) {
super(context);
}
\@httpGet()
\@httpAction('index')
getIndex() {
return this.content(`
<h2>
Hello World!
</h2>
`));
}
}
* */
function HttpContentResult(content) {
var self = this;
Object.defineProperty(this, 'data', {
get: function() {
return self[dataProperty];
},
set: function(value) {
self[dataProperty] = value;
if (value instanceof Buffer) {
self.contentEncoding = 'binary';
}
},
configurable: true,
enumerable: false
});
//set default content type and encoding
this.contentType = 'text/html';
this.contentEncoding = 'utf8';
this.data = content;
}
/**
* Inherits HttpAction
* */
LangUtils.inherits(HttpContentResult,HttpResult);
/**
* Represents a content that does nothing.
* @class
* @constructor
* @augments HttpResult
*/
function HttpEmptyResult() {
//
}
/**
* Inherits HttpAction
* */
LangUtils.inherits(HttpEmptyResult,HttpResult);
HttpEmptyResult.prototype.execute = function(context, callback)
{
//do nothing
callback = callback || function() {};
callback.call(context);
};
/**
* @param {string} key
* @param {*} value
* @returns {*}
* @private
*/
function _json_ignore_null_replacer(key, value) {
if (value==null)
return undefined;
return value;
}
/**
* @classdesc Represents the result an action that is used to send JSON-formatted content.
* @class
* @param {*} data - The data which is going to send when this result will be executed
* @augments HttpResult
* @constructor
*
* @example
*
import HttpBaseController from '@themost/web/controllers/base';
import {httpController,httpGet, httpAction} from '@themost/web/decorators';
import {HttpJsonResult} from "@themost/web/mvc";
\@httpController()
export default class HelloController extends HttpBaseController {
constructor(context) {
super(context);
}
// GET /hello/index.json
\@httpGet()
\@httpAction('index')
getIndex() {
return this.json({
"message": "Hello World"
});
}
}
*
@example
//Send a JSON formatted error to client
import HttpBaseController from '@themost/web/controllers/base';
import {httpController,httpGet, httpAction} from '@themost/web/decorators';
import {HttpJsonResult} from "@themost/web/mvc";
\@httpController()
export default class HelloController extends HttpBaseController {
constructor(context) {
super(context);
}
\@httpGet()
\@httpAction('index')
getIndex() {
return this.json({
"code": "E401",
"message": "You are not authorized to view data."
}).status(401);
}
}
*
*/
function HttpJsonResult(data)
{
if (data instanceof String) {
this.data = data;
}
else if (data instanceof Error) {
var keys = Object.getOwnPropertyNames(data);
var thisData = {};
_.forEach(keys, function(key) {
if (process.env.NODE_ENV !== 'development' && key==='stack') {
return;
}
thisData[key] = data[key];
});
this.data = thisData;
}
else {
this.data = data;
}
this.contentType = 'application/json;charset=utf-8';
this.contentEncoding = 'utf8';
}
/**
* Inherits HttpAction
* */
LangUtils.inherits(HttpJsonResult,HttpResult);
/**
* Executes an HttpResult instance against an existing HttpContext.
* @param {HttpContext} context
* @param {Function} callback
* */
HttpJsonResult.prototype.execute = function(context, callback) {
callback = callback || function() {};
try {
var response = context.response;
if (typeof this.data === 'undefined' || this.data === null) {
response.writeHead(204);
return callback.call(context);
}
response.writeHead(this.responseStatus || 200, {"Content-Type": this.contentType});
response.write(JSON.stringify(this.data, _json_ignore_null_replacer), this.contentEncoding);
callback.call(context);
}
catch(e) {
callback.call(context, e);
}
};
/**
* Represents an action that is used to send Javascript-formatted content.
* @class
* @param {*} data
* @constructor
* @augments HttpResult
*/
function HttpJavascriptResult(data)
{
if (typeof data === 'string')
this.data = data;
this.contentType = 'text/javascript;charset=utf-8';
this.contentEncoding = 'utf8';
}
/**
* Inherits HttpAction
* */
LangUtils.inherits(HttpJavascriptResult,HttpResult);
/**
* Represents an action that is used to send XML-formatted content.
* @class
* @param data
* @constructor
* @augments HttpResult
*/
function HttpXmlResult(data)
{
this.contentType = 'text/xml';
this.contentEncoding = 'utf8';
if (typeof data === 'undefined' || data == null)
return;
this.data = data;
}
/**
* Inherits HttpAction
* */
LangUtils.inherits(HttpXmlResult,HttpResult);
/**
* Executes an HttpResult instance against an existing HttpContext.
* @param {HttpContext} context
* @param {Function} callback
* */
HttpXmlResult.prototype.execute = function(context, callback) {
callback = callback || function() {};
try {
var response = context.response;
if (typeof this.data === 'undefined' || this.data === null) {
response.writeHead(204);
return callback.call(context);
}
response.writeHead(this.responseStatus || 200, {"Content-Type": this.contentType});
if (typeof this.data === 'object') {
response.write(xml.serialize(this.data, { item:'Item' }).outerXML(), this.contentEncoding);
}
else {
response.write(this.data, this.contentEncoding);
}
callback.call(context);
}
catch(e) {
callback.call(context, e);
}
};
/**
* Represents a redirect action to a specified URI.
* @class
* @param {string|*} url
* @constructor
* @augments HttpResult
*/
function HttpRedirectResult(url) {
this.url = url;
}
/**
* Inherits HttpAction
* */
LangUtils.inherits(HttpRedirectResult,HttpResult);
/**
*
* @param {HttpContext} context
* @param {Function} callback
*/
HttpRedirectResult.prototype.execute = function(context, callback)
{
/**
* @type ServerResponse
* */
var response = context.response;
response.writeHead(302, { 'Location': this.url });
//response.end();
callback.call(context);
};
/**
* Represents a static file result
* @class
* @param {string} physicalPath
* @param {string=} fileName
* @constructor
* @augments HttpResult
*/
function HttpFileResult(physicalPath, fileName) {
//
this.physicalPath = physicalPath;
this.fileName = fileName;
}
/**
* Inherits HttpAction
* */
LangUtils.inherits(HttpFileResult,HttpResult);
/**
*
* @param {HttpContext} context
* @param {Function} callback
*/
HttpFileResult.prototype.execute = function(context, callback)
{
callback = callback || function() {};
var physicalPath = this.physicalPath, fileName = this.fileName;
fs.exists(physicalPath, function(exists) {
if (!exists) {
callback(new HttpNotFoundError());
}
else {
try {
fs.stat(physicalPath, function (err, stats) {
if (err) {
callback(err);
}
else {
if (!stats.isFile()) {
callback(new HttpNotFoundError());
}
else {
//get if-none-match header
var requestETag = context.request.headers['if-none-match'];
//generate responseETag
var md5 = crypto.createHash('md5');
md5.update(stats.mtime.toString());
var responseETag = md5.digest('base64');
if (requestETag) {
if (requestETag === responseETag) {
context.response.writeHead(304);
context.response.end();
callback();
return;
}
}
var contentType = null;
//get file extension
var extensionName = path.extname(fileName || physicalPath);
//get MIME collection
var mimes = context.getApplication().getConfiguration().mimes;
var contentEncoding = null;
//find MIME type by extension
var mime = mimes.filter(function (x) {
return x.extension === extensionName;
})[0];
if (mime) {
contentType = mime.type;
if (mime.encoding)
contentEncoding = mime.encoding;
}
//throw exception (MIME not found or access denied)
if (_.isNil(contentType)) {
callback(new HttpForbiddenError())
}
else {
/*//finally process request
fs.readFile(physicalPath, 'binary', function (err, data) {
if (err) {
callback(e);
}
else {
//add Content-Disposition: attachment; filename="<file name.ext>"
context.response.writeHead(200, {
'Content-Type': contentType + (contentEncoding ? ';charset=' + contentEncoding : ''),
'ETag': responseETag
});
context.response.write(data, "binary");
callback();
}
});*/
//create read stream
var source = fs.createReadStream(physicalPath);
//add Content-Disposition: attachment; filename="<file name.ext>"
context.response.writeHead(200, {
'Content-Type': contentType + (contentEncoding ? ';charset=' + contentEncoding : ''),
'ETag': responseETag
});
//copy file
source.pipe(context.response);
source.on('end', function() {
callback();
});
source.on('error', function(err) {
callback(err);
});
}
}
}
});
}
catch (e) {
callback(e);
}
}
});
};
/**
* @param controller
* @param view
* @param extension
* @param callback
* @returns {*}
* @private
*/
function queryDefaultViewPath(controller, view, extension, callback) {
return queryAbsoluteViewPath.call(this, this.application.mapPath('/views'), controller, view, extension, callback);
}
/**
* @param view
* @param extension
* @param callback
* @returns {*}
* @private
*/
function querySharedViewPath(view, extension, callback) {
return queryAbsoluteViewPath.call(this, this.application.mapPath('/views'), 'shared', view, extension, callback);
}
/**
* @param search
* @param controller
* @param view
* @param extension
* @param callback
* @private
*/
function queryAbsoluteViewPath(search, controller, view, extension, callback) {
var result = path.resolve(search, sprintf('%s/%s.html.%s', controller, view, extension));
fs.exists(result, function(exists) {
if (exists)
return callback(null, result);
//search for capitalized controller name e.g. person as Person
var capitalizedController = controller.charAt(0).toUpperCase() + controller.substring(1);
result = path.resolve(search, sprintf('%s/%s.html.%s', capitalizedController, view, extension));
fs.exists(result, function(exists) {
if (exists)
return callback(null, result);
callback();
});
});
}
/**
* @param {string} p
* @returns {boolean}
* @private
*/
function isAbsolute(p) {
return path.normalize(p + '/') === path.normalize(path.resolve(p) + '/');
}
/**
* Represents a class that is used to render a view.
* @class
* @param {*=} name - The name of the view.
* @param {*=} data - The data that are going to be used to render the view.
* @augments HttpResult
*/
function HttpViewResult(name, data)
{
this.name = name;
this.data = data===undefined? []: data;
this.contentType = 'text/html;charset=utf-8';
this.contentEncoding = 'utf8';
}
/**
* Inherits HttpAction
* */
LangUtils.inherits(HttpViewResult,HttpResult);
/**
* Resolves view physical path based on the given view engine
* @param {HttpContext} context - An instance of HttpContext class which represents the underlying context
* @param {string} controller - A string which represents the name of the controller we want to search for
* @param {string} view - A string which represents the name of the view we want to search for
* @param {HttpViewEngineConfiguration} engine - The configuration of the target view engine
* @param {Function} callback
*/
HttpViewResult.resolveViewPath = function(context, controller, view, engine, callback) {
return queryDefaultViewPath.call(context, controller, view, engine.extension, function(err, result) {
if (err) { return callback(err); }
if (result) {
return callback(null, result);
}
else {
return querySharedViewPath.call(context, view, engine.extension, function(err, result) {
if (err) { return callback(err); }
if (result) {
return callback(null, result);
}
callback();
});
}
});
};
/**
* Sets or changes the name of this HttpViewResult instance.
* @param {string} s
* @returns {HttpViewResult}
*/
HttpViewResult.prototype.setName = function(s) {
this.name = s;
return this;
};
/**
* @param {function(Error=,*=)} callback
* @param {HttpContext} context - The HTTP context
* */
HttpViewResult.prototype.execute = function(context, callback)
{
var self = this;
callback = callback || function() {};
/**
* @type ServerResponse
* */
var response = context.response;
//if the name is not defined get the action name of the current controller
if (!this.name)
//get action name
this.name = context.data['action'];
//validate [path] route param in order to load a view that is located in a views' sub-directory (or in another absolute path)
var routePath;
if (context.request.route) {
routePath = context.request.route.path;
}
//get view name
var viewName = this.name;
if (/^partial/.test(viewName)) {
//partial view
viewName = viewName.substr(7).replace(/^-/,'');
context.request.route.partial = true;
}
//and of course controller's name
var controllerName = context.data['controller'];
//enumerate existing view engines e.g /views/controller/index.[html].ejs or /views/controller/index.[html].xform etc.
var viewPath, viewEngine;
async.eachSeries(context.getApplication().getConfiguration().engines, function(engine, cb) {
if (viewPath) { cb(); return; }
if (routePath && isAbsolute(routePath)) {
queryAbsoluteViewPath.call(context, routePath, controllerName, viewName, engine.extension, function(err, result) {
if (err) { return cb(err); }
if (result) {
viewPath = result;
viewEngine = engine;
return cb();
}
else {
return cb();
}
});
}
else {
var searchViewName = viewName;
if (routePath) {
searchViewName = path.join(routePath, viewName);
}
//search by relative path
queryDefaultViewPath.call(context, controllerName, searchViewName, engine.extension, function(err, result) {
if (err) { return cb(err); }
if (result) {
viewPath = result;
viewEngine = engine;
return cb();
}
else {
querySharedViewPath.call(context, searchViewName, engine.extension, function(err, result) {
if (err) { return cb(err); }
if (result) {
viewPath = result;
viewEngine = engine;
return cb();
}
cb();
});
}
});
}
}, function(err) {
if (err) {
return callback(err);
}
if (viewEngine) {
var moduleLoader = context.getApplication().getConfiguration().getStrategy(ModuleLoaderStrategy);
return Q.promise(function(resolve, reject) {
var engine;
if (/^\/web\//.test(viewEngine.type)) {
engine = require(viewEngine.type.replace(/^\/web\//,"./"));
}
else {
engine = _.isObject(moduleLoader) ? moduleLoader.require(viewEngine.type) : require(viewEngine.type);
}
/**
* @type {HttpViewEngine|*}
*/
var engineInstance = engine.createInstance(context);
//render
var event = { context:context, target:self };
return context.emit('preExecuteResult', event, function(err) {
if (err) {
return reject(err);
}
else {
engineInstance.render(viewPath, self.data, function(err, result) {
if (err) {
return reject(err);
}
else {
//HttpViewResult.result or data (?)
self.result = result;
return context.emit('postExecuteResult', event, function(err) {
if (err) {
return reject(err);
}
else {
response.writeHead(self.responseStatus || 200, {"Content-Type": self.contentType});
response.write(self.result, self.contentEncoding);
return resolve();
}
});
}
});
}
});
}).then(function() {
return callback();
}).catch(function(err) {
return callback(err);
});
}
else {
var error = new HttpNotFoundError();
if (context.request && context.request.url) {
error.resource = context.request.url;
}
return callback(error);
}
});
};
/**
* @class
* @classdesc Provides methods that respond to HTTP requests that are made to a web application
* @constructor
* @param {HttpContext} context - The executing HTTP context.
* */
function HttpController(context) {
/**
* @name HttpController#context
* @type {HttpContext}
*/
this.context = context;
}
/**
* Creates a view result object for the given request.
* @param {*=} data
* @returns {HttpViewResult}
*/
HttpController.prototype.view = function(data)
{
return new HttpViewResult(null, data);
};
/**
* Creates a view result based on the context content type
* @param {*=} data
* @returns HttpViewResult
* */
HttpController.prototype.result = function(data)
{
if (this.context) {
var fn = this[this.context.format];
if (typeof fn !== 'function')
throw new HttpError(400,'Not implemented.');
return fn.call(this, data);
}
else
throw new Error('Http context cannot be empty at this context.');
};
HttpController.prototype.forbidden = function (callback) {
return callback(new HttpForbiddenError());
};
/**
* Creates a view result object for the given request.
* @param {*=} data
* @returns HttpViewResult
* */
HttpController.prototype.html = function(data)
{
return new HttpViewResult(null, data);
};
/**
* Creates a view result object for the given request.
* @param {*=} data
* @returns HttpViewResult
* */
HttpController.prototype.htm = HttpController.prototype.html;
/**
* Creates a view result object for the given request.
* @param {String=} data
* @returns HttpJavascriptResult
* */
HttpController.prototype.js = function(data)
{
return new HttpJavascriptResult(data);
};
/**
* Creates a view result object that represents a client javascript object.
* This result may be used for sharing specific objects stored in memory or server filesystem
* e.g. serve a *.json file as a client variable with name window.myVar1 or
* serve user settings object ({ culture: 'en-US', notifyMe: false}) as a variable with name window.settings
* @param {String} name
* @param {String|*} obj
* @returns HttpResult
* */
HttpController.prototype.jsvar = function(name, obj)
{
if (typeof name !== 'string')
return new HttpEmptyResult();
if (name.length===0)
return new HttpEmptyResult();
if (typeof obj === 'undefined' || obj === null)
return new HttpJavascriptResult(name.concat(' = null;'));
else if (obj instanceof Date)
return new HttpJavascriptResult(name.concat(' = new Date(', obj.valueOf(), ');'));
else if (typeof obj === 'string')
return new HttpJavascriptResult(name.concat(' = ', obj, ';'));
else
return new HttpJavascriptResult(name.concat(' = ', JSON.stringify(obj), ';'));
};
/**
* Invokes a default action and returns an HttpViewResult instance
* @param {Function} callback
*/
HttpController.prototype.action = function(callback)
{
var self = this;
self.context.handleGet(function() {
return callback(null, self.view());
}).unhandle(function() {
return callback(new HttpMethodNotAllowedError());
});
};
/**
* Creates a content result object by using a string.
* @returns HttpContentResult
* */
HttpController.prototype.content = function(content)
{
return new HttpContentResult(content);
};
/**
* Creates a JSON result object by using the specified data.
* @returns HttpJsonResult
* */
HttpController.prototype.json = function(data)
{
return new HttpJsonResult(data);
};
/**
* Creates a XML result object by using the specified data.
* @returns HttpXmlResult
* */
HttpController.prototype.xml = function(data)
{
return new HttpXmlResult(data);
};
/**
* Creates a binary file result object by using the specified path.
* @param {string} physicalPath
* @param {string=} fileName
* @returns {HttpFileResult|HttpResult}
* */
HttpController.prototype.file = function(physicalPath, fileName)
{
return new HttpFileResult(physicalPath, fileName);
};
/**
* Creates a redirect result object that redirects to the specified URL.
* @returns HttpRedirectResult
* */
HttpController.prototype.redirect = function(url)
{
return new HttpRedirectResult(url);
};
/**
* Creates an empty result object.
* @returns HttpEmptyResult
* */
HttpController.prototype.empty = function()
{
return new HttpEmptyResult();
};
/**
* Promise resolver function
* @callback PromiseResolverFunction
* @param {Function} resolve
* @param {Function=} reject
* @param {Function=} notify
*/
/**
* Returns a promise by executing the given resolver function
* @param {PromiseResolverFunction} resolver
* @returns {Promise|*}
* */
HttpController.prototype.toPromise = function(resolver)
{
return Q.promise(resolver.bind(this));
};
/**
* Encapsulates information that is related to rendering a view.
* @class
* @param {HttpContext} context
* @property {DataModel} model
* @property {HtmlViewHelper} html
* @constructor
* @augments {SequentialEventEmitter}
*/
function HttpViewContext(context) {
/**
* Gets or sets the body of the current view
* @type {String}
*/
this.body='';
/**
* Gets or sets the title of the page if the view will be fully rendered
* @type {String}
*/
this.title='';
/**
* Gets or sets the view layout page if the view will be fully rendered
* @type {String}
*/
this.layout = null;
/**
* Gets or sets the view data
* @type {String}
*/
this.data = null;
/**
* Represents the current HTTP context
* @type {HttpContext}
*/
this.context = context;
/**
* @name HttpViewContext#writer
* @type HtmlWriter
* @description Gets an instance of HtmlWriter helper class
*/
var writer;
Object.defineProperty(this, 'writer', {
get:function() {
if (writer)
return writer;
writer = new HtmlWriter();
writer.indent = false;
return writer;
}, configurable:false, enumerable:false
});
var self = this;
Object.defineProperty(this, 'model', {
get:function() {
if (self.context.params)
if (self.context.params.controller)
return self.context.model(self.context.params.controller);
return null;
}, configurable:false, enumerable:false
});
//class extension initiators
if (typeof this.init === 'function') {
//call init() method
this.init();
}
}
LangUtils.inherits(HttpViewContext, SequentialEventEmitter);
/**
* @param {string} url
* @param {Function} callback
* @returns {string}
*/
HttpViewContext.prototype.render = function(url, callback) {
callback = callback || function() {};
//get response cookie, if any
var requestCookie = this.context.response.getHeader('set-cookie');
if (typeof this.context.request.headers.cookie !== 'undefined')
requestCookie = this.context.request.headers.cookie;
this.context.getApplication().executeRequest( { url: url, cookie: requestCookie }, function(err, result) {
if (err) {
callback(err);
}
else {
callback(null, result.body);
}
});
};
HttpViewContext.prototype.init = function() {
//
};
/**
*
* @param {String} s
* @param {String=} lib
* @returns {String}
*/
HttpViewContext.prototype.translate = function(s, lib) {
return this.context.translate(s, lib);
};
if (typeof exports !== 'undefined')
{
module.exports.HttpResult = HttpResult;
module.exports.HttpContentResult = HttpContentResult;
module.exports.HttpJsonResult =HttpJsonResult;
module.exports.HttpJavascriptResult =HttpJavascriptResult;
module.exports.HttpEmptyResult =HttpEmptyResult;
module.exports.HttpXmlResult =HttpXmlResult;
module.exports.HttpRedirectResult =HttpRedirectResult;
module.exports.HttpFileResult =HttpFileResult;
module.exports.HttpViewResult =HttpViewResult;
module.exports.HttpViewContext =HttpViewContext;
module.exports.HttpController =HttpController;
}