Is there any method to detect invalid (or undefined) route and trigger 404 page in Backbone.Controller?
I've defined routes in my Controller like this, but it didn't work.
class MyController extends Backbone.Controller
routes:
"method_a": "methodA"
"method_b": "methodB"
"*undefined": "show404Error"
# when access to /#method_a
methodA: ->
console.log "this page exists"
# when access to /#method_b
methodB: ->
console.log "this also exists"
# when access to /#some_invalid_hash_fragment_for_malicious_attack
show404Error: ->
console.log "sorry, this page does not exist"
UPDATE:
I used constructor of Backbone.Controller to match current hash fragment and #routes.
class MyController extends Backbone.Controller
constructor: ->
super()
hash = window.location.hash.replace '#', ''
if hash
for k, v of #routes
if k is hash
return
#show404Error()
routes:
"method_a": "methodA"
"method_b": "methodB"
"*undefined": "show404Error"
# when access to /#method_a
methodA: ->
console.log "this page exists"
# when access to /#method_b
methodB: ->
console.log "this also exists"
# when access to /#some_invalid_hash_fragment_for_malicious_attack
show404Error: ->
console.log "sorry, this page does not exist"
The above works, but I'm not sure why you have to do what you do in the constructor. It may be slightly brittle, but we create a separate controller that we include in last. Its last so that the splat route is the last one to match:
NotFound = Backbone.Controller.extend({
routes: {
"*path" : "notFound"
},
notFound: function(path) {
var msg = "Unable to find path: " + path;
alert(msg);
}
});
new NotFound();
Using a more robust version of the above seems a cleaner approach to me.
Related
I'm trying to get user data to save to my Rails DB for user info through a sign up form on a React front end. I've got my route and controller written properly (at least I think) and the fetch request below but I keep getting a 500 internal error through on submit. Please let me know what I'm missing, any help would be greatly appreciated!
My route:
resources :users, only: [:show, :create]
My create action in UsersController:
def create
user = User.create!(user_params)
if user.valid?
session[:user_id] = user.id # remembering who our user is
render json: user, status: :ok
else
render json: {error: user.errors.messages}
end
end
and lastly my fetch request from the Signup.js component on the React frontend, where I'm getting the error on the line that has 'fetch'
fetch(`/users`,{
method:'POST',
headers:{'Content-Type': 'application/json'},
body:JSON.stringify(user)
})
.then(res => {
if(res.ok){
res.json().then(user => {
history.push(`/users/${user.id}`)
})
This might only be part of your problem, but first creating, then asking for validity is backwards.
Do something like this instead:
def create
user = User.new(user_params)
if user.save # <-- will return false if the save fails
user.reload
session[:user_id] = user.id # remembering who our user is
render json: user, status: :ok
else
render json: {error: user.errors.messages}
end
end
If you really want to check validity explicitly:
def create
user = User.new(user_params)
if user.valid? # <-- will check validity
user.save
user.reload
session[:user_id] = user.id # remembering who our user is
render json: user, status: :ok
else
render json: {error: user.errors.messages}
end
end
My guess is your error might be coming from the fact that your user variable doesn't actually have an ID yet. You need to save the record, then refresh it to get an ID.
I'm running ionic-angular framework working on an app that was started before me. I need to run a function in a service to get an API key from an external server before anything else. Because I want to check if a user has an API key and check if their stored GUID is valid by making another request to the server which I need the API key for. Because I'm going to check if they need to be routed to the login page or not. When I have a route guard checking if a user is logged in my API requests aren't completed or error out because I don't have an API key yet.
If I understood your problem you're asking this: you need to check "something" to decide if the user goes to the login page or goes to another screen, at the beginning of your app, when you open it. And that "something" consists in making http requests.
I will tell you how to do it, but in my experience if you need to make HTTP requests to decide to what screen redirect the user, you will get a blank page in the meanwhile. Depending on the connection 0.1s, 0.3s, 0.7s... But it's uggly. So the alternative would be to create a "Splash" screen with a loader circle, and use that page as the initial page. Then the page checks whatever and takes you to the next page.
Now, answering your question: you need a "CanActivate". CanActivate is guard (a code) that is executed before accessing a route, to check if you can access that page or redirect the user to another page. This is very useful for local checks (like checking the local storage) but as I said, http requests will cause a blank page for a small time. Follow these steps:
1 - First you need to create the CanActivate class. That's like creating a normal Service in ionic. Then make your service implement the CanActivate interface:
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { Observable } form 'rxjs'; // Install 'npm i rxjs' if you don't have it!
#Injectable({
providedIn: 'root'
})
export class LoginGuard implements CanActivate { }
Then this service needs to implement a function called canActivate:
export class LoginGuard implements CanActivate {
constructor(private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) : boolean|Observable<boolean> {
return new Observable<boolean>( observer => {
// HERE CHECK
if (!condition_to_avoid_login) {
// Complete the expected route, and enter the login
observer.next(true);
observer.complete();
}
else {
// Avoid login, go somewhere else:
observer.next(false);
this.router.navigate("/my-other-page");
observer.complete();
}
})
}
}
2 - You need to add this Guard to your route. In your routing file: app-routing.module.ts, add this guard to your page:
import { LoginGuard } from '...../login-guard.service';
const routes: Routes = [
...
{
path: 'login',
loadChildren: () => import('...../login.module').then( m => m.LoginPageModule),
canActivate: [LoginGuard]
}
...
]
Now everytime the user accesses this route (/login) the LoginGuard will trigger. There you decide if continue to the login page or redirect.
I created a new route from the react-starter-kit project and it does an async fetch of some data, then renders it, but a second later the page reloads with a message saying "Page not found - sorry but the page you're trying to view does not exist".
In the console I see - "Warning: Text content did not match. Server: "Balances" Client: "Page Not Found"
async function action() {
let bittrex = new ccxt.bittrex ({
'apiKey': '',
'secret': ''
})
try {
// fetch account balance from the exchange
let bittrexBalance = await bittrex.fetchBalance ()
/**** commenting above and uncommenting this block stops it from happening....
let bittrexBalance = {};
bittrexBalance.info= [];
let currency = {};
currency.Currency = "BTC";
currency.Value=999;
// output the result
bittrexBalance.info.push(currency);*/
console.log ('balance', bittrexBalance)
let balances = [];
balances.push(bittrexBalance)
return {
title: "Balances",
component: (
<Layout>
<Balances balances={balances} />
</Layout>
),
};
} catch (e) {
console.log('SOME sort of error', e);
}
Does anyone have any idea what this could be?
Edit to add, I realise now that if I disable Javascript everything works perfectly...
It seems to be running through the universal router twice. The first time
That's the only clue I've found so far... I don't understand why it's reloading the page once it has already loaded...
The Page not found error is coming from it going through :
catch (e) the second time... I suspect something is happening inside the ccxt library but that the problem is actually that it is called a second time because the page is somehow reloaded...
It seems you have to call await bittrex.loadProducts() before fetching your Balance.
Edit : Seems also that bittrex.loadProducts() has been renamed by bittrex.loadMarkets()
More info in this issue on github
Your server code reached exception, which turns into rejection of route, because action method returns undefined, so server will fall down through —
next routes will not fit and finally it reaches the not found route.
I'm automating Web UI testing for an angular 2 website. However, I got a problem when wanting to check if the login step is successful. It always pass even I put the wrong password or the wrong XPath for logout button.
This element just exists when we login successfully. As i mentioned before, i intentionally put wrong password and even wrong xpath for btt_Logout (xbutton not button) so this element cannot exist but it's still true.
I don't understand what the problem is even I try a lot of things. I just change toBe(true) to toBe(false) but it still passes :). It seems to be that this expect does not work. I don't understand why
import {browser, ExpectedConditions as EC, $, $$, element,by} from 'protractor'
import { Login } from '../page_objects/login.page';
import { helper } from '../helpers/helper'
declare let expect:any;
describe('Testing Login ', function () {
beforeEach(async () => {
})
it('Go to loginpage', async function () {
//Go to Login Page
Login.openHomePage();
});
it('Login to the page', async function () {
//Enter username, password and press OK. It work well.
Login.login("admin","admin#123");
//Waiting to see btt_Logout. Problems in here. It always true even i make XPath of btt_Logout wrong or make password wrong. I check/
browser.wait(EC.presenceOf(Login.btt_Logout), 30000).then(function () {
expect((Login.btt_Logout).isPresent()).toBe(true);
}, function (error) {
expect(true).toBe(false);
});
});
afterAll(()=> {
helper.cleanUp();
console.warn(`Test finished!`)
})
})
Login Page Object:
import {browser, ExpectedConditions as EC, $, $$, element, by, ElementFinder} from 'protractor'
import {BasePage} from './base.page'
require('../helpers/waitReady.js');
class LoginPage extends BasePage {
//Internal element
protected txt_LoginUsername: ElementFinder = element(by.id('username'));
protected txt_LoginPassword: ElementFinder = element(by.id('password'));
protected btt_LoginSubmit: ElementFinder = element(by.xpath("//button[contains(#class,'btn-submit')]"));
//External element
public login(Username, Password) {
this.txt_LoginUsername.sendKeys(Username);
this.txt_LoginPassword.sendKeys(Password);
this.btt_LoginSubmit.click();
}
}
export const Login = new LoginPage();
Base Page:
import {browser, ExpectedConditions as EC, $, $$, element, by, ElementFinder} from 'protractor'
require('../helpers/waitReady.js');
export abstract class BasePage {
protected url: string = '' // Will be same as baseUrl by default.
public btt_Logout: ElementFinder = element(by.xpath("//xbutton[contains(#class,'btn-logout')]"));
async openHomePage() {
return await browser.get(this.url)
}
async openUrl(webUrl) {
return await browser.get(webUrl)
}
}
Result:
Started
JASMINE STARTING:
» Testing Login
▻ Go to loginpage
. ✓ Go to loginpage (16 s)
▻ Login to the page
. ✓ Login to the page (0.234 s)
Test finished!
2 specs, 0 failures
Finished in 16.599 seconds
Summary *
Executed 2 out of 2 specs in 17 s
PASSED 2 ( 100% )
FAILED 0 ( 0% )
SKIPPED 0 ( 0% )
[22:38:58] I/launcher - 0 instance(s) of WebDriver still running
[22:38:58] I/launcher - chrome #01 passed
I believe it's because you're using isPresent() in expect((Login.btt_Logout).isPresent()).toBe(true); which checks if the element exists so it returns true.
Your expect isn't getting triggered, not to mention the wait clutters up your test. Also, presenceOf only checks the element is in the dom, not it's visibility.
Try throwing the wait in your login() method (which is probably the right place for it anyway), and making the logout button public. Protractor/jasmine will handle the wait for you in the expect.
import {browser, ExpectedConditions as EC, $, $$, element, by, ElementFinder} from 'protractor'
import {BasePage} from './base.page'
require('../helpers/waitReady.js');
class LoginPage extends BasePage {
//Internal element
protected txt_LoginUsername: ElementFinder = element(by.id('username'));
protected txt_LoginPassword: ElementFinder = element(by.id('password'));
protected btt_LoginSubmit: ElementFinder = element(by.xpath("//button[contains(#class,'btn-submit')]"));
public btt_Logout: ElementFinder = element(by.xpath("//xbutton[contains(#class,'btn-logout')]"));
//External element
public login(Username, Password) {
this.txt_LoginUsername.sendKeys(Username);
this.txt_LoginPassword.sendKeys(Password);
this.btt_LoginSubmit.click();
return browser.wait(EC.visibilityOf(this.btt_Logout), 30000, 'timeout: waiting for login');
}
}
export const Login = new LoginPage();
Then your test would be...
it('Login to the page', async function () {
Login.login("admin","admin#123");
expect(Login.btt_Logout.isDisplayed()).toBe(true);
});
I just found that my issue is that I put the element login_button in base_page and it makes a big issue. When protractor opens, it will be looking for this element and it causes we must wait for a long time. – Mike just now edit
Have you checked if that certain object is not present in the HTML/CSS?
There are cases where in if you are checking a TEXT,
the element might still be there and only displays EMPTY value,
that's why it does not look visible but isPresent and isDisplayed.
Just wondering why my router has no method navigate?
I have new App.Router; called properly. And in my view I try to call my index view like so:
addTrip: function(e){
e.preventDefault() ;
//Adding to collection
var newTrip = this.collection.create({
title: this.$('#new_trip #title').val(),
where: this.$('#new_trip #where').val(),
desc: this.$('#new_trip #desc').val(),
rating: this.$('#new_trip .active').text()
}, { wait: true }) ;
if(newTrip.validationError){
this.errorReport(newTrip.validationError) ;
}
this.clearForm() ;
//Navigate to home route
App.Router.navigate('', true) ;
}
I get the following error in Chrome Dev Tools:
Uncaught TypeError: Object function (){ return parent.apply(this, arguments); } has no method 'navigate'
I even tried to call navigate from the console and it doesnt seem to work either.
What am I doing wrong?
You should call navigate on your router object. It seems you are calling it on the class itself.
App.myRouter = new Backbone.Router() //or if you have your own base router class App.myRouter = new App.Router()
myRouter.navigate('', true);