How to override all blueprint find actions in sailsjs - sails.js

I would like some code to run whenever the blueprint find action is called, regardless of the model. There are three ways I can think do this, none of which I can figure out how to implement.
Override all Blueprint find actions. In theory, in my routes I'd like to something like get * overridingController.find. The from within this function do my stuff and then call the blueprint find hook.
Use middleware which runs after the controller's action. I don't see that sails has middleware which can run after an action and modify the response.
Trigger a function to run on an event, e.g. blueprint.find or res.send. I don't see events for these.

For googlers, I solved this with a policy. I still would like to find a way to run something after the blueprint action.
module.exports = async function (req, res, proceed) {
if (req.options.model && /\/find$/.test(req.options.action)) {
// do stuff
proceed(); // if you want to
} else {
proceed();
}
};

Related

MVC CORE 2.0.0 run c# code on every page

I need to run some c# code each time any page based on _layout.cshtml is viewed. I don't want to put something in every controller.cs file, just something central like you used to have in ASP.NET's MasterPage.cs
Can't get this
Run a method in each request in MVC, C#?
or this
#Html.Action in Asp.Net Core
to run, not sure if it's because they're not CORE 2.0.0, I just get a lot of compilation errors. All I want to do is be able to run some code like this
public class myClass {
public static bool returnTrue() {
return true;
}
}
every time each page is loaded.
You can accomplish this with an action filter
public class GlobalFilter : IActionFilter{
public void OnActionExecuting(ActionExecutingContext context) {
//code here runs before the action method executes
}
public void OnActionExecuted(ActionExecutedContext context) {
//code here runs after the action method executes
}
}
Then in the Startup.cs file in the ConfigureServices method you wire up the ActionFilter like so:
services.AddScoped<GlobalFilter>(); //add it to IoC container.
services.AddMvc().AddMvcOptions(options => {
options.Filters.AddService(typeof(GlobalFilter)); //Tell MVC about it
});
Then you can place code in this ActionFilter which can run before every action method and you can place code in it to run after every action method. See code comments.
Through the context parameter you have access to the Controller, Controller Name, Action Descriptor, Action Name, Request object (Including the path) and so on, so there is lots of info available for you to determine which page you want to execute the code for. I'm not aware of a specific property that will tell you if the page is using _layout.cshtml but you could probably deduce that based on the other properties I mentioned.
Enjoy.
Filter would also work, but the correct way to go in .Net Core is Middleware. You can read more about it here.
If it's something simple as your example, you can go with the first examples on the link like:
app.Use(async (context, next) =>
{
returnTrue();
await next.Invoke();
});
Let me know if it helped!

Sails v1 new machine-based actions and custom responses

I'm in the middle of upgrading our API from Sails v0.12 -> v1, which was prompted by the use of self-validating machines for controller actions. After finally getting through a ton of headache replacing deprecated code, I've landed in a rough spot...
With v0.12 (rather, with the older "req, res" controller style), one could use custom response handlers across the board. I've taken advantage of this, and have request logging at the end of all our response types (with some additional sugaring of data). This was done to log all requests in the database, so we can get insights into what our production servers are doing (because they are load-balanced, having a central place to view this is a must, and this was an easy route to take).
So now, my problem is moving forward with "Actions2" machine-style actions. How does one use these custom response types in these things? Are we being forced to repeat ourselves in our exists? I can't find any good documentation to help guide this process, nor can I find a consistent way to "hook" into the end of a response using machines as actions. I can't find any documentation on what kind of options machines can give to Sails.
#Nelson yes, I understand that, but at the time, that isn't what I wanted at all. I wanted all of the benefits of Actions2.
EDIT: While the original, crossed-out comment below does still work, the prefered way to use Actions2 and the custom responses folder paradigm, is to do something similar to the following in an Actions2 file:
module.exports = {
friendlyName: 'Human-friendly name of function',
description: 'Long description of function and what it does.',
inputs: {
userCommand: {
type: 'string',
required: true,
description: 'Long, human-readable description of the input'
}
},
exits: {
success: {
responseType: 'chatbotResponse'
}
},
fn: async function(inputs, exits){
// do some crazy stuff with the inputs, which has already been validated.
return exits.success('Woot');
}
}
This ultimately will route through the responses/chatbotResponse.js, which looks something similar to this:
module.exports = async function chatbotResponse(data){
let res = this.res,
req = this.req;
if (!data) {
data = 'Something didn\'t go as planned...';
}
// how to call a Node Machine style helper with named inputs
await sails.helpers.finalizeRequestLog.with({req: req, res: res, body: {plainString: data}});
return res.json(data);
};
ORIGINAL:
As it turns out, in the Actions2 function, you just need to add the env param async function(inputs, exists, env). The env will give you access to the req and res. So, if you have custom responses, that perform special tasks (like request logging), you can just use return await env.res.customResponse('Hurray, you made a successful call!');

Hook for REST validation with sailsjs

I need to create a validation layer for my REST services, I'm using sailsjs.
Someone know how can I do that?
I tried to create a hook but I cant access routes definitions and the hook is called before start policies :'(
The way is something like picture below.
It is perfectly fine to use policies to pre-process requests before they are passed to the controllers. Policies are not just for authentication and acl. They are so versatile you can use them for anything.
E.g.
policies/beforeUpdateTicket.js
module.exports = function(req, res, ok) {
TicketService.checkTicket(req.params.id, null, true).then(function(ticket) {
# You can even modify req.body
req.body.checked = true;
return ok();
}).fail(function(err) {
# Don't go to the controller, respond with error
return res.send(JSON.stringify({
message: 'some_error'
}), 409);
});
};

Sail.js - routing to methods, custom policies & PATCH method

I have a few questions that I couldn't find answers anywhere online.
Does sails.js framework support HTTP PATCH method? If not - does anyone know if there is a planned feature in the future?
By default if I create method in a controller it is accessible with GET request is it the routes.js file where I need to specify that method is accessible only via POST or other type of methods?
How would you create a policy that would allow to change protected fields on entity only for specific rights having users. I.e: user that created entity can change "name", "description" fields but would not be able to change "comments" array unless user is ADMIN?
How would you add a custom header to "find" method which specifies how many items there are in database? I.e.: I have /api/posts/ and I do query for finding specific items {skip: 20; limit: 20} I would like to get response with those items and total count of items that would match query without SKIP and LIMIT modifiers. One thing that comes to my mind is that a policy that adds that that custom header would be a good choice but maybe there is a better one.
Is there any way to write a middle-ware that would be executed just before sending response to the client. I.e.: I just want to filter output JSON not to containt some values or add my own without touching the controller method.
Thank you in advance
I can help with 2 and 5. In my own experience, here is what I have done:
2) I usually just check req.method in the controller. If it's not a method I want to support, I respond with a 404 page. For example:
module.exports = {
myAction: function(req, res){
if (req.method != 'POST')
return res.notFound();
// Desired controller action logic here
}
}
5) I create services in api/services when I want to do this. You define functions in a service that accept callbacks as arguments so that you can then send your response from the controller after the service function finishes executing. You can access any service by the name of the file. For example, if I had MyService.js in api/services, and I needed it to work with the request body, I would add a function to it like this:
exports.myServiceFunction = function(requestBody, callback){
// Work with the request body and data access here to create
// data to give back to the controller
callback(data);
};
Then, I can use this service from the controller like so:
module.exports = {
myAction: function(req, res){
MyService.myServiceFunction(req.body, function(data){
res.json(data);
});
}
}
In your case, the data that the service sends back to the controller through the callback would be the filtered JSON.
I'm sorry I can't answer your other questions, but I hope this helps a bit. I'm still new to Sails.js and am constantly learning new things, so others might have better suggestions. Still, I hope I have answered two of your questions.

ASP.NET MVC2 AsyncController: Does performing multiple async operations in series cause a possible race condition?

The preamble
We're implementing a MVC2 site that needs to consume an external API via https (We cannot use WCF or even old-style SOAP WebServices, I'm afraid). We're using AsyncController wherever we need to communicate with the API, and everything is running fine so far.
Some scenarios have come up where we need to make multiple API calls in series, using results from one step to perform the next.
The general pattern (simplified for demonstration purposes) so far is as follows:
public class WhateverController : AsyncController
{
public void DoStuffAsync(DoStuffModel data)
{
AsyncManager.OutstandingOperations.Increment();
var apiUri = API.getCorrectServiceUri();
var req = new WebClient();
req.DownloadStringCompleted += (sender, e) =>
{
AsyncManager.Parameters["result"] = e.Result;
AsyncManager.OutstandingOperations.Decrement();
};
req.DownloadStringAsync(apiUri);
}
public ActionResult DoStuffCompleted(string result)
{
return View(result);
}
}
We have several Actions that need to perform API calls in parallel working just fine already; we just perform multiple requests, and ensure that we increment AsyncManager.OutstandingOperations correctly.
The scenario
To perform multiple API service requests in series, we presently are calling the next step within the event handler for the first request's DownloadStringCompleted. eg,
req.DownloadStringCompleted += (sender, e) =>
{
AsyncManager.Parameters["step1"] = e.Result;
OtherActionAsync(e.Result);
AsyncManager.OutstandingOperations.Decrement();
}
where OtherActionAsync is another action defined in this same controller following the same pattern as defined above.
The question
Can calling other async actions from within the event handler cause a possible race when accessing values within AsyncManager?
I tried looking around MSDN but all of the commentary about AsyncManager.Sync() was regarding the BeginMethod/EndMethod pattern with IAsyncCallback. In that scenario, the documentation warns about potential race conditions.
We don't need to actually call another action within the controller, if that is off-putting to you. The code to build another WebClient and call .DownloadStringAsync() on that could just as easily be placed within the event handler of the first request. I have just shown it like that here to make it slightly easier to read.
Hopefully that makes sense! If not, please leave a comment and I'll attempt to clarify anything you like.
Thanks!
It turns out the answer is "No".
(for future reference incase anyone comes across this question via a search)