Meteor - Rest API in existing project using iron:router - html sent? - api

I have an existing project that serves a webapp. I want to add a REST webservice to get data in and out.
some code:
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound'
});
Router.map(function() {
this.route('GeolocationBGRoute', {
path: '/api/v1/geolocation/',
where: 'server',
layout: null,
action: function () {
// GET, POST, PUT, DELETE
var requestMethod = this.request.method;
// Data from a POST request
var requestData = this.request.body;
// log stuff
console.log('GeolocationBG post: ' + requestMethod);
console.log(JSON.stringify(requestData));
// TODO: security/validation
// require some security with data
// validate userId/uuid/etc (inside Meteor.call?)
// Can insert into a Collection from the server (or whatever)
if (GeolocationLog.insert(requestData)) {
this.response.writeHead(200, {'Content-Type': 'application/json'});
this.response.end('ok');
return;
}
// if we end up with an error case, you can return 500
this.response.writeHead(500, {'Content-Type': 'application/json'});
this.response.end('failure');
}
});
});
When I call this page with
curl -v -H "Content-Type: application/json" -X PUT --data '{"foo":"bar"}' 'http://localhost:3000/api/v1/geolocation'
I get all the HTML back from the layoutTemplate: 'layout' - how can I just make a raw restful API which just returns JSON without the HTML of the existing project?

Add the Router.configure only on the client - either in a file under the client directory, or guard that section with an if (Meteor.isClient).
I like to keep my routes in a shared directory, but I define all of the controllers and front-end configuration stuff on the client.

Related

Dropwizard authentication header token

I am trying to implement OAuth2 authentication in a Dropwizard web-application. I have created the required Authenticator and Authorizer classes and added the code supplied in the Dropwizard manual in my Application's run-method as follows:
environment.jersey().register(new AuthDynamicFeature(
new OAuthCredentialAuthFilter.Builder<User>()
.setAuthenticator(new TokenAuthenticator(service))
.setAuthorizer(new TokenAuthorizer())
.setPrefix("Bearer")
.buildAuthFilter()));
environment.jersey().register(RolesAllowedDynamicFeature.class);
//If you want to use #Auth to inject a custom Principal type into your resource
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
My required behavior is that after my client has logged in by providing his/her credentials on my login page, I want to redirect the client to a greeting page I have created using Dropwizard Views and is under the path: "/me" as follows:
//After succesfull login and token generation
return Response.seeOther(new URI("/me")).build(); // redirect to greeting page
And my greeting resource looks as follows:
#Path("/me")
#Produces(MediaType.TEXT_HTML)
public class UserResource {
#GET
public UserView getView(#Auth User user) {
return new UserView(user);
}
}
Currently I am getting a "Credentials are required to access this resource." response after logging in. After some reading on token authentication (nice explanation here) I picked up that the token must be sent from the client in the header of each request. So my question is how do I tell the user's browser (client) to include the token in the header of future requests?
I managed to solve this by doing the following:
In order to verify the user, a token must be sent in the header of the request in the form of Authorization: Bearer <token-value>. This token is sent by the server upon authentication and must be stored by the client / user to be sent in future requests. I managed to store the token by using an ajax request when my login form is submitted as follows:
<#-- Handle form submission response to save the token on the client side-->
<script>
$('#loginForm').submit(function(event){
event.preventDefault();
$.ajax({
url: $(this).attr('action'),
type: 'POST',
data : $(this).serialize(),
dataType: 'json',
success: function(data){
//alert("The server says success!!: " +data);
console.log(data);
window.sessionStorage.accessToken = data.token;
window.location = data.url;
},
error: function(data){
alert("The server says error! : ");
console.log(data);
}
});
});
</script>
The login resource then produces JSON which is received in the data-variable in the code above. The required token resides in data.token - which is then stored. I added a second entry in the JSON named "url" to indicate the path to redirect to after successful authentication.
Now the token is stored on the client side when needed. In order to send this token in the request header, I needed to alter my approach to using the Views provided by Dropwizard. Instead of directly requiring authentication, I split the View's resource and the authenticated data resources. To clarify consider the following example. A user logs in, gets the token and then goes to a page that displays his/her username. For the page, a View resource is created with a .ftl file to serve as a template. Something like:
#Path("/me")
#Produces(MediaType.TEXT_HTML)
public class UserResource {
#GET
public UserView getView() {
return new UserView();
}
}
and...
public class UserView extends View {
public UserView() {
super("user.ftl");
}
}
And user.ftl:
<#include "include/head.html">
<#include "include/header.html">
<!-- Header -->
<div id ="headerWrapper">
</div>
<div class="container-fluid">
<div id="name">
<p>Hello user</p>
</div>
</div>
<#include "include/footer.html">
Now to retrieve the username I create a new resource which produces JSON on a new path. For example:
#Path("/getdetails")
#Produces(MediaType.APPLICATION_JSON)
public class UserDetailsResource {
#GET
#Timed
#UnitOfWork
public User getDetails(#Auth User user) {
return user;
}
}
This resource requires authentication and provides JSON from which the username can be retrieved. Now to get the username and place it inside the view, simply add a script to the users.ftl with an ajax request to the getdetails resource, providing the token in the header and using the result to place the username in the view. See script below.
<script>
$.ajax({
url: '/getdetails',
type: 'GET',
headers: {"Authorization": window.sessionStorage.accessToken},
dataType: 'json',
success: function(data){
//alert("The server says success!!: " +data);
console.log(data);
$("#name").text('Hello '+data.username);
},
error: function(data){
alert("The server says error! : ");
console.log(data);
}
});
</script>

json-server can we use other key instead of id for post and put request

I have fake api for testing in frontend side.
i have seen that id is required to put or post your data in json-server package, my question is can i use different key instead of id for ex.
{
id: 1, ---> i want to change this with my custom id
name: 'Test'
}
Let's see CLI options of json-server package:
$ json-server -h
...
--id, -i Set database id property (e.g. _id) [default: "id"]
...
Let's try to start json-server with new id called 'customId' (for example):
json-server --id customId testDb.json
Structure of testDb.json file: $ cat testDb.json
{
"messages": [
{
"customId": 1,
"description": "somedescription",
"body": "sometext"
}
]
}
Make a simple POST request via $.ajax function (or via Fiddler/Postman/etc.). Content-type of request should be set to application/json - explanation may be found on this project's github page:
A POST, PUT or PATCH request should include a Content-Type: application/json header to use the JSON in the request body. Otherwise it will result in a 200 OK but without changes being made to the data.
So... Make a request from Browser:
$.ajax({
type: "POST",
url: 'http://127.0.0.1:3000/messages/',
data: {body: 'body', description: 'description'},
success: resp => console.log(resp),
dataType: 'json'
});
Go to testDb and see the results. New chunk added. id automatically added with the desired name specified in --id key of console cmd.
{
"body": "body",
"description": "description",
"customId": 12
}
Voila!
I've came up with using custom routes in cases where I need custom id:
json-server --watch db.json --routes routes.json
routes.json:
{ "/customIDroute/:cusomID" : "/customIDroute?cusomID=:cusomID" }
If you start your server using a server.js file (read more about it in the docs), you can define custom ID routes in server.js like this
// server.js
const jsonServer = require('json-server')
const server = jsonServer.create()
const router = jsonServer.router('db.json')
const middlewares = jsonServer.defaults()
server.use(middlewares)
// custom routes
server.use(jsonServer.rewriter({
"/route/:id": "/route?customId=:id"
}))
server.use(router)
server.listen(3000, () => {
console.log('JSON Server is running')
})
And you would start your server with command:
node server.js
Internaly getById from lodash-id is used.
If you use file-based server version the equivalent to cli --id, -i
is router.db._.id = "customId";
If you want to do per resource, you can do it with a middleware like this (put before others):
server.use((req, res, next) => {
if (req.url.includes("/resourceName/")) {
router.db._.id = "code";
} else {
router.db._.id = "pk";
}
next();
});

Angular 2 AuthHttp with jwt not connecting

I'm trying to use jwt's authHttp to set an API connection to a particular Back End. I'm trying to make it first without any token so I can test it but it seams like it's not even getting connected. I'm using it as following:
this.authHttp.get('localhost:3001/api/basic')
.subscribe(
data => console.log("data"),
err => console.log(err),
() => console.log('Request Complete')
);
The error I'm getting in the console is AuthHttpError {}
I've set my ngModules as it say in the guide:
providers: [
{
provide: AuthHttp,
useFactory: authHttpServiceFactory,
deps: [Http, RequestOptions]
}
And
function authHttpServiceFactory(http: Http, options: RequestOptions) {
return new AuthHttp(new AuthConfig({noTokenScheme : true}), http);
}
The thing that drive's me crazy is that using http it works fine like this:
this.http.get('http://localhost:3001/api/basic').subscribe(
data=> console.log(data),
error=> console.log("Getting Error")
);
You are probably thinking "Why he is not using http then instead of authHttp?". Well, that's because setting a heather "Authorization" and its token seams impossible with http.
Any help or guidance would be extremely helpful.
If you don't need JsonWebTokens but simply want to add custom headers, you can do it this way without having to import the angular2-jwt library :
In your service :
private customHeaders: Headers = this.setCredentialsHeader();
setCredentialsHeader() {
let headers = new Headers();
let credentials = window.localStorage.getItem('credentials2');
headers.append('Authorization', 'Basic ' + credentials);
return headers;
}
someMethod() {
let url = 'your.URL.to.API';
return this.http
.get(url, { headers: this.customHeaders })
.map(result => {
console.log(result);
});
}
This way you can add your Authorization header with the type of data you want.
If it's a Authorization Bearer type header you are looking for and use it with angular2-jwt, you can use the default configuration first before trying to provide your own AuthHttp instance through the factory. It will be much simpler to debug and figure where the problem is.
From the documentation : https://github.com/auth0/angular2-jwt#configuration-options
AUTH_PROVIDERS gives a default configuration setup:
In your module with your service, just import the AUTH_PROVIDERS like this :
import { AUTH_PROVIDERS } from 'angular2-jwt';
...
#NgModule({
...
providers: [
AUTH_PROVIDERS,
...
]
})
and simply use the AuthHttp instance in your service like you did.
You should see in the Navigator Network tab your headers being added to your request.
EDIT :
As stated in the documentation, it is appending the token value in the headers from the Token Getter Function defined in the AUTH_PROVIDERS by default.
You therefore need to add your JWT in your LocalStorage with the default name id_token.
To give you my working example, I'm setting a JWT upon the authentication process, where I get a JWT as a response from my Http Call :
auth.service.ts
this.identityService.setToken(token.accessToken);
identity.service.ts
setToken(token?) {
if (token) {
window.localStorage.setItem('id_token', token);
} else {
window.localStorage.removeItem('id_token');
}
}
You should be able to see your JWT in your network tab if done correctly.
Afterwards, the AuthHttp instance should add the headers to your requests as intended...
It might not work correctly if your Token is not a JWT. To check if it's a good one, you can use a website such as https://jwt.io/ where it will be decoded.
If it's still not working, this means the problem is coming from elsewhere. A service not provided correctly, etc.

mootools Request class and CORS

I'm trying to use CORS to have a script do an Ajax request to geonames.
My script calls this web service method: http://www.geonames.org/export/web-services.html#findNearby
If you check the response headers of the sample call, they include:
Access-Control-Allow-Origin: *
When I try this with mootools (version 1.4.5 just downloaded):
var urlGeonames = "http://api.geonames.org/findNearbyPlaceName";
var req = new Request({
method: 'get',
url: urlGeonames,
data: {
'lat': '89.18',
'lng': '-0.37',
'username': 'myusername',
'radius': '5'
}
}).send();
then I get an error that says :
XMLHttpRequest cannot load
http://api.geonames.org/findNearbyPlaceName?lat=89.18&lng=-0.37&username=myusername&radius=5.
Origin http://127.0.0.1 is not allowed by Access-Control-Allow-Origin.</pre>
On the other hand, when I try old style Ajax code like this:
invocation = new XMLHttpRequest();
if(invocation)
{
invocation.open('GET', urlFlickr, true);
invocation.onreadystatechange = handler;
invocation.send();
}
then it works and I get the XML response in the XHR responseXML.
I found this post A CORS POST request works from plain javascript, but why not with jQuery? that is similar. But here I'm not dealing with my server so I can only work on the javascript side.
Has anyone worked with CORS and mootools and can help on this issue ?
Thanks so much
JM
Hey man check out mootools more JSONP this will solve your problem:
http://mootools.net/docs/more/Request/Request.JSONP
Also it looks like your forgetting to ask for it in JSON format from geonames.org
Try something like:
var myJSONP = new Request.JSONP({
url: 'http://api.geonames.org/findNearbyPlaceNameJSON',
data: {
'lat': '89.18',
'lng': '-0.37',
'username': 'myusername'
},
onRequest: function(url){
// a script tag is created with a src attribute equal to url
},
onComplete: function(data){
// the request was completed.
console.log(data);
}
}).send();
Hope this helps!
The first answer on this other thread:
MooTools CORS request vs native Javascript
Might help.
Basically, the X-Requested-With header is automatically sent by the Mootools with the request, but the server either has to be configured to accept that header or you can remove it using
delete foo.headers['X-Requested-With'];
Before calling
foo.send();
To allow it by the server, you can add this to the .htaccess file of your script that gives back the JSON data:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept"
So yours would look like:
var myJSON = new Request({
url: 'http://api.geonames.org/findNearbyPlaceNameJSON',
data: {
'lat': '89.18',
'lng': '-0.37',
'username': 'myusername'
},
onRequest: function(url){
// a script tag is created with a src attribute equal to url
},
onComplete: function(data){
// the request was completed.
console.log(data);
}
});
delete myJSON.headers['X-Requested-With'];
myJSON.send();

CAS authentication and redirects with jQuery AJAX

I've got an HTML page that needs to make requests to a CAS-protected (Central Authentication Service) web service using the jQuery AJAX functions. I've got the following code:
$.ajax({
type: "GET",
url: request,
dataType: "json",
complete: function(xmlHttp) {
console.log(xmlHttp);
alert(xmlHttp.status);
},
success: handleRedirects
});
The request variable can be either to the CAS server (https://cas.mydomain.com/login?service=myServiceURL) or directly to the service (which should then redirect back to CAS to get a service ticket). Firebug shows that the request is being made and that it comes back as a 302 redirect. However, the $.ajax() function isn't handling the redirect.
I wrote this function to work around this:
var handleRedirects = function(data, textStatus) {
console.log(data, textStatus);
if (data.redirect) {
console.log("Calling a redirect: " + data.redirect);
$.get(data.redirect, handleRedirects);
} else {
//function that handles the actual data processing
gotResponse(data);
}
};
However, even with this, the handleRedirects function never gets called, and the xmlHttp.status always returns 0. It also doesn't look like the cookies are getting sent with the cas.mydomain.com call. (See this question for a similar problem.)
Is this a problem with the AJAX calls not handling redirects, or is there more going on here than meets the eye?
There is indeed more going on than meets the eye.
After some investigation, it appears that jQuery AJAX requests made in this way fail if they're not made to the same subdomain. In this example, requests are being made to cas.mydomain.com from a different server. Even if it is also on mydomain.com, the request will fail because the subdomain doesn't match.
jQuery AJAX does handle redirects properly. I did some testing with scripts on the same subdomain to verify that. In addition, cookies are also passed as you would expect. See my blog post for this research.
Also keep in mind that the protocols must be the same. That is, since cas.mydomain.com is using HTTPS, the page from which you are calling it must also be on HTTPS or the request will fail.
Cross domain calls are not allowed by the browser. The simplest way would be to use JSONP on the mobile application end and use a CAS gateway to return a ticket.
You can make such cross-domain AJAX calls with a PHP proxy. In the following example the proxy is capable of calling REST web services that return a JSON string.
wsproxy.php
<?php
if (!isset($_POST["username"]) || !isset($_POST["password"]))
die("Username or password not set.");
$username = $_POST["username"];
$password = $_POST["password"];
if (!isset($_GET['url'])
die("URL was not set.");
//Rebuild URL (needed if the url passed as GET parameter
//also contains GET parameters
$url = $_GET['url'];
foreach ($_GET as $key => $value) {
if ($key != 'url') {
$url .= "&" . $key . "=" . $value;
}
}
//Set username and password for HTTP Basic Authentication
$context = stream_context_create(array(
'http' => array(
'header' => "Authorization: Basic " . base64_encode("$username:$password")
)
));
//Call WS
$json = file_get_contents($url, false, $context);
// Read HTTP Status
if(isset($http_response_header[0]))
list($version,$status_code,$msg) =
explode(' ',$http_response_header[0], 3);
// Check HTTP Status
if($status_code != 200) {
if($status_code == 404) {
die("404 - Not Found");
} else {
die($status_code . " - Error");
}
}
//Add content header
header('Content-Type: application/json');
print $json;
?>
URL usage
http://yourDomain.com/wsproxy.php?url=https://wsToCall.com/ws/resource?param1=false&param2=true
jQuery $.ajax or $.post
Note that if you don't need to pass username and password, then a GET request is sufficient.
$.ajax({
type : "POST",
url : "http://" + document.domain +
"/wsproxy.php?url=http://wsToCall.com/ws/resource?param1=false&param2=true",
dataType : "json",
success : handleRedirects,
data: { username: "foo", password: "bar" }
});