Access request headers from beforeSave Model Hook
Asked Answered
W

5

7

How can i access the details of the user who raise the request from a Model Hook

Comment.beforeSave =  function(next,com) {
//Want to add 2 more properties before saving 
com.added_at = new Date();    
com.added_by =  //How can i set the user id here ??
//In case of a Remote hook i have ctx in param and i can get user id like this     ctx.req.accessToken.userId;  But in Model Hook how can i do the same?    
next();    
};

Is there any way to do this? I tried with Remote hook for main Item in the way

MainItem.beforeRemote('**', function(ctx, user, next) {   
if(ctx.methodString == 'leave_request.prototype.__create__comments'){       
    ctx.req.body.added_by = ctx.req.accessToken.userId;     
    ctx.req.body.added_at = new Date();                         
    console.log("Added headers as .."+ctx.req.body.added_by);
}    
else{   
    ctx.req.body.requested_at = new Date();
    ctx.req.body.requested_by = ctx.req.accessToken.userId; 
    console.log("Added header @ else as .."+ctx.req.body.requested_by);
}
next();

});

And i get the console logs properly once i make request from explorer , but But the explorer always return me the error

"error": {
    "name": "ValidationError",
    "status": 422,
    "message": "The `comment` instance is not valid. Details: `added_by` can't be blank; `added_at` can't be blank.",
    "statusCode": 422,
    "details": {
      "context": "comment",
      "codes": {
        "added_by": [
          "presence"
        ],
        "added_at": [
          "presence"
        ]
      },
      "messages": {
        "added_by": [
          "can't be blank"
        ],
        "added_at": [
          "can't be blank"
        ]
      }
    },
    "stack": "ValidationError: The `comment` instance is not valid. Details: `added_by` can't be blank; `added_at` can't be blank.\n   "
  }
}

and my model is like

 "properties": {
"body": {
  "type": "string",
  "required": true
},
"added_by": {
  "type": "number",
  "required": true
},
"added_at": {
  "type": "date",
  "required": true
},
"leave_request_id":{
  "type": "number",
  "required": true
}

}

Warmup answered 14/11, 2014 at 10:24 Comment(0)
E
10

It seems that you can't update related model with simply overriding ctx.req.body. Instead of you should override ctx.args.data - it looks like this ctx parameter is used to initalize the related model.

So it will look like that:

MainItem.beforeRemote('**', function(ctx, user, next) {   
  if(ctx.methodString == 'leave_request.prototype.__create__comments'){  
     ctx.args.data.added_by = ctx.req.accessToken.userId;     
     ctx.args.data.added_at = new Date();                         
     console.log("Added headers as .."+ctx.args.data.added_by);
  }    
  else{  ... }
  next();
Ethanol answered 20/11, 2014 at 8:36 Comment(1)
How to get request body for the operation hook in ctx?Hummer
P
2

The beforeRemote hooks execute before the model hooks, so you can add the userId to the request body.

Comment.beforeRemote('**', function (ctx, unused, next) {
    var userId = ctx.req.accessToken.userId;
    if (ctx.methodString == 'Comment.create' || ctx.methodString == 'Comment.updateAttributes') {
        ctx.req.body.userId = userId;
    }
    next();
});

you might want to review which methodstring suit you best.

Pennywise answered 14/11, 2014 at 13:37 Comment(2)
My Comment model is not exposed to api directly. So beforeRemote will not executing for it. My endpoint is MainItem/<id>/Comment. So beforeRemote will execute only for MainItemWarmup
In that case, you would need to set the hook on your MainItem model and try to store the userId in a global, or in the Comment settings. This is less than optimal, and could lead to errors on concurrent requests. Sorry bout that.Pennywise
J
1

Faced with the same problem, I used node expiremantal domain feature (that intended for error handling).

Saving incoming request object:

// -- Your pre-processing middleware here --
app.use(function (req, res, next) {
  // create per request domain instance
  var domain = require('domain').create();

  // save request to domain, to make it accessible everywhere
  domain.req = req;
  domain.run(next);
});

Next inside model hook you have access to req object, which is created per each connection:

process.domain.req 

Also StrongLoop team added context propagation (based on continuation-local-storage), but it is not documented yet.

Jp answered 18/11, 2014 at 8:2 Comment(0)
R
1

I solved this by adding the middleware for body parsing. In the middleware.js I wrote the following code:

...
"parse": {
   "body-parser#json": {},
   "body-parser#urlencoded": {"params": { "extended": true }}
},
...

Also, in the server.js I added the require for the body parser and multer:

var loopback = require('loopback');
...
var bodyParser = require('body-parser');
var multer = require('multer');
...

app.use(bodyParser.json()); // application/json
app.use(bodyParser.urlencoded({ extended: true })); // application/x-www-form-urlencoded
app.use(multer()); // multipart/form-data
...

Then add the dependencies in the package.json

"body-parser": "^1.12.4",
"multer": "^0.1.8"

Now you can do things like the following in the /models/user.js (for any model)

  user.beforeRemote('create', function(ctx, unused, next) {
     console.log("The registered user is: " + ctx.req.body.email);
     next();
  });

I hope this helps! :)

Roussel answered 10/6, 2015 at 15:14 Comment(0)
S
0

If we assume that there is a relation comments of User -> Comment, you may also to try relation method POST /users/{user_id}/comments which will populate the foreignId (which can be added_by).

Another thing is added_at. As far as I understand, validation hook is triggered before create hook. It means that validation will fail, since this field is marked as required in a model. The question is whether this field should be marked as required, since it is set by the server, and not required to be set by the client of the API.

Slurry answered 19/11, 2014 at 23:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.