So I have been working on a SPA project using Sailsjs. The problem is:
When the first page loads, I use res.view or res.render in my controller. But, for subsequent requests, I dont want to use res.view or res.render, rather I'd like to use res.json.
Right now, I use the method:
return req.param('json') ? res.json(myObj) : res.view('layout', myObj);
This works, but I was looking for a more generic and abstract method in which my controller itself would know (on the basis of req.param('json')) whether to use res.view or res.json without me having to tell it explicitly.
Any help ?
This is what res.ok() is for.
In the controller action below, res.ok will either display the myAction.ejs using data as the view locals, or respond with data as JSON, depending on how the request came in (i.e. via AJAX, sockets or a regular browser request):
module.exports = {
myAction: function(req, res) {
var data = {someKey: "someVal"};
return res.ok(data);
}
}
Internally, the ok response uses req.wantsJSON to determine what to do; it checks headers, looks for an xhr object and generally does its best to guess your intent. req.wantsJSON is available for all requests (as is req.isSocket), so you can use them yourself as needed.
So after a bit of tinkering, I resolved this using a service.
I wrote a service (in GlobalUtils.js):
render: function (req, res, obj) {
if(req.param('json')) {
return res.json(obj);
}
else {
return res.view('layout', obj);
}
}
And I use this service in my controllers, like so:
return GlobalUtils.render(req, res, myObj);
But still, looking for a better method.
Related
I have anPOST api endpoint lets say /users to fetch the list of users.
It is POST because body of the request is very huge and might not fit in url for GET request.
suppose the body of user POST have a key called age , which should give me user of certain age ie kind of filtering
now in express i have route like
app.post('/users', function(r,res){
// function body
})
and i cant actually put any code inside that function body
so i was able to intercept the request by using one more handler for /users and putting it before the original handler but obviously it intercepts all /users requests and breaks earlier functionality
how can i intercept only the request with particular age and then pass through other requests to the original handler, so that original functionality keeps working?
I want to know how can i do this using route handlers and not middlewares ?
i cant mess with the url or request body also
First off, this sounds like a really bad design so really the better way to fix things is to just fix the URL design so you don't have this conflict between code you can and can't modify. I say this because it sounds like you're trying to "hack" into something rather than make a proper design.
If your code is using the regular body-parser middleware, then the body of the post will already be parsed and in req.body. So, you can look for the desired parameter in req.body.age and check its value.
If it meets your criteria, then you can process the request and you're done. If it doesn't meet your request, then you call next() to continue processing to other request handlers.
// make sure this is defined BEFORE other /users request handlers
app.post('/users', function(req, res, next) {
// test some condition here
if (+req.body.age > 30) {
// process the request and send a response
res.send("You're too old");
} else {
// continue processing to other request handlers
next();
}
})
The way I deal with this is if I have a route that works, and I need something else, I add another route that is similar. This way you leave the original alone - which provides a working service. This is what I think you re describing.
You can call routes anything you like. If you want a list of users you can pass a variable like this:
$.get('/contactCard/'+qry);
app.get('/contactCard/:sort', function(req, res) {
var cId = req.params.sort;
console.log('cId: ' + cId);
then you set up your search query and go get the data a bit like this:
let params = {
TableName: ddbTable,
ProjectionExpression : "cEmail,Forename,Surname",
KeyConditionExpression: "ID = :e ",
ExpressionAttributeValues: {
":e" : cId
}
};
console.log("params", JSON.stringify(params, null, 2));
docClient.query(params, function(err, data) {
then you check for error or success:
if (err) {
console.log("Error:", JSON.stringify(err, null, 2));
} else {
console.log("Success", JSON.stringify(data, null, 2));
let contacts = data;
then here you render to the page you want and pass the data as you wish.
res.render('members/contactcard', {
contacts:contacts,
static_path: '/static'
});
}
});
MEAN stack newbie here. I'm having difficulty understanding how delete works in MEAN. I'm using this SO Q&A and tutorial as guides, but whenever I test it out I get an error saying the data can't be deleted. Can somebody tell me what I've been doing wrong?
Here are my codes:
Controller JS
$scope.deleteProduct = function (value, idx) {
var this_id = value._id;
// delete
$http.delete('/api/products/delete:' + this_id)
.success(function (data) {
console.log(data);
})
.error(function (data) {
console.log('Error: ' + data);
})
}
Node Server
app.delete('/api/products/delete:', productController.delete);
Server's "Controller"
module.exports.delete = function (req, res) {
Service.remove({
_id: req.params._id
}, function (err, service) {
if (err) {
res.send(err);
}
else {
res.json({message: "Delete successful."});
}
});
}
This is how I understood this. Is this correct?
Controller JS gets the id to be deleted and calls $http's delete request(?), using said ID and the /api/products/delete:.
Node Server sees that I called '/api/products/delete:' and passes the request to Server's Controller to complete the request.
Server's Controller deletes the data and returns status.
Where did I go wrong? Please help.
Also, I've been seeing some posts that say $resource works better than $http. Why?
Thank you.
I think you've got a couple things wrong here.
In Express in order to use params you need to have something in the route that can be replaced. i.e /api/:id express replaces the :id with whatever you pass in so if you send /api/1, request.params.id is 1
So first problem is your route is
app.delete('/api/products/delete:', productController.delete);
tha dosen't mean anything to Express. I think you want
app.delete('/api/products/:id', productController.delete);
now req.params.id should contain the parameter you send. Note im dropping the underscore here. you could use
app.delete('/api/products/:_id', productController.delete); and keep the underscore if you like.
Second mistake I think is your Angular code. you have the : in your call it should just be
$http.delete('/api/products/' + this_id)
Now you're sending the route with whatever Id you are trying to delete i.e
/api/products/1
Now Express gets that and can map it to /api/products/:id and replace the id and now your controller should work. barring any other issues.
Edit
I'm not very familiar with Angular but I think the reason people are saying to use $resource is it is easier. You can directly call the different HTTP verbs directly on the objects themselves objects like
product.update and product.delete rather than trying to craft the http calls yourself. I'm sure there is a lot more to it than that but its a feature that's built into Angular that can be leveraged. I think one of the catches is the URLs for the resources just have to be set up a specific way on the server but I believe there was a way to override them in Angular.
Newbie question while trying to understand code created by others. Believe me I tried to understand this. Here goes..
For what reason would someone still call functions like .qs() and .json() in Request - module after we got what we need with .post() and sent the response already. They can't affect the request.post as they are called afterwards, can they?
With my skills I'm not able to understand from response module API docs (v2.22.0) what these actually do.
This is not the whole code but I tried to get the important parts here:
// When request comes to /getthisapge, make external query and return data in JSON format.
var request = require('request');
module.exports = function(app) {
app.get('/getthispage', function(req, res, next) {
var filter = {};
var query = {};
filter.category = req.query.category;
query.onBehalf = req.query.onBehalf;
request.post(URIandoptions, function(error, response, body) {
res.json(body.members)
}).qs(query).json(filter);
}
}
Without knowing exactly what the post function does (unnecessary to your question), you need to look at the order of execution.
request.post(URIandoptions, function (error, response, body){
res.json(body.members)
})
.qs(query) // ?
.json(filter); // ?
The function passed into post() does not get called at that specific moment. It is given to the post() function to do with as it pleases. This means technically that the function may never be called (depends on the api).
qs() and json() both get called upon the returning of the prior function. Usually this type of api means the following:
call post(), passing in a function to be run on completion
call qs() to setup the query details
call json() to tell the post function how to act, which in turn executes the actual post, running the completion function after data has been retrieved.
I understand that out of the box Express isn't an MVC framework, however I'm trying to set it up like one.
I've used other similar frameworks in PHP like Laravel where in a route in you can use a controller like
Route::get('user/profile', 'UserController#showProfile');
Which will run all the code in the showProfile method in the UserController class,
so my question is, how would I achieve the same thing or something similar using Express?
I'm using Node 5 and writing the code in ECMAScript 6.
Currently I have a class I want to use as the controller and a method I want to return the data, I'm able to log the data to the console when a user navigates to the route but haven't figured out how to send it back as the response.
If you dive into the documentation, you'll find that the "controller methods" you refer to need to conform to a specific signature. Namely, they receive (at least) the request and response representations.
If you have already created a router, this will be a rough equivalent to the PHP you posted:
router.get('user/profile', userController.showProfile)
Your showProfile "method" needs to have this signature:
const userController = {
showProfile(req, res) { /*...*/}
}
I put "method" in quotes because express won't call it as a method unless you explicitly bind it to the controller object. We're passing it as an unbound function here. If you wanted to use it as a method (to have access to the controller as this), pass userController.showProfile.bind(userController) to router.get†.
But for now let's stick to those req and res parameters of the showProfile handler (that's the proper name). req represents the HTTP request, you can get headers, request payload and other stuff from it. res is the HTTP response that will be sent. So you can use it to set an HTTP status code, send body data and so on.
For illustrative purposes, let's assume you can get your user profile synchronously by calling userController.getProfile(id). And let's assume that, by some mechanism, a property userId is set on the request that is the currently authenticated user's ID.
const userController = {
showProfile(req, res) {
// We call some code to get what we want to send back
const profile = userController.getProfile(req.userId)
// We send it in the response as JSON
res.send(profile)
}
}
res.json will JSON.stringify the profile and send it as response payload.
How do you get req.userId set, you ask? Well, this is just an example, but you can achieve similar results by using middleware. A middleware is simply a handler that does something and then lets other handlers continue processing the request. But again, there's plenty to read from the docs.
† It's usually not necessary though, since controllers tend to be singletons. You can simply access its properties by doing userController.otherProperty. Of course, you don't even need to define a handler as a method of a controller object, it can be a function that stands on its own.
I did something like this
usercontroller.js
class UserController() {
constructor() {
this.users = ['user1', 'user2'];
}
getUsers(req, res) {
res.status(200).json(this.users);
}
}
//router.js
const port = 3000;
const app = express();
const _invoke = function(controller) {
return function(req, res) {
const [controllerClass, method] = controller.split('#')
const className = require(controllerClass)
const obj = new className
obj[method](req, res)
}
}
app.get('/get-users',
_invoke('./controllers/UserController#getUsers'));
app.listen(port);
I'm trying to implement update functionality to an Express.js app, and I'd like to use a PUT request to send the new data, but I keep getting errors using PUT. From everything I've read, it's just a matter of using app.put, but that isn't working. I've got the following in my routes file:
send = function(req, res) {
req.send(res.locals.content);
};
app.put('/api/:company', function(res,req) {
res.send('this is an update');
}, send);
When I use postman to make a PUT request, I get a "cannot PUT /api/petshop" as an error. I don't understand why I can't PUT, or what's going wrong.
You may be lacking the actual update function. You have the put path returning the result back to the client but missing the part when you tell the database to update the data.
If you're using MongoDB and ExpressJS, you could write something like this :
app.put('/api/:company', function (req, res) {
var company = req.company;
company = _.extend(company, req.body);
company.save(function(err) {
if (err) {
return res.send('/company', {
errors: err.errors,
company: company
});
} else {
res.jsonp(company);
}
})
});
This mean stack project may help you as it covers this CRUD functionality which I just used here swapping their articles for your companies. same same.
Your callback function has the arguments in the wrong order.
Change the order of callback to function(req, res).
Don't use function(res, req).
Also if you want to redirect in put or delete (to get adress), you can't use normal res.redirect('/path'), you should use res.redirect(303, '/path') instead. (source)
If not, you'll get Cannot PUT error.
Have you been checking out your headers information?
Because header should be header['content-type'] = 'application/json'; then only you will get the update object in server side (node-express), otherwise if you have content type plain 'text/htm' like that you will get empty req.body in your node app.