I am using Durandal in an asp.net application which is all working well. What I would like to achieve is to put something into the routing of it so if required I can stop the current route and redirect.
The reason for this is I want to permission base some routes where the permissions are stored in a database. So during the routing I want to check the route, use web api accordingly to check if they have access to that route and redirect if so OR use a method on the viewmodel to check this and redirect accordingly. I do use the activate function on the viewmodel, I wondered if the route can be redirected here perhaps?
Has anyone done this before?
EDIT:
Following the great answer below the following is the code I eventually used on a test route to get this working. The web api function HasAccessToRoute part returns a bool:
define(['durandal/http', 'plugins/router', 'knockout', 'durandal/app'], function (http, router, ko, app) {
function viewModel() {
var self = this;
self.canActivate = function () {
return http.ajaxRequest("get", "/api/route/hasaccesstoroute?route=test")
.done(function (result) {
if (!result) {
app.showMessage("Test area cannot be accessed");
}
});
};
}
var model = new viewModel();
return model;
});
Yes, it is possible. Take a look at canActivate here. You can return a promise in your canActivate handler and fetch your authorization profiles asynchronously. Once you have the authorization profile, you can then resolve your canActivate with either true or false, accordingly. This is what we do.
Also, the routes in Durandal are client-side, not server-side. Or are you doing server-side rendering with, say, Razor? If not, then the only time you would be going out to the server, essentially, is to obtain data, usually through a RESTful Web API (although you can do this with action-based routes as well).
This is an important point since canActivate is a client-side handler.
Related
I want to access my current route (eg /[user_id]/posts) for both regular pages and APIs, so that I can log it in errors, increase page hit counters, etc.
The only automated way I found to retrieve the current route involves useRouter, but that is only accessible in React components.
I want to avoid hardcoding a route in each of my handlers as that can get out of sync easily.
How can I automate retrieving the current route inside a handler?
It's simply not supported by the framework. There are 2 alternatives I found:
1/ Use another framework such as Nest.js. With Nest, you can access router information in an interceptor. E.g. with the Fastify adapter:
#Injectable()
export class InstrumentationInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
return next.handle().pipe(
tap(() => {
const ctx = context.switchToHttp();
console.log(ctx.getRequest().routerPath);
}),
);
}
}
2/ Create your own controller infrastructure and attach metadata to each controller, e.g.:
export class UserController extends BaseController {
getRoute(): string {
return "/user/:userid";
}
}
Note that you will need to manually keep routes in sync with your file structure. The above can be coded via Decorators too.
3?/ Theoretically you can configure a Custom Server in Nest and use a router that supports retrieving route paths, with a hook for you to retrieve the route (e.g. attach it to the request object during a middleware). If you find yourself doing this though, take a look at 1/.
I'm writing a licence validation part for my application and want to redirect the user to a renewal page if and only if their licence has expired.
I am using FlowRouter and Blaze.
All my authenticated routes are in a group:
let authenticated = FlowRouter.group({
triggersEnter: [checkAuthenticated, checkSubscription]
});
I then check if the subscription is valid like so:
const checkSubscription = function(context){
let path = FlowRouter.current().path;
if (!Meteor.userId()){
return;
}
const sub = new Subscription();
if (sub.isInvalid() && path !=="/manage-practice/subscription"){
FlowRouter.go("/manage-practice/subscription");
}
};
My class subscription uses a collection that I can only load once a user has logged in. My problem is that the router usually triggers this redirection before this data has been loaded.
Is there a best practice approach to solve this?
Redirect with Triggers
I'm not sure about this being 'best practice' but one approach is to use the Flow Router redirect functionality on your login event.
You can see examples at: https://atmospherejs.com/kadira/flow-router#redirecting-with-triggers and https://github.com/meteor-useraccounts/flow-routing.
The initial login path (using Accounts.onLogin();) could be to a generic 'loading...' template while you evaluate the user's collection. On a callback you can then use the custom redirect function to either redirect to the requested page in your app, or redirect the user to your '/manage-practice/subscription' path.
FlowRouter.wait()
I have to confess I wasn't previously familiar with this second option, but I've just come across FlowRouter.wait(). This can be useful to delay the default routing process until some other evaluation is complete. I suspect this might only be relevant if a user logs directly into a page within your authenticated routing group.
Documentation: https://atmospherejs.com/kadira/flow-router#flowrouter-wait-and-flowrouter-initialize
I am making a dockerized services-based application. Some of the services will be written in meteor, some won't.
One of the services is a registration service, where users can register for the platform.
When doing microservices, normally I do the following:
var MyService = DDP.connect(service_url);
var MyOtherService = DDP.connect(other_service_url);
var RegistrationService = DDP.connect(registration_service_url);
What I want to do is use the loginWithFacebook method. The issue is that using Meteor.loginWithFacebook on the frontend will invoke its backend methods on the main frontend server.
However, I want to invoke its backend methods on the RegistrationService server (which has the relevant packages). The reason is because I am using the Accounts.onCreateUser hook to do extra stuff, and also because I want to keep the registration service separate from the frontend.
Just for clarity, even though it is not correct, imagine I have this:
'click #facebook-login': function() {
Meteor.loginWithFacebook(data, callback)
}
However, I want the loginWithFacebook method to use the server-side methods from RegistrationService when calling the client-side method .loginWithFacebook, so I actually want to do something to the effect of the following:
'click #facebook-login': function() {
RegistrationService.loginWithFacebook(data, callback)
}
Any help on this will be greatly appreciated. Thank you!
I believe you are looking for DDP.connect. Basically underneath meteor all calls to the server from the client and all communication from the server to the client use Distributed Data Protocol. (https://www.meteor.com/ddp) As the documentation points out by default a client opens a DDP connection to the server it is loaded from. However, in your case, you'd want to use DDP.connect to connect to other servers for various different tasks, such as a registration services server for RegistrationService. (http://docs.meteor.com/#/full/ddp_connect) As a simplified example you'll be looking to do something like this:
if (Meteor.isClient) {
var registrationServices = DDP.connect("http://your.registrationservices.com:3000");
Template.registerSomething.events({
'click #facebook-login': function(){
registrationServices.call('loginWithFacebook', data, function(error, results){ ... }); // registration services points to a different service from your default.
}
});
}
Don't forget that you can also have various DDP.connect's to your various microservices. These are akin to web service connections in other applications.
You can maybe achieve connection through your other service by specifying the service's remote connection to Accounts and Meteor.users:
var RegistrationService = DDP.connect(registration_service_url);
Accounts.connection = RegistrationService;
Meteor.users = new Meteor.Collection('users',{connection: RegistrationService});
Then would call Meteor.loginWithFacebook and it should use the other app's methods for logging in.
Using iron router I can currently pass query parameter data to the route by doing something like:
Router.go Router.current().path.split("?")[0] + '?searchTerm=apple'
Which appends a searchTerm to the routes current path. Then in my router file I can access the search term with: this.params.searchTerm
But what if I want to send this data to the route in the body of the request? If I do not want to affect the URL then sending data to the route over the body would be useful. Just like a post ajax request? How can I do that with Router.go or anything else iron router supports?
Basically I want to get data to my route, but I dont want to use session, or affect the url in any way. So my last option is to pass the data in the body, but how?
Meteor doesn't work with old school ajax requests.
If you really must accept ajax requests you could (ab)use server-side routes in iron-router:
this.route('serverRoute', {
where: 'server',
action: function() {
this.response.end("THIS IS A SERVER ROUTE..");
}
})
But the accepted meteor way for handling what you described, would be to use Meteor methods on the server side define methods:
Meteor.methods({
foo: function (arg1, arg2) {
doStuff(arg1, arg2);
});
Then on the client you call them like so:
Meteor.call('foo', 1, 2, function (error, result) { /* CallbackHandleingCode */ } );
This does not affect the url whatsoever, as internally meteor uses websockets for exchanging data between client and server.
I am trying to call an external REST service from angular using $http service.
The thing is that I am stuck on the $http.get method, because everytime I call the rest service i get an error with status = 0 and no information in the data parameter of the error callback.So far I've tried calling a local service runnig on port 5000 : $http.get('http://localhost:5000/ping') and this is supposed to return a json object with a property and a value. Another approach was calling http://api.flickr.com/services/rest/?method=flickr.test.echo&name=test in the hope of getting an answer. For both of them I get the same error: that I mentioned earlier.The call is made from an angular controller that has injected the http service.Thanks.
Have you tried:
$http({method: 'GET', url: 'someURL'}).
success(function(data, status, headers, config) {
//set view model or do something.
}).
error(function(data, status, headers, config) {
});
Make sure that you have passed the parameters correctly if there are any.
The general syntax should be like the following :
$http.get('../link/yourApplication/searchBySomeNumber?someNum='+$scope.someNum+'&asOfDate='+asOfDate+'&status=undefined')
.success(function(data, status, headers, config) {
//your code
console.log('Data return successful');
})
.error(function(data, status, headers, config) {
$scope.status = status;
alert('Info Error');
console.log('Group Info Error');
});
As $http returns a Promise, you can use the .then() method to log your results when the promise is resolved, or log an error in case anything goes wrong:
$http.get('http://localhost:5000/ping')
.then(function(returnedJson) {
console.log(returnedJson.data);
})
.catch(console.error) // or $log.error if you are using $log from Angular
Please note that the clean JSON response is obtained by logging the .data property of the returnedJson object. As it is a Promise, it contains other information that are not relevant to consume the web service.
Also note that the web service you want to consume should also be in the same domain as your Angular app, otherwise you may incur into a Cross Domain error, unless the service allows usage from external websites by exposing a Cross Domain Policy.
(Find more info here: Can someone post a well formed crossdomain.xml sample?)
If that's the case, this post should be helpful:
jQuery AJAX cross domain
Hope this helps.