forked from cfs:http-methods
why fork? remove embedded cors handler res.setHeader("Access-Control-Allow-Origin", "http://meteor.local";)
Add server-side methods to the HTTP
object your app. It's a server-side package only - no client simulations added.
Usage
The HTTP
object gets a methods
method:
1 HTTP.methods({ 2 'list': function() { 3 return '<b>Default content type is text/html</b>'; 4 } 5 });
Methods scope
The methods scope contains different kinds of inputs. We can also get user details if logged in.
this.userId
- the user whose id and token was used to run this method, if set/foundthis.method
-GET
,POST
,PUT
,DELETE
this.query
- query params?token=1&id=2
->{ token: 1, id: 2 }
this.params
- set params/foo/:name/test/:id
->{ name: '', id: '' }
this.userAgent
- get the user agent string set in the request headerthis.requestHeaders
- request headers objectthis.setUserId(id)
- option for setting thethis.userId
this.isSimulation
- always false on the serverthis.unblock
- not implementedthis.setContentType('text/html')
- set the content type in header, defaults totext/html
this.addHeader('Content-Disposition', 'attachment; filename="name.ext"')
this.setStatusCode(200)
- set the status code in the response headercreateReadStream
- if a request, then get the read streamcreateWriteStream
- if you want to stream data to the clientError
- when streaming we have to be able to send error and close connectionthis.request
The original request object
Passing data via header
From the client:
1 HTTP.post('list', { 2 data: { foo: 'bar' } 3 }, function(err, result) { 4 console.log('Content: ' + result.content + ' === "Hello"'); 5 });
HTTP Server method:
1 HTTP.methods({ 2 'list': function(data) { 3 if (data.foo === 'bar') { 4 /* data we pass via the header is parsed by EJSON.parse 5 If not able, then it returns the raw data instead */ 6 } 7 return 'Hello'; 8 } 9 });
Parameters
The method name or URL can be used to pass parameters to the method. The parameters are available on the server under this.params
:
Client
1 HTTP.post('/items/12/emails/5', function(err, result) { 2 console.log('Got back: ' + result.content); 3 });
Server
1 HTTP.methods({ 2 '/items/:itemId/emails/:emailId': function() { 3 // this.params.itemId === '12' 4 // this.params.emailId === '5' 5 } 6 });
Extended usage
The HTTP.methods
normally takes a function, but it can be set to an object for fine-grained handling.
Example:
1 HTTP.methods({ 2 '/hello': { 3 get: function(data) {}, 4 // post: function(data) {}, 5 // put: function(data) {}, 6 // delete: function(data) {}, 7 // options: function() { 8 // // Example of a simple options function 9 // this.setStatusCode(200); 10 // this.addHeader('Accept', 'POST,PUT'); 11 // // The options for this restpoint 12 // var options = { 13 // POST: { 14 // description: 'Create an issue', 15 // parameters: { 16 // title: { 17 // type: 'string', 18 // description: 'Issue title' 19 // } 20 // } 21 // } 22 // }; 23 // // Print the options in pretty json 24 // return JSON.stringify(options, null, '\t'); 25 // }, 26 // stream: true // flag whether to allow stream handling in the request 27 } 28 });
In this example the mounted http rest point will only support the get
method
Example:
1 HTTP.methods({ 2 '/hello': { 3 method: function(data) {}, 4 } 5 });
In this example all methods get
, put
, post
, delete
will use the same function - This would be equal to setting the function directly on the http mount point
Authentication
The client needs the access_token
to login in HTTP methods. One could create a HTTP login/logout method for allowing pure external access
Client
1 HTTP.post('/hello', { 2 params: { 3 token: Accounts && Accounts._storedLoginToken() 4 } 5 }, function(err, result) { 6 console.log('Got back: ' + result.content); 7 });
Server
1 'hello': function(data) { 2 if (this.userId) { 3 var user = Meteor.users.findOne({ _id: this.userId }); 4 return 'Hello ' + (user && user.username || user && user.emails[0].address || 'user'); 5 } else { 6 this.setStatusCode(401); // Unauthorized 7 } 8 }
Using custom authentication
It's possible to make your own function to set the userId - not using the built-in token pattern.
1 // My auth will return the userId 2 var myAuth = function() { 3 // Read the token from '/hello?token=5' 4 var userToken = self.query.token; 5 // Check the userToken before adding it to the db query 6 // Set the this.userId 7 if (userToken) { 8 var user = Meteor.users.findOne({ 'services.resume.loginTokens.token': userToken }); 9 10 // Set the userId in the scope 11 return user && user._id; 12 } 13 }; 14 15 HTTP.methods({ 16 '/hello': { 17 auth: myAuth, 18 method: function(data) { 19 // this.userId is set by myAuth 20 if (this.userId) { /**/ } else { /**/ } 21 } 22 } 23 });
The above resembles the builtin auth handler
Security
When buffering data instead of streaming we set the buffer limit to 5mb - This can be changed on the fly:
1 // Set the max data length 2 // 5mb = 5 * 1024 * 1024 = 5242880; 3 HTTP.methodsMaxDataLength = 5242880;
Login and logout (TODO)
These operations are not currently supported for off Meteor use - there are some security considerations.
basic-auth
is broadly supported, but:
- password should not be sent in clear text - hash with base64?
- should be used on https connections
- Its difficult / impossible to logout a user?
token
the current access_token
seems to be a better solution. Better control and options to logout users. But calling the initial login
method still requires:
- hashing of password
- use https
HTTP Client-side usage
If you want to use the HTTP client-side functionality and find yourself having a hard time viewing all available options; these can be found on https://docs.meteor.com/#/full/http.