MEAN stack/angularjs post request failure - javascript

Im struggeling to get the server to accept my post request, cause i would like to post some data on /api/kill .
For some reason it wont work. how can i make it work?
I downloaded the latest release for Mean stack by this link: https://github.com/dickeyxxx/mean-sample
i tried to edit the article module to start with.
var promise = $http.put('/api/kill', null);
response:
angular.js:11756 PUT SITE:3000/server-error 404 (Not Found)
attempt number two:
var promise = $http.post('/api/kill', null);
returns:
angular.js:11756 POST SITE:3000/api/kill 404 (Not Found)
( only if the route is:
app.route('/api/kill').all(articlesPolicy.isAllowed)
.get(articles.list)
.put(articles.killuserbyid)
so, if route is :
app.route('/api/kill').all(articlesPolicy.isAllowed)
.get(articles.list)
.post(articles.killuserbyid)
it would return the orginal:
angular.js:11756 PUT SITE:3000/server-error 404 (Not Found)
MY codes:
in my article.server.routes.js i do have:
'use strict';
/**
* Module dependencies
*/
var articlesPolicy = require('../policies/articles.server.policy'),
articles = require('../controllers/articles.server.controller');
module.exports = function (app) {
// Articles collection routes
app.route('/api/kill').all(articlesPolicy.isAllowed)
.get(articles.list)
.put(articles.killuserbyid)
.delete(articles.delete);
// Single article routes
// Finish by binding the article middleware
app.param('articleId', articles.articleByID);
};
my invokeRolesPolicies in articles.server.policy:
exports.invokeRolesPolicies = function () {
acl.allow([{
roles: ['admin'],
allows: [{
resources: '/api/kill',
permissions: '*'
}]
}, {
roles: ['user'],
allows: [{
resources: '/api/kill',
permissions: '*'
}]
}, {
roles: ['guest'],
allows: [{
resources: '/api/kill',
permissions: '*'
}]
}]);
};
my ArticlesListController function in list-articles.client.controller:
ArticlesListController.$inject = ['ArticlesService','$scope'];
function ArticlesListController(ArticlesService,$scope) {
var vm = this;
vm.testing = function() {
ArticlesService.test();
}
}
and, finally my articleservice function (articles.client.service)
ArticlesService.$inject = ['$resource','$http'];
function ArticlesService($resource,$http) {
var o = [];
o.test = function() {
console.log("tester..");
var promise = $http.put('/api/kill', null);
promise.then(function(payload) {
console.log(payload.data);
});
}
return o;
}

If I am understanding your question/issue correctly, you are receiving a 404 error, when issuing a POST request to /api/kill in article.server.routes.js
Correct me if I'm wrong, but your current route in your code is:
app.route('/api/kill').all(articlesPolicy.isAllowed)
.get(articles.list)
.put(articles.killuserbyid)
.delete(articles.delete);
As it stands, there are only route handlers for GET PUT and DELETE requests. There are no route handlers for POST requests, so your server is issuing a 404 error since it cannot find a route handler to respond to POST requests to /api/kill.
Try modifying your route to something like the below and see if you get a response
app.route('/api/kill').all(articlesPolicy.isAllowed)
.get(articles.list)
.post(...) // replace ... with appropriate route handling function
.put(articles.killuserbyid)
.delete(articles.delete);

Related

Backbone Marionette middleware only runs on first route load

I am trying to add some middleware to Marionette's extended version of Backbone's router. Here's my code.
AppName.Router = Backbone.Marionette.AppRouter.extend({
appRoutes:{
// routes
},
route: function(route, name, callback) {
var router = this;
if (!callback) {
callback = this[name];
}
var middleware = function() {
console.log('in middleware');
callback.apply(router, arguments);
};
return Backbone.Router.prototype.route.call(this, route, name, middleware);
}
});
What I think should be happening is whenever I load a route, the console prints 'in middleware'.
What is happening is whenever I load the first route and only the first route, the console prints 'in middleware'.
I researched by using the top solution on this question and these are the results that I get.
Edit: I have also tried 'execute' as specified in the documentation and have had the same results.

Proper use of transformers vs interceptors

When POSTing to an endpoint in a service layer to update a user's profile, I need to strip certain values from the request payload (the profile with the desired modifications from the client) and re-attach them in the response payload (the updated profile from the server). I am currently performing behavior using Angular's request and response transformers, like this:
myService.updateProfile = function (profile) {
return $http({
method: 'POST',
withCredentials: true,
url: root + 'users/profile',
data: profile,
transformRequest : requestTransformer,
transformResponse : responseTransformer
});
};
// the map used during transformation below
var myMap = {
0: 'foo',
1: 'bar',
2: 'etc'
};
// prependTransform() and appendTransform() are similar to the example provided in Angular transformer docs here:
// https://docs.angularjs.org/api/ng/service/$http#overriding-the-default-transformations-per-request
var requestTransformer = httpTransformer.prependTransform($http.defaults.transformRequest, function(profileRequest) {
profileRequest.myKey = myMap.indexOf(profileRequest.myValue);
delete profileRequest.myValue;
return profileRequest;
});
var responseTransformer = httpTransformer.appendTransform($http.defaults.transformResponse, function(profileResponse) {
profileRequest.myValue = myMap[profileRequest.myKey];
delete profileRequest.myKey;
return profileResponse;
});
I prepend a transformer to the default request transformers and append a transformer to the default response transformers. My question is, is there a better way to do this? Perhaps using interceptors, as documented here, instead? If so, how?
I think your solution is fine but if you want an alternative, you can intercept specific requests like so. HTTP interceptors are mostly useful for handling global HTTP requests/responses (auth, error handling, etc.).
In any case, the "response" payload should be taken cared of from the API/server-side.
$provide.factory('userProfileInterceptor', function() {
return {
request: function(config) {
if (config.url.indexOf('/users/profile') >=0){
if (config.params.myValue) delete config.params.myValue;
}
return config;
},
response: function(response) {
if (response.config.url.indexOf('/users/profile') >=0){
delete response.data.myKey;
}
return response;
}
};
});
$httpProvider.interceptors.push('userProfileInterceptor');

Ember CLI + Ember Data + Simple Auth: authorize gets not called

i am using Ember CLI + Ember Data + Simple Auth. The authenticator is working fine. But when im am doing a Rest Call with Ember Data Rest Adapter this.store.findAll("user"); the authorize function in my custom authorizer don't gets called.
The Rest API Endpoint is on an other domain, so i added the url to the crossOriginWhitelist in my environment.js.
environment.js:
module.exports = function(environment) {
var ENV = {
// some configuration
};
ENV['simple-auth'] = {
crossOriginWhitelist: ['http://api.xxxx.com'],
authorizer: 'authorizer:xxxx',
routeAfterAuthentication: 'dashboard',
};
return ENV;
};
authorizer
import Ember from 'ember';
import Base from 'simple-auth/authorizers/base';
var XXXXAuthorizer = Base.extend({
authorize: function(jqXHR, requestOptions) {
// Some Code, gets not called, damn it :(
}
});
export default {
name: 'authorization',
before: 'simple-auth',
initialize: function(container) {
container.register('authorizer:xxxx', XXXXAuthorizer);
}
};
index.html
....
<script>
window.XXXXWebclientENV = {{ENV}};
window.ENV = window.MyAppENV;
window.EmberENV = window.XXXXWebclientENV.EmberENV;
</script>
<script>
window.XXXXWebclient = require('xxxx-webclient/app')['default'].create(XXXXWebclientENV.APP);
</script>
....
Thanks for help :)
I had a similar problem. For me it was the crossOriginWhitelist config.
I set it like this:
// config/environment.js
ENV['simple-auth'] = {
crossOriginWhitelist: ['*'] // <-- Make sure it's an array, not a string
};
to see if I could get it working (I could), then I could narrow it down to figure out exactly what URL I should use to enforce the restriction (port number and hostname etc).
Don't leave it like that though!
You should actually figure out what URL works for the whitelist, and use that.
I am facing the same issue. I have same setup but the authorize function is not being called. May be you can try by adding the port number in your crossOriginWhiteList url.
I am adding window.ENV = window.MyAppENV line in new initializer which runs before simple-auth. You have added that in index file and may be that is the reason why simple-auth is not able to read your configuration.
Does the other configuration routeAfterAuthentication: 'dashboard', works properly? If not then this might be the reason. Try adding new initializer like
export default {
name: 'simple-auth-config',
before: 'simple-auth',
initialize: function() {
window.ENV = window.MyAppNameENV;
}
};

ExpressJS why is my GET method called after my DELETE method?

In my express app, when the DELETE method below is called, the GET method is immediately called after and it's giving me an error in my angular code that says it is expected an object but got an array.
Why is my GET method being called when i'm explicitly doing res.send(204); in my DELETE method and how can I fix this?
Server console:
DELETE /notes/5357ff1d91340db03d000001 204 4ms
GET /notes 200 2ms - 2b
Express Note route
exports.get = function (db) {
return function (req, res) {
var collection = db.get('notes');
collection.find({}, {}, function (e, docs) {
res.send(docs);
});
};
};
exports.delete = function(db) {
return function(req, res) {
var note_id = req.params.id;
var collection = db.get('notes');
collection.remove(
{ _id: note_id },
function(err, doc) {
// If it failed, return error
if (err) {
res.send("There was a problem deleting that note from the database.");
} else {
console.log('were in delete success');
res.send(204);
}
}
);
}
}
app.js
var note = require('./routes/note.js');
app.get('/notes', note.get(db));
app.post('/notes', note.create(db));
app.put('/notes/:id', note.update(db));
app.delete('/notes/:id', note.delete(db));
angularjs controller
$scope.delete = function(note_id) {
var note = noteService.get();
note.$delete({id: note_id});
}
angularjs noteService
angular.module('express_example').factory('noteService',function($resource, SETTINGS) {
return $resource(SETTINGS.base + '/notes/:id', { id: '#id' },
{
//query: { method: 'GET', isArray: true },
//create: { method: 'POST', isArray: true },
update: { method: 'PUT' }
//delete: { method: 'DELETE', isArray: true }
});
});
** UPDATE **
To help paint the picture, here's the angular error i'm getting:
Error: [$resource:badcfg] Error in resource configuration. Expected response to contain an object but got an array http://errors.angularjs.org/1.2.16/$resource/badcfg?p0=object&p1=array
I'm assuming that i'm getting this error because my delete method is calling my get method (somehow) and the get method returns the entire collection.
Server side
You're removing an element from a collection in your delete function. This is done asynchronously and calling your callback when it's finished.
During this time, other requests are executed, this is why your GET request is executed before your DELETE request is finished.
The same happens in your get function, you're trying to find an element from a collection and this function is too asynchronous.
But this is server side only and it is fine, it should work this way, your problem is located client side.
Client side
If you want to delete your note after you got it, you will have to use a callback function in your angular controller which will be called only when you got your note (if you need help on that, show us your noteService angular code).
This is some basic javascript understanding problem, actions are often made asynchronously and you need callbacks to have an execution chain.
Maybe try doing something like this:
$scope.delete = function(note_id) {
var note = noteService.get({ id: note_id }, function()
{
note.$delete();
});
}
Your code doesn't make sense though, why is there a get in the $scope.delete? Why not do as simply as following:
$scope.delete = function(note_id) {
noteService.delete({ id: note_id });
}
Error
I think you get this error because of what your server sends in your exports.delete function. You're sending a string or no content at all when angular expects an object (a REST API never sends strings). You should send something like that:
res.send({
results: [],
errors: [
"Your error"
]
});

AngularJS: How to send auth token with $resource requests?

I want to send an auth token when requesting a resource from my API.
I did implement a service using $resource:
factory('Todo', ['$resource', function($resource) {
return $resource('http://localhost:port/todos.json', {port:":3001"} , {
query: {method: 'GET', isArray: true}
});
}])
And I have a service that stores the auth token:
factory('TokenHandler', function() {
var tokenHandler = {};
var token = "none";
tokenHandler.set = function( newToken ) {
token = newToken;
};
tokenHandler.get = function() {
return token;
};
return tokenHandler;
});
I would like to send the token from tokenHandler.get with every request send via the Todo service. I was able to send it by putting it into the call of a specific action. For example this works:
Todo.query( {access_token : tokenHandler.get()} );
But I would prefer to define the access_token as a parameter in the Todo service, as it has to be sent with every call. And to improve DRY.
But everything in the factory is executed only once, so the access_token would have to be available before defining the factory and it cant change afterwards.
Is there a way to put a dynamically updated request parameter in the service?
Thanks to Andy Joslin. I picked his idea of wrapping the resource actions. The service for the resource looks like this now:
.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
var resource = $resource('http://localhost:port/todos/:id', {
port:":3001",
id:'#id'
}, {
update: {method: 'PUT'}
});
resource = tokenHandler.wrapActions( resource, ["query", "update"] );
return resource;
}])
As you can see the resource is defined the usual way in the first place. In my example this includes a custom action called update. Afterwards the resource is overwritten by the return of the tokenHandler.wrapAction() method which takes the resource and an array of actions as parameters.
As you would expect the latter method actually wraps the actions to include the auth token in every request and returns a modified resource. So let's have a look at the code for that:
.factory('TokenHandler', function() {
var tokenHandler = {};
var token = "none";
tokenHandler.set = function( newToken ) {
token = newToken;
};
tokenHandler.get = function() {
return token;
};
// wrap given actions of a resource to send auth token with every
// request
tokenHandler.wrapActions = function( resource, actions ) {
// copy original resource
var wrappedResource = resource;
for (var i=0; i < actions.length; i++) {
tokenWrapper( wrappedResource, actions[i] );
};
// return modified copy of resource
return wrappedResource;
};
// wraps resource action to send request with auth token
var tokenWrapper = function( resource, action ) {
// copy original action
resource['_' + action] = resource[action];
// create new action wrapping the original and sending token
resource[action] = function( data, success, error){
return resource['_' + action](
angular.extend({}, data || {}, {access_token: tokenHandler.get()}),
success,
error
);
};
};
return tokenHandler;
});
As you can see the wrapActions() method creates a copy of the resource from it's parameters and loops through the actions array to call another function tokenWrapper() for every action. In the end it returns the modified copy of the resource.
The tokenWrappermethod first of all creates a copy of preexisting resource action. This copy has a trailing underscore. So query()becomes _query(). Afterwards a new method overwrites the original query() method. This new method wraps _query(), as suggested by Andy Joslin, to provide the auth token with every request send through that action.
The good thing with this approach is, that we still can use the predefined actions which come with every angularjs resource (get, query, save, etc.), without having to redefine them. And in the rest of the code (within controllers for example) we can use the default action name.
Another way is to use an HTTP interceptor which replaces a "magic" Authorization header with the current OAuth token. The code below is OAuth specific, but remedying that is a simple exercise for the reader.
// Injects an HTTP interceptor that replaces a "Bearer" authorization header
// with the current Bearer token.
module.factory('oauthHttpInterceptor', function (OAuth) {
return {
request: function (config) {
// This is just example logic, you could check the URL (for example)
if (config.headers.Authorization === 'Bearer') {
config.headers.Authorization = 'Bearer ' + btoa(OAuth.accessToken);
}
return config;
}
};
});
module.config(function ($httpProvider) {
$httpProvider.interceptors.push('oauthHttpInterceptor');
});
I really like this approach:
http://blog.brunoscopelliti.com/authentication-to-a-restful-web-service-in-an-angularjs-web-app
where the token is always automagically sent within the request header without the need of a wrapper.
// Define a new http header
$http.defaults.headers.common['auth-token'] = 'C3PO R2D2';
You could create a wrapper function for it.
app.factory('Todo', function($resource, TokenHandler) {
var res= $resource('http://localhost:port/todos.json', {
port: ':3001',
}, {
_query: {method: 'GET', isArray: true}
});
res.query = function(data, success, error) {
//We put a {} on the first parameter of extend so it won't edit data
return res._query(
angular.extend({}, data || {}, {access_token: TokenHandler.get()}),
success,
error
);
};
return res;
})
I had to deal with this problem as well. I don't think if it is an elegant solution but it works and there are 2 lines of code :
I suppose you get your token from your server after an authentication in SessionService for instance. Then, call this kind of method :
angular.module('xxx.sessionService', ['ngResource']).
factory('SessionService', function( $http, $rootScope) {
//...
function setHttpProviderCommonHeaderToken(token){
$http.defaults.headers.common['X-AUTH-TOKEN'] = token;
}
});
After that all your requests from $resource and $http will have token in their header.
Another solution would be to use resource.bind(additionalParamDefaults), that return a new instance of the resource bound with additional parameters
var myResource = $resource(url, {id: '#_id'});
var myResourceProtectedByToken = myResource.bind({ access_token : function(){
return tokenHandler.get();
}});
return myResourceProtectedByToken;
The access_token function will be called every time any of the action on the resource is called.
I might be misunderstanding all of your question (feel free to correct me :) ) but to specifically address adding the access_token for every request, have you tried injecting the TokenHandler module into the Todo module?
// app
var app = angular.module('app', ['ngResource']);
// token handler
app.factory('TokenHandler', function() { /* ... */ });
// inject the TokenHandler
app.factory('Todo', function($resource, TokenHandler) {
// get the token
var token = TokenHandler.get();
// and add it as a default param
return $resource('http://localhost:port/todos.json', {
port: ':3001',
access_token : token
});
})
You can call Todo.query() and it will append ?token=none to your URL. Or if you prefer to add a token placeholder you can of course do that too:
http://localhost:port/todos.json/:token
Hope this helps :)
Following your accepted answer, I would propose to extend the resource in order to set the token with the Todo object:
.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
var resource = $resource('http://localhost:port/todos/:id', {
port:":3001",
id:'#id'
}, {
update: {method: 'PUT'}
});
resource = tokenHandler.wrapActions( resource, ["query", "update"] );
resource.prototype.setToken = function setTodoToken(newToken) {
tokenHandler.set(newToken);
};
return resource;
}]);
In that way there is no need to import the TokenHandler each time you want to use the Todo object and you can use:
todo.setToken(theNewToken);
Another change I would do is to allow default actions if they are empty in wrapActions:
if (!actions || actions.length === 0) {
actions = [];
for (i in resource) {
if (i !== 'bind') {
actions.push(i);
}
}
}

Categories

Resources