Comparing version 1.0.0-alpha-01 to 1.0.0-alpha-02
@@ -174,21 +174,3 @@ 'use strict'; | ||
let server = this.getComponent('appix/server'); | ||
let Request = di.load('@{appix}/request'); | ||
server.on('request', (request, response) => { | ||
di.setAlias('controllersPath', this.defaults.controllersPath); | ||
di.setAlias('modulesPath', this.defaults.modulesPath); | ||
let nRequest = new Request(this, { | ||
request, | ||
response | ||
}, request.url); | ||
nRequest.process(); | ||
}); | ||
if (Type.isString(this.defaults.listenHost)) { | ||
server.listen(this.defaults.listenPort, this.defaults.listenHost); | ||
} else { | ||
server.listen(this.defaults.listenPort); | ||
} | ||
server.startUp(this); | ||
process.on('uncaughtException', error => { | ||
@@ -201,3 +183,2 @@ logger.fatal(error.message, { | ||
}); | ||
logger.info('Listen server', this.defaults); | ||
@@ -204,0 +185,0 @@ } |
@@ -74,4 +74,30 @@ 'use strict'; | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Server#startUp | ||
* | ||
* @description | ||
* Startup server based on bootstrap process | ||
*/ | ||
startUp(bootstrap) { | ||
let Request = di.load('@{appix}/request'); | ||
this.on('request', (request, response) => { | ||
di.setAlias('controllersPath', bootstrap.defaults.controllersPath); | ||
di.setAlias('modulesPath', bootstrap.defaults.modulesPath); | ||
let nRequest = new Request(bootstrap, { | ||
request, | ||
response | ||
}, request.url); | ||
nRequest.process(); | ||
}); | ||
if (Type.isString(bootstrap.defaults.listenHost)) { | ||
this.listen(bootstrap.defaults.listenPort, bootstrap.defaults.listenHost); | ||
} else { | ||
this.listen(bootstrap.defaults.listenPort); | ||
} | ||
} | ||
} | ||
module.exports = Server; |
@@ -136,2 +136,15 @@ 'use strict'; | ||
* @function | ||
* @name Controller#getRequestHeader | ||
* @param {String} name of header | ||
* @description | ||
* Get request header, by header name | ||
* @return {String} | ||
*/ | ||
getRequestHeader(name) { | ||
return this.__request__.getRequestHeader(name); | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Controller#getRequestHeaders | ||
@@ -202,2 +215,25 @@ * | ||
* @function | ||
* @name Controller#getRequestCookies | ||
* @description | ||
* Return all cookies | ||
*/ | ||
getRequestCookies() { | ||
return this.__request__.getRequestCookies(); | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Controller#getRequestCookie | ||
* @param {String} name cookie | ||
* @description | ||
* Return cookie value | ||
*/ | ||
getRequestCookie(name) { | ||
return this.__request__.getRequestCookie(name); | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Controller#onEnd | ||
@@ -296,2 +332,55 @@ * | ||
* @function | ||
* @name Controller#setResponseStatusCode | ||
* @param {Number} num status code number | ||
* @description | ||
* Set status code which will be sent to client | ||
*/ | ||
setResponseStatusCode(num) { | ||
this.__request__.setResponseStatusCode(num); | ||
} | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#setResponseCookie | ||
* @param key {String} cookie name | ||
* @param value {String} cookie value | ||
* @param expires {String|Object|Number} expire date | ||
* @param path {String} cookie path | ||
* @param domain {String} cookie domain | ||
* @param isHttpOnly {Boolean} is http only | ||
* @description | ||
* Sets an cookie header | ||
*/ | ||
setResponseCookie(key, value, expires, path, domain, isHttpOnly) { | ||
this.__request__.setResponseCookie(key, value, expires, path, domain, isHttpOnly); | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Controller#setResponseHeader | ||
* @param {String} key header name | ||
* @param {String|Object} value header value | ||
* @description | ||
* Sets an response header | ||
*/ | ||
setResponseHeader(key, value) { | ||
this.__request__.setResponseHeader(key, value); | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Controller#hasResponseHeader | ||
* @param {String} key header name | ||
* @description | ||
* Check if response header is present | ||
* @return {Boolean} | ||
*/ | ||
hasResponseHeader(key) { | ||
return this.__request__.hasResponseHeader(key); | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Controller#getFiltersToApply | ||
@@ -441,6 +530,18 @@ * @private | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Controller#redirect | ||
* @param {String} url string url | ||
* @param {Number} code status code | ||
* @description | ||
* Redirect to page | ||
*/ | ||
redirect(url, code) { | ||
this.stopChain(); | ||
return this.__request__.redirect(url, code); | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Controller#beforeEach | ||
@@ -447,0 +548,0 @@ * |
@@ -10,2 +10,3 @@ 'use strict'; | ||
let Controller = di.load('@{appix}/controller'); | ||
let COOKIE_PARSE_REGEX = /(\w+[^=]+)=([^;]+)/g; | ||
let logger; | ||
@@ -42,3 +43,5 @@ let router; | ||
isForwarded: Type.BOOLEAN, | ||
isRedirected: Type.BOOLEAN, | ||
statusCode: Type.NUMBER, | ||
requestCookies: Type.OBJECT, | ||
responseHeaders: Type.OBJECT | ||
@@ -54,2 +57,3 @@ }); | ||
this.isForwarded = config.isForwarded || false; | ||
this.isRedirected = false; | ||
this.id = config.id || di.uuid(); | ||
@@ -62,2 +66,14 @@ this.url = url; | ||
this.responseHeaders = {}; | ||
this.requestCookies = config.requestCookies || {}; | ||
if (!this.isForwarded) { | ||
let cookies = this.getRequestHeader('Cookie'); | ||
if (Type.isString(cookies)) { | ||
cookies.replace( | ||
COOKIE_PARSE_REGEX, | ||
(cookie, key, value) => this.requestCookies[key] = value | ||
); | ||
} | ||
} | ||
this.response.on('destory', () => { | ||
@@ -142,2 +158,27 @@ this.events.emit('destory'); | ||
* @function | ||
* @name Request#getRequestHeader | ||
* @param {String} name header name | ||
* | ||
* @description | ||
* Get an request header by header name | ||
*/ | ||
getRequestHeader(name) { | ||
if (!Type.isString(name)) { | ||
throw new error.HttpException(500, `Request header name must be string type`); | ||
} | ||
let keys = Object.keys(this.request.headers); | ||
let value = ''; | ||
while (keys.length) { | ||
let key = keys.shift(); | ||
if (name.toLocaleLowerCase() === key.toLocaleLowerCase()) { | ||
value = this.request.headers[key]; | ||
break; | ||
} | ||
} | ||
return value; | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Request#getRequestHeaders | ||
@@ -246,2 +287,147 @@ * | ||
* @function | ||
* @name Request#hasResponseHeader | ||
* @param {String} key header name | ||
* @description | ||
* Check if response header is present | ||
* @return {Boolean} | ||
*/ | ||
hasResponseHeader(key) { | ||
if (Type.isString(key)) { | ||
key = key.toLocaleLowerCase(); | ||
} else { | ||
throw new error.HttpException(500, `Response header key must be string type`); | ||
} | ||
return this.responseHeaders.hasOwnProperty(key); | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Request#getRequestCookie | ||
* @param {String} name cookie | ||
* @description | ||
* Return cookie value | ||
*/ | ||
getRequestCookie(name) { | ||
return this.requestCookies[name]; | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Request#getRequestCookies | ||
* @description | ||
* Return all cookies | ||
*/ | ||
getRequestCookies() { | ||
return this.requestCookies; | ||
} | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Request#setResponseCookie | ||
* @param key {String} cookie name | ||
* @param value {String} cookie value | ||
* @param expires {String|Object|Number} expire date | ||
* @param path {String} cookie path | ||
* @param domain {String} cookie domain | ||
* @param isHttpOnly {Boolean} is http only | ||
* @description | ||
* Sets an cookie header | ||
*/ | ||
setResponseCookie(key, value, expires, path, domain, isHttpOnly) { | ||
var cookie, date; | ||
if (Type.isUndefined(key) || Type.isUndefined(value)) { | ||
throw new error.HttpException(500, 'Request.setResponseCookie: key and value must be provided!', { | ||
key, | ||
value, | ||
expires, | ||
path, | ||
domain, | ||
isHttpOnly | ||
}); | ||
} | ||
cookie = key + "=" + value; | ||
if (!!expires) { | ||
if (Type.isNumber(expires)) { | ||
date = new Date(); | ||
date.setTime(date.getTime() + expires); | ||
cookie += "; Expires=" + date.toGMTString(); | ||
} else if (Type.isString(expires)) { | ||
cookie += "; Expires=" + expires; | ||
} else if (Type.isDate(expires)) { | ||
cookie += "; Expires=" + expires.toGMTString(); | ||
} | ||
} | ||
if (!!path) { | ||
cookie += "; Path=" + path; | ||
} | ||
if (!!domain) { | ||
cookie += "; Domain=" + domain; | ||
} | ||
if (!!isHttpOnly) { | ||
cookie += "; HttpOnly"; | ||
} | ||
this.setResponseHeader('Set-cookie', cookie); | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Request#setResponseHeader | ||
* @param {String} key header name | ||
* @param {String|Object} value header value | ||
* @description | ||
* Sets an response header | ||
*/ | ||
setResponseHeader(key, value) { | ||
if (Type.isString(key)) { | ||
key = key.toLocaleLowerCase(); | ||
} else { | ||
throw new error.HttpException(500, `Response header key must be string type ${key}`); | ||
} | ||
if (!value) { | ||
throw new error.HttpException(500, `Response header value must be defined ${value}`); | ||
} | ||
value = value.toString(); | ||
if (this.hasResponseHeader(key) && !Type.isArray(this.responseHeaders[key])) { | ||
let header = this.responseHeaders[key]; | ||
this.responseHeaders[key] = []; | ||
this.responseHeaders[key].push(header); | ||
this.responseHeaders[key].push(value); | ||
} else if (this.hasResponseHeader(key) && Type.isArray(this.responseHeaders[key])) { | ||
this.responseHeaders[key].push(value); | ||
} else { | ||
this.responseHeaders[key] = value; | ||
} | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Request#setResponseStatusCode | ||
* @param {Number} num status code number | ||
* @description | ||
* Set status code which will be sent to client | ||
*/ | ||
setResponseStatusCode(num) { | ||
if (!Type.isNumber(num)) { | ||
num = parseInt(num); | ||
} | ||
if (isNaN(num)) { | ||
throw new error.HttpException(500, 'Status code must be number', { | ||
num | ||
}); | ||
} | ||
this.statusCode = num; | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Request#render | ||
@@ -254,5 +440,5 @@ * @param {Buffer|String} response | ||
render(response) { | ||
let rKey = 'content-type'; | ||
if (!this.responseHeaders.hasOwnProperty(rKey)) { | ||
this.responseHeaders[rKey] = 'text/html'; | ||
let rKey = 'Content-Type'; | ||
if (!this.hasResponseHeader(rKey)) { | ||
this.setResponseHeader(rKey, 'text/html'); | ||
} | ||
@@ -301,2 +487,3 @@ logger.info('Request.render', { | ||
response: this.response, | ||
requestCookies: this.requestCookies, | ||
isForwarded: true, | ||
@@ -345,2 +532,18 @@ events: this.events, | ||
* @function | ||
* @name Request#redirect | ||
* @param {String} url string url | ||
* @param {Number} code status code default is 302 temporary redirect! | ||
* @description | ||
* Redirect to page | ||
*/ | ||
redirect(url, code) { | ||
this.isRedirected = true; | ||
this.setResponseStatusCode(code || 302); | ||
this.setResponseHeader('Location', url); | ||
return Promise.resolve(`Redirect to: ${code} - ${url}`); | ||
} | ||
/** | ||
* @since 1.0.0 | ||
* @function | ||
* @name Request#handleModule | ||
@@ -374,19 +577,27 @@ * @param {String} moduleName | ||
handleController(controllerName, actionName) { | ||
var request = this; | ||
let ControllerToInitialize = di.load('@{controllersPath}/' + controllerName); | ||
let controller = new ControllerToInitialize({ | ||
getMethod: () => this.getMethod, | ||
getParams: () => this.getParams, | ||
getParsedUrl: () => this.getParsedUrl, | ||
getRequestBody: () => this.getRequestBody, | ||
getPathname: () => this.getPathname, | ||
getRequestDomain: () => this.getRequestDomain, | ||
getRequestHeaders: () => this.getRequestHeaders, | ||
getRequestLocalAddress: () => this.getRequestLocalAddress, | ||
getRequestLocalPort: () => this.getRequestLocalPort, | ||
getRequestRemoteAddress: () => this.getRequestRemoteAddress, | ||
getRequestRemotePort: () => this.getRequestRemotePort, | ||
onEnd: () => this.onEnd, | ||
forwardRoute: () => this.forwardRoute, | ||
forwardUrl: () => this.forwardUrl, | ||
getMethod: this.getMethod.bind(this), | ||
getParams: this.getParams.bind(this), | ||
getParsedUrl: this.getParsedUrl.bind(this), | ||
getRequestBody: this.getRequestBody.bind(this), | ||
getPathname: this.getPathname.bind(this), | ||
getRequestDomain: this.getRequestDomain.bind(this), | ||
getRequestHeaders: this.getRequestHeaders.bind(this), | ||
getRequestLocalAddress: this.getRequestLocalAddress.bind(this), | ||
getRequestLocalPort: this.getRequestLocalPort.bind(this), | ||
getRequestRemoteAddress: this.getRequestRemoteAddress.bind(this), | ||
getRequestRemotePort: this.getRequestRemotePort.bind(this), | ||
getRequestHeader: this.getRequestHeader.bind(this), | ||
getRequestCookie: this.getRequestCookie.bind(this), | ||
getRequestCookies: this.getRequestCookies.bind(this), | ||
setResponseStatusCode: this.getRequestHeader.bind(this), | ||
setResponseCookie: this.setResponseCookie.bind(this), | ||
setResponseHeader: this.setResponseHeader.bind(this), | ||
hasResponseHeader: this.hasResponseHeader.bind(this), | ||
redirect: this.redirect.bind(this), | ||
onEnd: this.onEnd.bind(this), | ||
forwardRoute: this.forwardRoute.bind(this), | ||
forwardUrl: this.forwardUrl.bind(this), | ||
id: this.id, | ||
@@ -439,4 +650,8 @@ bootstrap: this.bootstrap, | ||
} | ||
// on redirection don't apply filters | ||
return yield controller.applyAfterEachFilters(action); | ||
if (!request.isRedirected) { | ||
return yield controller.applyAfterEachFilters(action); | ||
} | ||
return action; | ||
}).then(item => this.render(item)); | ||
@@ -443,0 +658,0 @@ } |
@@ -5,3 +5,3 @@ { | ||
"description": "Lightweight application framework with dyependency injection and dynamic type checking for node js", | ||
"version": "1.0.0-alpha-01", | ||
"version": "1.0.0-alpha-02", | ||
"dependencies": { | ||
@@ -8,0 +8,0 @@ "di-node": "0.2.x", |
@@ -1,4 +0,93 @@ | ||
# Appix 1.0.0-alpha-01 [![Build Status](https://travis-ci.org/igorzg/easynode.svg?branch=master)](https://travis-ci.org/igorzg/easynode) | ||
# Appix 1.0.0-alpha-02 [![Build Status](https://travis-ci.org/igorzg/appix.svg?branch=master)](https://travis-ci.org/igorzg/appix) | ||
* Es6 Node.js framework dev version | ||
* Lightweight application framework with dyependency injection and dynamic type checking for node js | ||
* This application framework is improved version of mvcjs nodejs framework | ||
* This application framework is improved version of mvcjs nodejs framework | ||
## Features | ||
1. Appix follow [reactive](http://www.reactivemanifesto.org/) design pattern. | ||
2. Catch all runtime/syntax errors | ||
3. Has a dependency injection | ||
4. Built on top of ES6 | ||
**Hello world example in appix** | ||
- **npm install appix** | ||
- app/env.json | ||
```json | ||
{ | ||
"components": { | ||
"appix/logger": { | ||
"enabled": true, | ||
"console": true, | ||
"level": 30 | ||
}, | ||
"appix/router": { | ||
"useCustomErrorHandler": false | ||
} | ||
} | ||
} | ||
``` | ||
- app/index.js | ||
```js | ||
'use strict'; | ||
let di = require('appix'); | ||
let Bootstrap = di.load('@{appix}/bootstrap'); | ||
// bootstrap application | ||
let init = new Bootstrap({ | ||
listenPort: 9000, | ||
appPath: __dirname + '/' | ||
}); | ||
// set bootstrapped instance under custom name | ||
di.setInstance('node', init); | ||
// get router component | ||
let router = init.getComponent('appix/router'); | ||
// add some routes | ||
// Route actions are case sensitive! | ||
router.add([ | ||
{ | ||
url: '/', | ||
route: 'app/Index' | ||
}, | ||
{ | ||
url: '/goto301', | ||
route: 'app/Redirect301' | ||
}, | ||
{ | ||
url: '/goto', | ||
route: 'app/Redirect' | ||
}, | ||
{ | ||
url: '/favicon.ico', | ||
route: 'home/myfaviconhandler' | ||
} | ||
]); | ||
// run server | ||
init.listen(); | ||
``` | ||
- app/controllers/home.js | ||
```js | ||
'use strict'; | ||
let di = require('appix'); | ||
let Controller = di.load('@{appix}/controller'); | ||
class Home extends Controller { | ||
actionRedirect() { | ||
return this.redirect('/', 302); | ||
} | ||
actionRedirect301() { | ||
return this.redirect('/', 301); | ||
} | ||
actionIndex() { | ||
return 'Hello world'; | ||
} | ||
} | ||
module.exports = Home; | ||
``` | ||
- run node app/index.js | ||
- open localhost:9000 |
75568
2509
94