You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
4.7 KiB
204 lines
4.7 KiB
//
|
|
// (C) 2011, Nodejitsu Inc.
|
|
// MIT License
|
|
//
|
|
// A simple web service for storing JSON data via REST
|
|
//
|
|
// GET - View Object
|
|
// POST - Create Object
|
|
// PUT - Update Object
|
|
// DELETE - Delete Object
|
|
//
|
|
|
|
var revalidator = require('../'),
|
|
http = require('http'),
|
|
//
|
|
// Keep our objects in a simple memory store
|
|
//
|
|
memoryStore = {},
|
|
//
|
|
// Set up our request schema
|
|
//
|
|
schema = {
|
|
properties: {
|
|
url: {
|
|
description: 'the url the object should be stored at',
|
|
type: 'string',
|
|
pattern: '^/[^#%&*{}\\:<>?\/+]+$',
|
|
required: true
|
|
},
|
|
challenge: {
|
|
description: 'a means of protecting data (insufficient for production, used as example)',
|
|
type: 'string',
|
|
minLength: 5
|
|
},
|
|
body: {
|
|
description: 'what to store at the url',
|
|
type: 'any',
|
|
default: null
|
|
}
|
|
}
|
|
}
|
|
|
|
var server = http.createServer(function validateRestRequest (req, res) {
|
|
req.method = req.method.toUpperCase();
|
|
|
|
//
|
|
// Log the requests
|
|
//
|
|
console.log(req.method, req.url);
|
|
|
|
//
|
|
// Buffer the request so it can be parsed as JSON
|
|
//
|
|
var requestBody = [];
|
|
req.on('data', function addDataToBody (data) {
|
|
requestBody.push(data);
|
|
});
|
|
|
|
//
|
|
// Once the request has ended work with the body
|
|
//
|
|
req.on('end', function dealWithRest () {
|
|
|
|
//
|
|
// Parse the JSON
|
|
//
|
|
requestBody = requestBody.join('');
|
|
if ({POST: 1, PUT: 1}[req.method]) {
|
|
try {
|
|
requestBody = JSON.parse(requestBody);
|
|
}
|
|
catch (e) {
|
|
res.writeHead(400);
|
|
res.end(e);
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
requestBody = {};
|
|
}
|
|
|
|
//
|
|
// If this was sent to a url but the body url was not declared
|
|
// Make sure the body get the requested url so that our schema
|
|
// validates before we work on it
|
|
//
|
|
if (!requestBody.url) {
|
|
requestBody.url = req.url;
|
|
}
|
|
|
|
//
|
|
// Don't let users override the main API endpoint
|
|
//
|
|
if (requestBody.url === '/') {
|
|
res.writeHead(400);
|
|
res.end('Cannot override the API endpoint "/"');
|
|
return;
|
|
}
|
|
|
|
//
|
|
// See if our request and target are out of sync
|
|
// This lets us double check the url we are about to take up
|
|
// if we choose to send the request to the url directly
|
|
//
|
|
if (req.url !== '/' && requestBody.url !== req.url) {
|
|
res.writeHead(400);
|
|
res.end('Requested url and actual url do not match');
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Validate the schema
|
|
//
|
|
var validation = revalidator.validate(requestBody, schema);
|
|
if (!validation.valid) {
|
|
res.writeHead(400);
|
|
res.end(validation.errors.join('\n'));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Grab the current value from storage and
|
|
// check if it is a valid state for REST
|
|
//
|
|
var storedValue = memoryStore[requestBody.url];
|
|
if (req.method === 'POST') {
|
|
if (storedValue) {
|
|
res.writeHead(400);
|
|
res.end('ALREADY EXISTS');
|
|
return;
|
|
}
|
|
}
|
|
else if (!storedValue) {
|
|
res.writeHead(404);
|
|
res.end('DOES NOT EXIST');
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check our challenge
|
|
//
|
|
if (storedValue && requestBody.challenge != storedValue.challenge) {
|
|
res.writeHead(403);
|
|
res.end('NOT AUTHORIZED');
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Since revalidator only checks and does not manipulate
|
|
// our object we need to set up the defaults our selves
|
|
// For an easier solution to this please look at Flatiron's
|
|
// `Resourceful` project
|
|
//
|
|
if (requestBody.body === undefined) {
|
|
requestBody.body = schema.properties.body.default;
|
|
}
|
|
|
|
//
|
|
// Use REST to determine how to manipulate the stored
|
|
// values
|
|
//
|
|
switch (req.method) {
|
|
|
|
case "GET":
|
|
res.writeHead(200);
|
|
var result = storedValue.body;
|
|
res.end(JSON.stringify(result));
|
|
return;
|
|
|
|
case "POST":
|
|
res.writeHead(201);
|
|
res.end();
|
|
memoryStore[requestBody.url] = requestBody;
|
|
return;
|
|
|
|
case "DELETE":
|
|
delete memoryStore[requestBody.url];
|
|
res.writeHead(200);
|
|
res.end();
|
|
return;
|
|
|
|
case "PUT":
|
|
memoryStore[requestBody.url] = requestBody;
|
|
res.writeHead(200);
|
|
res.end();
|
|
return;
|
|
|
|
default:
|
|
res.writeHead(400);
|
|
res.end('Invalid Http Verb');
|
|
return;
|
|
}
|
|
});
|
|
})
|
|
//
|
|
// Listen to various ports depending on environment we are being run on
|
|
//
|
|
server.listen(process.env.PORT || process.env.C9_PORT || 1337, function reportListening () {
|
|
|
|
console.log('JSON REST Service listening on port', this.address().port);
|
|
console.log('Requests can be sent via REST to "/" if they conform to the following schema:');
|
|
console.log(JSON.stringify(schema, null, ' '));
|
|
|
|
}); |