How to send POST and GET requests from Sails app to outside Sails API - sails.js

I am beginning to use SailsJS and i found it wonderful and powerful.
Can anybody please explain me how to send POST and GET requests to an API outside Sails and where do i actually write this request?
Thanks a lot and Happy 2016 everyone!!!
Edit:
Hello #arcseldon, thank you for been trying to help me.
I'll try to explain myself better, and show you my code.
I have an API, written in php (which i think is not relevant) which accepts POST, GET, PUT, DELETE methods. I use Postman for testings and everything looks OK.
I am trying to make an app in Sails, which GETs and POSTs requests to my API, but i dont know where is the best place to put the GET's and POST's codes.
In the model i already have the following to ask for a token to perform the other requests, and it works:
gettoken: function (requestnewtoken,tokenresult) {
if(!requestnewtoken) {
tokenresult(global.tokeng);
} else {
request({
headers: {
'User-agent' : 'develop',
'Content-Type' : 'application/x-www-form-urlencoded;charset=UTF-8',
'Content-Length' : '29',
'Authorization' : 'Basic ' + global.idsecret
},
uri: "https://myapi/oauth2/token",
method: "POST",
form: {
grant_type: "client_credentials"
}
}, function(error, response, body) {
var tokenjson = JSON.parse(body);
var token = tokenjson['access_token'];
global.tokeng = token;
tokenresult(token);
});
}
}
Then, i perform a GET request to another endpoint, which works:
listpublicroutes: function(requestnewtoken,cb) {
Model.gettoken(requestnewtoken,function(token) {
request({
headers: {
'Authorization' : 'Bearer ' + token
},
uri: "https://myapi/folder/file.json",
method: "GET",
timeout: 10000,
followRedirect: true,
maxRedirects: 10
}, function(error,response, body) {
if(error || (response.statusCode != 200)) {
Model.listpublicroutes(true,cb);
} else {
cb(null,JSON.parse(body));
}
});
});
}
My doubts are if this is the best way to write a POST and GET request or they could be more simple, and if the requests should be done in the controller (or anywhere else) instead of the model.
Can you give me an example of a POST and GET request?
Thanks a lot to everyone who's trying to understand me.

Your question isn't clear exactly what you are asking... Here are a few suggestions depending on what you wish to do.
If you are trying to call out and make an http request from within
server-side Sails code then I would recommend you take a look at the
NPM module request.
If you are talking about making get / post requests to test your API,
then use a web browser plugin / tool such as postman (also a
Chrome plugin of same name).
If you are talking about calling a completely different domain URL
using AJAX from within your web application client (via web browser)
then you can just use any AJAX approach (jquery / angular / whatever
client library you are using to make ajax calls) but be aware that the
domain you are calling to would have to have been setup with a cross
origin resource sharing (CORS).
You have control over your own CORS settings (allowing apps originating from other domains to call into your sails API from the browser, by updating config/cors.js settings.
Does this answer your question? If you need further help leave me a message below, and I'll try to assist.
Update Based On Question Update:
#Michi - ok, you wish to call your PHP api from within SailsJS - the three contenders in terms of location to do this are inside a Controller, a custom Model method, or within a custom service.
My "recommendation" is that most of the time, this logic sits within a Controller if it is logic that doesn't really need to be shared elsewhere. You could conceivably call into a Model.method(), but "usually" you want to think of the models as your domain data (certainly not always, if the responsibility for what you are doing truly belongs to a single Model etc which owns all the state interaction then the model might be the right place to locate that responsibility).
However, in your case, you are getting a token, and without seeing your model code (you have just provided the getToken function which doesn't really look like it is tied to a particular model..?) then I would say opt to invoke it within your controller if not needed elsewhere. Otherwise, refactor it out into a service. To quote the SailsJS documentation:
Services can be thought of as libraries which contain functions that
you might want to use in many places of your application. For example,
you might have an EmailService which wraps some default email message
boilerplate code that you would want to use in many parts of your
application. The main benefit of using services in Sails is that they
are globalized--you don't have to use require() to access them.
I have frequently put custom logic in Controllers, Models and Services, but for networking related logic etc, my preference is:
Controller if one-off logic.
Service if reusability required, or encapsulating the code as service improves readability / maintenance of the app
Use a model, only if you strongly believe the logic and responsibility is truly tied to that model - err on the side of caution here and use sparingly. But I acknowledge my recommendations may be construed as subjective - am basing them on what I believe to be good OOP practices in general terms.

Related

How to redirect the url from nested site in pencilblue?

I want to 301 redirect the URLs from previous site that are nested, as pencilblue doesn’t support them,
e.g. a/b to page/b
For this I have been experimenting in include/http/request_handler.js but facing some issues.
Call never comes inside RequestHandler.prototype.handleRequest or even RequestHandler.prototype.onSessionRetrieved (seems these methods are not being called from anywhere)
Therefore I placed the code in RequestHandler and after confirming that req is not for public resource or api, I create a new url and execute
return this.doRedirect(newUrl, 301)
This actually works but at the same time I receive
Can’t render headers after they are sent error
#1075 has not helped me much as I’m not sure which specific controller I should modify. I need to catch the req as early as possible and see if it’s a page then redirect to page prefixed url.
Thanks in advance.
There are couple of ways to do redirects. You can do them from a controller or from middleware. You are correct in that, some of the functions in the request handler are not called. These are deprecated despite the fact pencilblue team didn't mark them as such. They replaced a good deal of the request handler functionality with /include/http/router.js and include/http/middleware/index.js. Plugins can register their own middleware to hijack the request pipeline.
See Advanced Routing on wiki for more info about creating your own middleware.
Using the routing framework your plugin would be able to register middleware that would be able to inspect the request and then redirect based on your specific criteria. The Router will be accessible from req.router and from there you could call req.router.redirect (Source).
Reference: #1224

Adobe CQ 5 Accepts Arbitrary POST Request

i know that Adobe CQ 5 was built on top of Apache Sling (which using JackRabbit). I'm a bit baffled as for why the website will accept arbitrary POST request from outside (unauthenticated user) into Publish Instance through the Dispatcher and then reply with HTTP 200 Content Updated. Should not content update only allowed from Author Instance in this case? Should not such request met with HTTP 403 response instead? - Why is it that even not logged in can get HTTP Response 200?
The response looked like this:
{
"changes": [],
"referer": "http://www.example.com/content/somesite/en.html",
"path": "/content/somesite/en",
"location": "/content/somesite/en",
"parentLocation": "/content/somesite",
"status.code": 200,
"status.message": "OK",
"title": "Content modified /content/somesite/en"
}
I've set POST Referrer Filter for now to prevent arbitrary POST request 'outside' the website got accepted, however i can still get this response by typing jquery ajax request in the browser console while opening the website.
I do wonder if this is bad or something, really new to Adobe CQ.
The JQuery Script for testing it is actually only these:
$.ajax({
url: 'http://www.example.com/content/fasfas',
type: 'post',
data: {},
headers: {
Accept: 'application/json',
},
dataType: 'json',
success: function (data) {
console.info(data);
}
});
Thanks in advance!
This is an issue of not taking the necessary steps to secure the AEM servers. There is a security checklist provided by Adobe to ensure that AEM installation is secure when deployed. Similar security checklist for the dispatcher is also present.
As for your case, there are few issues which are evident
The filter configuration within the dispatcher doesn't deny POST
requests, thereby allowing them to pass through the dispatcher and reach the AEM instance.
The anonymous user in the AEM publisher seems to have more than just
READ privileges on the repository thereby allowing him to make changes to the repo using POST requests.
The referrer filter configuration was allowing requests from external systems as well (which you have blocked now).
Your dispatcher should block all POST operations on the publisher. This is recommended in Adobe's official documentation for configuring dispatcher.
Publisher should also disable write permission for anonymous users and everyone group to paths that are not allowed to be modified by community. Unless you are using CUG, write should be disabled for anonymous across the publisher instance.

Calling external REST web service from Single Page Application

I am creating a SPA with Backbone & Underscore JS. The basic feature of the app being that on entering a search term , it needs a trigger an external REST web service call and fetch the JSON response. However when i try this, the browser cancels the request as i guess it tries to make a cross-domain AJAX call.
I am hosting this SPA in my local and the REST web service is hosted on a external server. If i need to make cross-domain calls, what is the procedure which i need to follow without making any changes in the server side? I heard JSONP is one of the alternatives but not sure on the approach.
It looks like it is the same problem as in this question. It is pretty useful:
JSONP and Backbone.js
If your external service supports it already, you are correct that JSONP would be the way to go for cross domain requests without having to change anything on the server side. I assume you're using jQuery. Here's an example from jQuery's docs:
var flickerAPI = "http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?";
$.getJSON( flickerAPI, {
tags: "mount rainier",
tagmode: "any",
format: "json"
})
.done(function( data ) {
console.log(data);
});
You'll notice the ?jsoncallback=? in the flickr URL. That tells flickr to wrap the response in a JSONP callback instead of just returning normal JSON. When flickr sees that, they wrap the response like this:
jQuery19104044632513541728_1395560629443({
"title": "Recent Uploads tagged mountrainier",
...other json data...
});
So instead of returing JSON, they wrap it in a function call which jQuery puts on the global window object. That function call will call your success function with the json data.
Luckily, you don't have to know anything about the inner-workings of it. All you do is call $.getJSON and it'll work!

Accessing JSON Resource on a RESTful one page app

Given a one page app that uses push state and RESTful backend, we can imagine accessing the listing of a resource at /resourceName (i.e. /users). So /users would create a formated list of users
Now the problem is that this resource JSON or XML feed should also be mapped to /resourceName, so if boot form my application entry point at / then all is good, when navigating to /users the JS router can trigger a Ajax call that get the JSON data. Now the problem is if the URL is pointing directly at /users then i will land on a JSON feed instead of the actual listing. I could route all call to a main entry point and then let the JS router do the work though if i do so the AJAX call to fetch JSON wil brake.
I remember a while ago people adding .json to their json request, or even a GET parameter ?format=json and then having the controller taking different actions. I find that somewhat hacky.. Are there any other ways to go about this?
For that matter i am using laravel4 backend and backboneJS
I think the .json on the end of the request is the best approach. the other approach could be to create a separate endpoint endpoint for api request api.mydomain.com vs www.mydomain.com
What method you use to get a different response depends on how you'd like to go about it. Since you're asking about an opinionated topic (There is no one right answer), here's some options you can explore.
First, here's a good read from Apigee on API design, which covers what I'll write about here. See page 20 on "Support multiple formats"
The Rails way: Append a .json, .xml or other extension at the end of your request and handle that code within Laravel (You may want to use the "before" filter to check the request or Laravel's excellent route parameters, which allow the use of regex to define the route).
You can check the "accept" header in the request and set that header in your ajax calls to "application/json" instead of the default "application/html" to inform your application which format to use in its response. Again, the before or after filters may come in handy to check the request and define the response as appropriate
Create a query string `?format=json" or similar. Same comments as in point 1.
Laravel doesn't have built-in methods to change the response for you. You can, however, fairly easily detect what's being asked and choose which format to return in. It does take some thinking about how you want to accomplish that, however.
Some options off the top of my head:
Use the "before" or "after" filter to check what the request "wants" for a format, and do some transformations on the response to make that work
Extend the request and response class to "automate" this (request class to detect format, response class to transform the response to the correct format)
Hope that helps
It's valid to say which representation do you want. E.g. JSON, XML or binary, depends on what you want and which serializers have you developed.
You framework should support either setting of default representation or if you provide some mapping URL -> method you should be able to say which representation you are going to return - also either by default or taken within some object which represents your request.
I ended up using different endpoints as suggested by #Aaron Saunders. In laravel 4 this is dumb easy to implement using group routes:
app.php:
'domain' => 'whatever.dev',
routes.php:
define('APP_DOMAIN', 'app.' . Config::get('app.domain'));
define('API_DOMAIN', 'api.' . Config::get('app.domain'));
Route::group(array('domain' => API_DOMAIN), function()
{
// API ROUTES
});
Route::group(array('domain' => APP_DOMAIN), function()
{
// VIEW ROUTES
});
Beautiful!

Logging a user out when using HTTP Basic authentication

I want users to be able to log in via HTTP Basic authentication modes.
The problem is that I also want them to be able to log out again - weirdly browsers just don't seem to support that.
This is considered to be a social-hacking risk - user leaves their machine unlocked and their browser open and someone else can easily visit the site as them. Note that just closing the browser-tab is not enough to reset the token, so it could be an easy thing for users to miss.
So I've come up with a workaround, but it's a total cludge:
1) Redirect them to a Logoff page
2) On that page fire a script to ajax load another page with dummy credentials:
$j.ajax({
url: '<%:Url.Action("LogOff401", new { id = random })%>',
type: 'POST',
username: '<%:random%>',
password: '<%:random%>',
success: function () { alert('logged off'); }
});
3) That should always return 401 the first time (to force the new credentials to be passed) and then only accept the dummy credentials:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOff401(string id)
{
// if we've been passed HTTP authorisation
string httpAuth = this.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(httpAuth) &&
httpAuth.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
// build the string we expect - don't allow regular users to pass
byte[] enc = Encoding.UTF8.GetBytes(id + ':' + id);
string expected = "basic " + Convert.ToBase64String(enc);
if (string.Equals(httpAuth, expected, StringComparison.OrdinalIgnoreCase))
{
return Content("You are logged out.");
}
}
// return a request for an HTTP basic auth token, this will cause XmlHttp to pass the new header
this.Response.StatusCode = 401;
this.Response.StatusDescription = "Unauthorized";
this.Response.AppendHeader("WWW-Authenticate", "basic realm=\"My Realm\"");
return Content("Force AJAX component to sent header");
}
4) Now the random string credentials have been accepted and cached by the browser instead. When they visit another page it will try to use them, fail, and then prompt for the right ones.
Note that my code examples are using jQuery and ASP.Net MVC, but the same thing should be possible with any technology stack.
There's another way to do this in IE6 and above:
document.execCommand("ClearAuthenticationCache");
However that clears all authentication - they log out of my site and they're logged out of their e-mail too. So that's out.
Is there any better way to do this?
I've seen other questions on this, but they're 2 years old - is there any better way now in IE9, FX4, Chrome etc?
If there is no better way to do this can this cludge be relied upon? Is there any way to make it more robust?
The short anser is:
There is no reliable procedure for achieving a "logoff" using
HTTP Basic or Digest authentication given current implemenations of basic auth.
Such authentication works by having the client add an Authorization header
to the request.
If for a certain resource the server is not satisfied with the credentials provided (e.g. if there are none), it will responde with a
"401 Unauthorized" status code and request authentication. For that purpose it will provide a WWW-Authenticate header with the response.
A client need not wait for a server requesting authentication.
It may simply provide an Authorization header based on some local
assumptions (e.g. cached information from the last successful attempt).
While your outlined approach on "clearing" out authentication info has a good chance of working with a wide range of clients (namely widespread browsers),
there is absolutely no guarantee that a nother client might be "smarter" and
simply discriminate proper authentication data for your "logout" page and any other pages of the target site.
You will recognize a similar "problem" with using client side certificate based authentication.
As long as there is no explicit support from clients you might fight on lost ground.
So, if "logoff" is a concern, move over to any session based authentication.
If you have access to the implementation of authentication on the server side you might be able implementing a functionality that will disregard authentication information presented with Authorization header (if still identical to what has been presented during current "session) on request of your application level code (or provide some "timout" after which any credentials will be re-requested), so that the client will ask the user for providing "new" credentials (performing a new login).