javascript - then - http post angularjs example
AngularJS: ¿Cómo enviar auth token con $ resource requests? (8)
Deseo enviar un token de autenticación cuando solicite un recurso de mi API.
Implementé un servicio usando $ resource:
factory(''Todo'', [''$resource'', function($resource) {
return $resource(''http://localhost:port/todos.json'', {port:":3001"} , {
query: {method: ''GET'', isArray: true}
});
}])
Y tengo un servicio que almacena el token de autenticación:
factory(''TokenHandler'', function() {
var tokenHandler = {};
var token = "none";
tokenHandler.set = function( newToken ) {
token = newToken;
};
tokenHandler.get = function() {
return token;
};
return tokenHandler;
});
Me gustaría enviar el token de tokenHandler.get
con cada solicitud enviada a través del servicio Todo
. Pude enviarlo colocándolo en la llamada de una acción específica. Por ejemplo, esto funciona:
Todo.query( {access_token : tokenHandler.get()} );
Pero preferiría definir access_token como un parámetro en el servicio Todo
, ya que debe enviarse con cada llamada. Y para mejorar DRY. Pero todo en la fábrica se ejecuta solo una vez, por lo que access_token debería estar disponible antes de definir la fábrica y no puede cambiar después.
¿Hay alguna manera de poner un parámetro de solicitud actualizado dinámicamente en el servicio?
Gracias a Andy Joslin. Elegí su idea de envolver las acciones de recursos. El servicio para el recurso se ve así ahora:
.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;
}])
Como puede ver, el recurso se define de la manera habitual en primer lugar. En mi ejemplo, esto incluye una acción personalizada llamada update
. Posteriormente, el recurso se sobrescribe con el método tokenHandler.wrapAction()
que toma el recurso y una serie de acciones como parámetros.
Como es de esperar, el último método ajusta las acciones para incluir el token de autenticación en cada solicitud y devuelve un recurso modificado. Así que echemos un vistazo al código para eso:
.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;
});
Como puede ver, el método wrapActions()
crea una copia del recurso de sus parámetros y realiza un bucle a través de la matriz de actions
para llamar a otra función tokenWrapper()
para cada acción. Al final, devuelve la copia modificada del recurso.
El método tokenWrapper
primero crea una copia de la acción del recurso preexistente. Esta copia tiene un guión bajo final. Entonces query()
convierte en _query()
. Luego, un nuevo método sobrescribe el método query()
original. Este nuevo método envuelve _query()
, como lo sugiere Andy Joslin, para proporcionar el token de autenticación con cada solicitud enviada a través de esa acción.
Lo bueno de este enfoque es que todavía podemos usar las acciones predefinidas que vienen con cada recurso angularjs (get, query, save, etc.), sin tener que redefinirlos. Y en el resto del código (dentro de los controladores, por ejemplo) podemos usar el nombre de acción predeterminado.
Me gusta mucho este enfoque:
http://blog.brunoscopelliti.com/authentication-to-a-restful-web-service-in-an-angularjs-web-app
donde el token siempre se envía automágicamente dentro del encabezado de solicitud sin la necesidad de un contenedor.
// Define a new http header
$http.defaults.headers.common[''auth-token''] = ''C3PO R2D2'';
Otra forma es usar un interceptor HTTP que reemplace un encabezado de Autorización "mágico" con el token OAuth actual. El siguiente código es específico de OAuth, pero remediarlo es un ejercicio simple para el lector.
// 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'');
});
Otra solución sería usar resource.bind (additionalParamDefaults), que devuelve una nueva instancia del recurso enlazado con parámetros adicionales
var myResource = $resource(url, {id: ''@_id''});
var myResourceProtectedByToken = myResource.bind({ access_token : function(){
return tokenHandler.get();
}});
return myResourceProtectedByToken;
Se llamará a la función access_token cada vez que se llame a alguna de las acciones en el recurso.
Podría crear una función de envoltura para ello.
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;
})
Puede que esté malinterpretando toda su pregunta (no dude en corregirme :)) pero para abordar específicamente la adición de access_token
para cada solicitud, ¿ha intentado inyectar el módulo TokenHandler
en el módulo Todo
?
// 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
});
})
Puede llamar a Todo.query()
y anexará ?token=none
a su URL. O si prefiere agregar un marcador de posición de token, puede hacerlo también:
http://localhost:port/todos.json/:token
Espero que esto ayude :)
Siguiendo su respuesta aceptada, propondría ampliar el recurso para configurar el token con el objeto Todo:
.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;
}]);
De esta forma, no es necesario importar el TokenHandler cada vez que quiera usar el objeto Todo y puede usar:
todo.setToken(theNewToken);
Otro cambio que haría sería permitir acciones predeterminadas si están vacías en wrapActions
:
if (!actions || actions.length === 0) {
actions = [];
for (i in resource) {
if (i !== ''bind'') {
actions.push(i);
}
}
}
Tuve que lidiar con este problema también. No creo que sea una solución elegante, pero funciona y hay 2 líneas de código:
Supongo que obtienes tu token de tu servidor después de una autenticación en SessionService, por ejemplo. Entonces, llama a este tipo de método:
angular.module(''xxx.sessionService'', [''ngResource'']).
factory(''SessionService'', function( $http, $rootScope) {
//...
function setHttpProviderCommonHeaderToken(token){
$http.defaults.headers.common[''X-AUTH-TOKEN''] = token;
}
});
Después de eso, todas sus solicitudes de $ resource y $ http tendrán token en su encabezado.