Trying to take a url and in an AngularJS controller redirect that request to the best place to get the json data. The VideoSearchCtrl is bound to the search form. The url generated is correct for the template so I'm using the controller to redirect it to the place for the json data.
GuideControllers.controller('VideoSearchCtrl', ['$scope', 'VideoSearch',
function($scope, VideoSearch) {
var pattern = new RegExp(".*/search\\?=(.*)");
var params = pattern.exec( document.URL )[1];//redirect to videos to execute the search for the data
$scope.videos = VideoSearch.query({ resource: "videos", action: "search", q: params });
}
]);
This sends /videos/search?q=xyz in to the query. The factory creates the resource:
var VideoSearchServices = angular.module('VideoSearchServices', ['ngResource']);
VideoSearchServices.factory('VideoSearch', ['$resource',
function($resource){
return $resource("/:resource/:action:params", {resource: "#resource", action: "#action", params: "#params"}, {
query: {
isArray: true,
method: "GET",
headers: {
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
}
});
}
]);
But the server gets the url as /videos/search%fq=xyz, not /videos/search?q=xyz and therefore the "show" method is being invoked instead of a custom "search" action. Obviously there is some escaping somewhere? Or maybe the "?" is also a special pattern the resource factory looks for? Probably obvious to someone used to AngularJS or javascript for that matter.
I have a template for search and the json is retrieved from a different location. Both work but I can't ask for the json with the above code.
First, do:
return $resource("/:resource/:action", {resource: "#resource", action: "#action"}, {
Then:
$scope.videos = VideoSearch.query({ resource: "videos", action: "search", q: params });
The point is params are not a part of the url you have to declare to the resource, you just declare resource and action then you add params which is natural for all routes
Actually there is a better way to do this with $location from Angular and jQuery's extend that should work with any future params that are added. Will only have to add the new params to the query factory.
GuideControllers.controller('VideoSearchCtrl', ['$scope', '$location', 'VideoSearch',
function($scope, $location, VideoSearch) {
var route = jQuery.extend(
{ resource: "videos", action: 'search' },
$location.search()
);
$scope.videos = VideoSearch.query(route);
}
]);
Related
I have this $http request interceptor
app.config(function($httpProvider) {
$httpProvider.interceptors.push(function() {
return {
request: function(req) {
// Set the `Authorization` header for every outgoing HTTP request
req.headers['cdt_app_header'] = 'tamales';
return req;
}
};
});
});
Is there any way we can add a header or cookie to every $http request, but keep the header value secure / not visible with JavaScript?
We can add an obfuscation layer with this header to prevent easy access to our API endpoints, but I am wondering about a more truly secure solution.
Cookies are used for secure sessions, and these are more secure because they cannot be accessed with JavaScript. Say we have a user who can do this request with front-end code:
GET /api/users
we don't really want them to be able to make a simple request with cURL or a browser without an extra piece of information. The cookie we give them will give them the ability to use the browser address bar to make a GET request to /api/users, but if we add the requirement to have another cookie or header in place, then we can prevent them from accessing endpoints that are authorized for, in a format that we don't really want them to use.
In other words, we want to do our best to give them access, but only in the context of a front-end Angular app.
I can't add a comment because of my rep but what are you doing on the back-end to authorize users? If the cookie is signed and contains user permissions it shouldn't matter that the header is visible in the client as it will also be verified on the back-end API call.
in this sample i used HttpRestService to get RESTful API, read this article
at first we create a service to get our configs in this sample is getConfigs
we use getConfigs in the app.run when application is started, after get the configs we set them all in the header as sample.
after that we can get userProfile with new header and also secure by call it from our controller as you see.
in this sample you need to define apiUrl, it's your api host url, remember after logout you can remove the header, also you can define your configs dynamically to make more secure for your application.
HttpRestService.js github link
app.js
var app = angular.module("app", ["HttpRestApp"]);
app.service
app.service("service", ["$http", "$q", "RestService", function (http, q, restService) {
this.getConfigs = function () {
var deferred = q.defer();
http({
method: "GET",
async: true,
headers: {
"Content-Type": "application/json"
},
url: "you url to get configs"
}).then(function (response) {
deferred.resolve(response.data);
}, function (error) {
deferred.resolve(error);
});
return deferred.promise;
}
var api = {
user: "User" //this mean UserController
}
//get user with new header
//this hint to your api with this model "public Get(int id){ return data; }"
//http://localhost:3000/api/users/123456
this.getUserProfile= function(params, then) {
restService.get(params, api.user, true).then(then);
}
}]);
app.run
app.run(["RestService", "service", function (restService, service) {
var header = {
"Content-Type": "application/json"
}
//get your configs and set all in the header
service.getConfigs().then(function (configs) {
header["systemId"] = configs.systemId;
});
var apiUrl = "http://localhost:3000/";
restService.setBaseUrl(apiUrl, header);
}]);
app.controller
app.controller("ctrl", ["$scope", "service", function ($scope, service) {
$scope.getUserProfile = function () {
//this is just sample
service.getUserProfile({ id: 123456 }, function (data) {
$scope.user = data;
});
}
$scope.getUserProfile();
}]);
I'm trying to hit an endpoint that looks like /shop/<item_id>
However, because it also requires an auth token, I am sending an array to my $resource along with the token.
APIshop.create([CreateCampaignService, {auth_token: User.token}]
How can I make it so APIshop is able to take something like this:
APIshop.create([CreateCampaignService, {auth_token: User.token}, {item_id: Cart.item_id}]) and hit /shop/<item_id>?
Here is APIshop:
app.provider('APIshop', function(API_URL) {
this.$get = ['$resource', function($resource) {
var shop = $resource(API_URL.url + API_URL.loc + ':service/', {service: '#service'}, {
'create': {method:'POST', isArray: false, params: {service: 'shop'}},
'update': {method:'PUT', isArray: false, params: {service: 'shop'}}
});
return shop;
}];
});
You can't send it in the array since it is only accepting one param unless you concatenate the token and item id. That is bad practice. You should include the token in the header of the request.
take a look here for more info
Basic Authentication with a Guid token for REST api instead of username/password
In https://docs.angularjs.org/api/ngResource/service/$resource under paramDefaults
Default values for url parameters. These can be overridden in actions
methods. If any of the parameter value is a function, it will be
executed every time when a param value needs to be obtained for a
request (unless the param was overridden).
Each key value in the parameter object is first bound to url template
if present and then any excess keys are appended to the url search
query after the ?.
Given a template /path/:verb and parameter {verb:'greet',
salutation:'Hello'} results in URL /path/greet?salutation=Hello.
If the parameter value is prefixed with # then the value of that
parameter will be taken from the corresponding key on the data object
(useful for non-GET operations).
So this should work with TOKEN stored somewhere ($rootScope maybe):
app.provider('APIshop', function(API_URL) {
this.$get = ['$resource', function($resource) {
var shop = $resource(API_URL.url + API_URL.loc + ':service/:id', {service: '#service', id:'#id'}, {
'create': {method:'POST', isArray: false, params: {service: 'shop', access_token: TOKEN}},
'update': {method:'PUT', isArray: false, params: {service: 'shop', access_token: TOKEN}}
});
return shop;
}];
});
I am designing UI for a REST based server side application developed using Jersey API. I want to take inputs from HTML form and pass the data as query string parameters to build the REST URL. I went through the AngularJS documentation too but didn't find the answer to my question. I came to know that $resource is used for GET/POST/PUT requests and I know that we can build URL with query strings using $resource. Can anyone guide me on how to pass the query string parameters from form fields?
Create an input field in HTML and button to add data
<div ng-controller="myCtrl">
<input ng-model="item.name" />
<button ng-click="addItem()">Add Item</button>
</div>
Create a Service for calling API
var myApp = angular.module("myApp",['ngResource'])
myApp.factory("myService", function ($resource) {
return $resource(
"http://localhost/items/:id",
{id: '#id'},
{
update: { method: 'PUT' },
query: {method: 'GET', isArray: true},
get: {method: 'GET'}
}
)
})
Create a Controller
myApp.controller("myCtrl", function ($scope, myService) {
$scope.Details = myService.get();
$scope.addItem = function () {
myService.save($scope.item, function (data) {
$scope.Details.push(data);
$scope.item = {};
});
};
I'm setting up a project using django-tastypie REST API and AngularJS. I'm fine with reading things from the json file through angular, but I cannot find a decent tutorial that would show me how to make even a simple CRUD application that isn't saving all the information in an object or whatever, but is manipulating the database through the tastypie api. Can any of you show me a tutorial of such sort or maybe just show me some sample code for this?
Thank you.
Use $resource - A factory which creates a resource object that lets you interact with RESTful server-side data sources.
Let's say you have Django model Book, and tastypie resource named BookResource. It's URL is /api/v1/book/. As you know, this URL actually is a resource, that means you can manipulate data in your Book model with GET, POST, DELETE, etc. requests.
You can "map" the Angular $resource to this API resource in a way:
someModule.factory('bookResource', ['$resource', function($resource) {
var apiResourceUrl = "/api/v1/book/:bookId/";
// id - your model instance's id or pk, that is represented in API resource objects.
var resource = $resource(apiResourceUrl, {bookId: '#id'}, {
all: {
method: 'GET', params: {}, // GET params that will included in request.
isArray: true, // Returned object for this action is an array (miltiple instances).
},
get: {
method: 'GET',
},
// [Define custom save method to use PUT instead of POST.][2]
save: {
/* But, the PUT request requires the all fields in object.
Missing fields may cause errors, or be filled in by default values.
It's like a Django form save.
*/
method: 'PUT',
},
// [Tastypie use POST for create new instances][3]
create: {
method: 'POST',
},
delete: {
method: 'DELETE',
},
// Some custom increment action. (/api/v1/books/1/?updateViews)
updateViews: {
method: 'GET',
params: {"updateViews": true},
isArray: false,
},
});
}]);
someModule.controller('bookCtrl', ['$scope', '$routeParams', 'bookResource',
function ($scope, $routeParams, bookResource) {
if ("bookId" in $routeParams) {
// Here is single instance (API's detail request)
var currentBook = bookResource.get({bookId: $routeParams.bookId}, function () {
// When request finished and `currentBook` has data.
// Update scope ($apply is important)
$scope.$apply(function(){
$scope.currentBook = currentBook;
});
// And you can change it in REST way.
currentBook.title = "New title";
currentBook.$save(); // Send PUT request to API that updates the instance
currentBook.$updateViews();
});
}
// Show all books collection on page.
var allBooks = bookResource.all(function () {
$scope.$apply(function(){
$scope.allBooks = allBooks;
});
});
// Create new
var newBook = new bookResource({
title: "AngularJS-Learning",
price: 0,
});
newBook.$save();
}]);
Angular's docs provide more information how to make usage of resource really incredibly.
Here is the problem with urls. As I remember, Angular will send request to /api/v1/books/1 (without slash in the end) and you'll get 404 from tastypie. Let me check this.
[2] http://django-tastypie.readthedocs.org/en/latest/interacting.html#updating-an-existing-resource-put
[3] http://django-tastypie.readthedocs.org/en/latest/interacting.html#creating-a-new-resource-post
I have this endpoints
/clients/:id
/bills/:id
/clients/:id/bills
I'm trying to create some resources with angular-resource to represent my API.
Clients and Bills Resources
I created a resource for the clients,
.factory('Clients', function($resource){
return $resource('/clients/:id')
})
.factory('Bills', function($resource){
return $resource('/bills/:id')
});
Those worked fine.
The Problem
My problem is when I wanted to define a resource to represent the bills of a client calling the endpoint /client/:id/bills
I thought that this should be a Bills resource with a method getFromClient() or something like that, as it will return an array of Bills from the client. But I have already use the Bills name. And the endpoint is different to the one already defined.
Any idea how to structure this?
I think what I was loooking for is now in Anguar 1.1
.factory('Bills', function($resource){
return $resource('/bills/:id',{}, {
get: {
method: 'GET',
params:{
clientId: '#clientId'
}
},
'getFromClient': {
method:'GET',
params: {
clientId: '#clientId'
},
url: host + "/clients/:clientId/bills",
isArray: true
}
})
});
Now you can add a url property to the method declaration to override the main url.
If you want to go with a library that enable you to solve this problema and many others, you could try https://github.com/platanus/angular-restmod
Here is an example:
.factory('Client', function(restmod){
return restmod.model('clients', {
bills: { hasMany: 'Bill' }
});
}
.factory('Bill', function(restmod){
return restmod.model('bills');
}
.controller('myController', function(Client){
$scope.client = Client.$find(1);
$scope.client.bills.$search();
});
Check the ngResource docs... Scroll down to the first example heading:
http://docs.angularjs.org/api/ngResource.$resource
It's kinda confusing because they're not doing it right in the example... lol
I believe it would look something like:
.factory('Clients', function($resource){
return $resource('/clients/:id', {id:'#id'})
})
.factory('ClientBills', function($resource){
return $resource('/clients/:clientId/bills/:id', {clientId:'#clientId', id:'#id'})
});
I haven't tested this, but I think that's right. =)
UPDATE
You would then access them like so:
.controller('MyCtrl', function ($scope, $routeParams, Clients, Bills) {
$scope.client = Clients.get({id:$routeParams.clientId})
$scope.bills = ClientBills.get({clientId:$routeParams.clientId})
})
I also changed the name of the service to "ClientBills", as it is more specific to the task, and you may want a "Bills" service that doesn't require a client id...