diff --git a/CHANGELOG.md b/CHANGELOG.md index 38dcb8f..e34e845 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,22 @@ # Changelog All notable changes to this project will be documented in this file. +## 0.0.3 - 2014-09-14 + +### Added +- Add URL regular expression exclude list option +- Readme updates. +- Dev dependency updates + +### Deprecated +- Nothing. + +### Removed +- Nothing. + +### Fixed +- Nothing. + ## 0.0.2 - 2014-09-14 ### Added diff --git a/README.md b/README.md index 9508ca9..028bfa5 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,32 @@ Here's an example of manual catch and re-throw: app.use(koaJsonApiHeaders()); -If you have an API endpoint you wish to not use JSON API headers for you can add jsonapiexclude=true to the URL query string. +*Exclude List* + +If you have an API endpoint that you do not want to enforce JSON API headers you can exclude it from the header validations. + +There are two methods for excluding: + +- Add jsonapiexclude=true to the URL query string. + +Example: http://localhost:3000/signin/google?jsonapiexclude=true If the URL query string key 'jsonapiexclude' exists (any value) the JSON API headers validation will be skipped. +- Pass in an exclude list of URL regular expression patterns when you use `app.use()' + +Example: + + app.use(koaJsonApiHeaders({whiteList: [ + 'signin\/google', + 'auth\/google\\?code' + ]})); + +*Note: + +- No start or end '/' +- The escaping of the '/' and the double escaping of the '?' as these are regular expression characters. + ## Tests with code coverage report in `test/coverage` Note: Requires nodes at least v0.11.13 (earlier v0.11 versions may work, have not checked for this). diff --git a/lib/koa-jsonapi-headers.js b/lib/koa-jsonapi-headers.js index 0daaafd..7629f7e 100644 --- a/lib/koa-jsonapi-headers.js +++ b/lib/koa-jsonapi-headers.js @@ -1,39 +1,64 @@ 'use strict'; -module.exports = function koaJsonApiHeaders() { +module.exports = function koaJsonApiHeaders(options) { + + if(!options) { + options = {}; + } + + if (!options.excludeList) { + options.excludeList = []; + } + + function validateJsonApiHeaders(ctx) { + + // Content-type: application/vnd.api+json + if (!ctx.header['content-type'] || !/application\/vnd\.api\+json/.test(ctx.header['content-type'])) { + ctx.throw(400, { + message: { + status: 400, + title: 'Bad Request', + detail: 'API requires header "Content-type application/vnd.api+json" for exchanging data.' + } + }); + } + + // Accept: application/vnd.api+json + if (!ctx.header.accept || !/application\/vnd\.api\+json/.test(ctx.header.accept)) { + ctx.throw(400, { + message: { + status: 400, + title: 'Bad Request', + detail: 'API requires header "Accept application/vnd.api+json" for exchanging data.' + } + }); + } + + } return function *jsonApiHeaders(next) { + var ctx = this; + /* Check for URL query value 'jsonapiexclude=true' - If it exists skip jsaonapi header validation + If it exists skip jsaonapi header validation for this request */ - var jsonApiExclude = this.query.jsonapiexclude || false; - - if (jsonApiExclude === false) { - - // Content-type: application/vnd.api+json - if (!this.header['content-type'] || !/application\/vnd\.api\+json/.test(this.header['content-type'])) { - this.throw(400, { - message: { - status: 400, - title: 'Bad Request', - detail: 'API requires header "Content-type application/vnd.api+json" for exchanging data.' - } - }); - } + var jsonApiExclude = ctx.query.jsonapiexclude || false; - // Accept: application/vnd.api+json - if (!this.header.accept || !/application\/vnd\.api\+json/.test(this.header.accept)) { - this.throw(400, { - message: { - status: 400, - title: 'Bad Request', - detail: 'API requires header "Accept application/vnd.api+json" for exchanging data.' - } - }); + /* + Check if this request is in the excludeList to not validate + If match is found jsonApiExclude to true and skip jsaonapi header validation for this request + */ + options.excludeList.forEach(function (regex) { + var r = new RegExp(regex); + if (r.test(ctx.url)) { + jsonApiExclude = true; } + }); + if (jsonApiExclude === false) { + validateJsonApiHeaders(ctx); } yield next; diff --git a/package.json b/package.json index 88c90fc..23edd44 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "koa-jsonapi-headers", - "version": "0.0.2", + "version": "0.0.3", "description": "KoaJS Validate JSON-API Request Headers Middleware", "repository": { "type": "git", @@ -29,7 +29,7 @@ "coveralls": "^2.11.1", "istanbul-harmony": "^0.3.0", "jshint": "^2.5.4", - "koa": "^0.10.0", + "koa": "^0.11.0", "mocha": "^1.21.4", "supertest": "^0.13.0" } diff --git a/test/koa-jsonapi-headers.spec.js b/test/koa-jsonapi-headers.spec.js index 2cdfa07..28d03fc 100644 --- a/test/koa-jsonapi-headers.spec.js +++ b/test/koa-jsonapi-headers.spec.js @@ -117,7 +117,7 @@ describe('JSON API Headers Middleware', function () { }); - it('should permit exclusions', function (done) { + it('should permit exclusions via URL query', function (done) { app.use(koaJsonApiHeaders()); @@ -141,6 +141,33 @@ describe('JSON API Headers Middleware', function () { }); + it('should permit exclusions via regex list', function (done) { + + app.use(koaJsonApiHeaders({excludeList: [ + 'resource\/path', + 'excluded\/endpoint\\?id' + ]})); + + // default route + app.use(function *route1(next) { + this.body = 'Excluded headers request OK'; + yield next; + }); + + request(app.listen()) + .get('/excluded/endpoint?id=1') + .expect(200) + .end(function (err, res) { + if (err) { + should.not.exist(err); + return done(err); + } + res.text.should.equal('Excluded headers request OK'); + done(); + }); + + }); + }); });