If you load up the Durandal SPA template and try to navigate to a bogus URL (by adding something like "assdf" to the URL), the url is maintained and no error is provided. How can this be changed so that invalid URLs give the user an error page? I think an error is better than leaving the user wondering if their URL worked or not.
Have you looked at the handleInvalidRoute:
http://durandaljs.com/documentation/Router/
In Hot Towel for example, it is used to show the user an error toast.
EDIT: For example, I created a basic error view, then added this to main.js and it seems to work, though I think I prefer the growl type approach to showing the error to the user in Hot Towel.
app.start().then(function() {
//Replace 'viewmodels' in the moduleId with 'views' to locate the view.
//Look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
// ** ADDED **
router.handleInvalidRoute = function (route, params) {
router.navigateTo('#/error');
};
//configure routing
router.useConvention();
router.mapNav('welcome');
router.mapNav('flickr');
// ** ADDED **
router.mapRoute('error', 'viewmodels/error', 'error', false);
app.adaptToDevice();
//Show the app by setting the root view model for our application with a transition.
app.setRoot('viewmodels/shell', 'entrance');
});
EDIT2: IF you're worried about normal urls too (not hashed ones that will be handled by durandal\sammy) then you'll need to handle it outside of durandal. i.e. you can put something like the following in your global.asax:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
var httpException = exception as HttpException;
if(httpException != null) //It's an Http Exception, Let's handle it.
{
switch (httpException.GetHttpCode())
{
case 404:
// Page not found.
Response.Redirect("~/#/error");
break;
}
}
}
See here for a more complete approach: How can I properly handle 404 in ASP.NET MVC?
Related
I open a file download from a remote API on my webpage via window.open(). The API (a Flask server) has error handling and returns the error message if there's an internal server error, like this:
#app.errorhandler(502) //all other errors are handled the same way, including 500, etc.
#crossdomain(origin='*')
def bad_gateway_error(error):
return "Bad Gateway Error - Please make sure you're using a properly formatted file! Details: " + str(error), 200
I want to display this error on my site instead of redirecting to the error page. I'm trying to catch it via:
try {
window.open("https://API/receivedoc?timestamp="+timestamp,"_self")
} catch(e) {
filerootdiv.querySelector('.output').innerHTML = String(e);
}
This however does nothing (tested in Chrome). How could I catch the error when I'm using window.open? I guess it might be because in the error handling I return a 200 message so that the string I return actually gets returned instead of just crashing the server (this needs to stay this way as it's working just fine with all the other errors when I'm not trying to return a file). The issue is that I can't tell if the API request would return a file or a string before doing a window.open().
UPDATE
I've tried implementing:
let new_window = window.open("https://flaskmin.run.aws-usw02-pr.ice.predix.io/receivedoc?timestamp="+timestamp,"_self")
newWindow.onerror = function() {
filerootdiv.querySelector('.output').innerHTML = "Error!";
However this still only opens a new window with the error. I guess it's because of the error handling on the server side (I cannot change this). Can I somehow probe the content of new_window before redirecting to it, and just not open it if it's just a string containing the word 'error'?
I'm building a login page that, upon submitting and validation of the user credentials, opens up a native mobile application. Up till last week, I had this working cross mobile OS by using a custom scheme URI, something like:
function onLoginCallback() {
const redirectUri = 'customscheme:/action?param1=val1¶m2=val2';
window.location.replace(redirectUri);
}
The login page is displayed in an IABT, short for In App Browser Tab.
However, since the release of version 61 of Chrome, this is approach is broken on Android. Chrome blocks the redirect because there's no apparent user action related to the redirect (see here for more information on the matter).
As a consequence, when executing the code above, I'll end up with a warning in the console:
Navigation is blocked: customscheme:/action?param1=val1¶m2=val2
I've also tried updating the custom scheme url to an intent url but to no avail. Googling about this issue doesn't readily provide a clear solution, so I'm hoping anyone on can help me out.
Edit: Tried to reproduce the issue with the following scenario (as close as possible to the real life scenario):
IABT displays a page with a single button
Clicking the button fires an jsonp call to a mock endpoint
The JSONP callback is executed and fires off a custom event
An event handler for the custom event is triggered and redirects the browser to another mock endpoint
That mock endpoint responds with a 302 to the custom deeplink scheme.
Alas, this seems to be working. I would have expected that the inclusion of the jsonp call would cause Chrome to block the final redirect as it would not be able to identify it as a user initiated action.
Edit 2: Managed to get a reproducible scenario. We've set up a dummy endpoint, that upon request simply returns a 302 with the custom scheme in the Location header. This is blocked on all tries, except for the first one. That fact still boggles the mind. We're using the AppAuth for Android application to test the setup.
I'm opening a custom tab to the endpoint as shown below. The code is taken from this answer.
void launchTab(Context context, Uri uri){
final CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
#Override
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient client) {
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
final CustomTabsIntent intent = builder.build();
client.warmup(0L); // This prevents backgrounding after redirection
intent.launchUrl(context, uri);
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
CustomTabsClient.bindCustomTabsService(context, "com.android.chrome", connection);
}
We ended up implementing our login and registration forms with a classic post-redirect-get pattern.
The server responds with a 302 to the custom URI scheme. Because in this setup there's no asynchronous execution between the user submitting the form and the browser receiving a redirect, Chrome correctly identifies the chain of actions as trusted and thus will not block the navigation.
I realise this might not be the preferred solution for everyone. A possible alternative to support asynchronous execution flows is the use of universal links as these use regular http(s) schemes, to which redirects were (at the time of posting my question) not considered harmful by Chrome.
For those who use App Auth client and Identity Server:
Startup.cs
services.AddTransient<IAuthorizeResponseGenerator, AuthorizeRG>();
AuthorizeRG.cs
public class AuthorizeRG: AuthorizeResponseGenerator
{
public override async Task<AuthorizeResponse> CreateResponseAsync(ValidatedAuthorizeRequest request)
{
var response = await base.CreateResponseAsync(request);
if (response.RedirectUri != null && request.IsNativeClient())
//this fix chrome navigation blocked on native clients https://bugs.chromium.org/p/chromium/issues/detail?id=738724
response.Request.RedirectUri = $"/native/redirect/{HttpUtility.UrlEncode(response.RedirectUri)}";
return response;
}
}
NativeController.cs
[Route("[controller]")]
public class NativeController : Controller
{
[HttpGet("Redirect/{redirectUri}")]
public IActionResult Redirect([FromRoute] string redirectUri)
{
redirectUri = HttpUtility.UrlDecode(redirectUri);
redirectUri += HttpContext.Request.QueryString.ToUriComponent();
return this.LoadingPage("Redirect", redirectUri);
}
}
Extensions.cs
/// <summary>
/// Checks if the redirect URI is for a native client.
/// </summary>
/// <returns></returns>
public static bool IsNativeClient(this AuthorizationRequest context)
{
return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
&& !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
}
public static bool IsNativeClient(this ValidatedAuthorizeRequest context)
{
return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
&& !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
}
public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri)
{
controller.HttpContext.Response.StatusCode = 200;
controller.HttpContext.Response.Headers["Location"] = "";
return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri });
}
This works for me, but please comment if it broke smth in your authorization flow
I have a sign-in method in my Meteor application that redirects users to different routes after login in to the system. Here is my method:
Meteor.loginWithPassword(emailVar, passwordVar, function (err) {
if (err !== undefined) {
// error handling code
} else {
if (!Roles.userIsInRole(Meteor.userId(), 'active')) {
return Router.go('account-deactivated');
}
if (Roles.userIsInRole(Meteor.userId(), 'pharmacist')) {
return Router.go('pharmacist-dashboard');
}
if (Roles.userIsInRole(Meteor.userId(), 'admin')) {
return Router.go('admin-dashboard');
}
}
});
While this method works as expected, it produces some issues with my theme (AdminLTE) due to JavaScript loading problems (ex: app.min.js etc.). For example the sliding effects doesn't work on redirected page. But when I reload the page from the browser it starts to work as expected.
I know this is a separate issue that needs to be addressed. But if there is a way to completely reload a link in Meteor using iron-router it would be helpful. Specially when a page is transfered to a completely different user environment where a new set of JavaScript and CSS are used.
I went through the user documentations of iron-router but the fixes do not provide a solution.
Try using window.location.href to redirect.
Using Router.go is merely loading the template for the route you are linking to, whereas using window.location.href is loading the url as if it was a link you just clicked (actual refresh).
You'll need to use the actual url though, not the 'route name'.
window.location.href = "http://yourapp.com/route/here";
I am currently building a win-store app for a client, and I can't seem to figure out how to link to another app from within my current app.
I have seen plenty of documentation on how to open the default application from a URI or file type but nothing on opening a specific app.
Currently I am able to open my app with the function below, after adding a protocol to the declarations in my package.appxmanifest, but this isn't the preferred solution since this will not cause the app to open in full screen.
function openApp() {
var options = new Windows.System.LauncherOptions();
options.preferredApplicationPackageFamilyName = "packageFamilyName";
options.preferredApplicationDisplayName = "App Display Name";
var uri = new Windows.Foundation.Uri("linktomyapphere:");
Windows.System.Launcher.launchUriAsync(uri).then(
function (e) {},
function (err) {
console.log(err);
});
}
The best way is definitely URL activation (aka protocol activation). If you own the other app, you register a protocol for it - something like myapp://. That is done in the manifest. Then you follow the instructions on this page to activate that from your app. You can even pass URL parameters so you can pass some state in during activation.
I had implemented HttpHandler for Js extension for google api hosted jquery script files. because when it is called need to replace http with https. But visual studio started compiling javascript on pages being loaded. how do I suppress this behavior. And most interesting why did it happened.
My Http Handler :
public class HttpToHttpsHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
try
{
if (context.Request.RawUrl.Contains("http:"))
{
context.Response.ContentType = "text/plain";
string newUrl = context.Request.RawUrl.Replace("http", "https");
context.Server.Transfer(newUrl);
}
}
catch (Exception)
{
throw;
}
}
}
What possibly went wrong.
It is because you have used Server.Transfer() which doesn't issue redirect to the browser but changes the execution path on the server - in the result ASP.NET will try to create something out of your JavaScript.
You want to make a simple redirection so just use Response.Redirect()
context.Response.Redirect(newUrl, false);
Also I would like suggest a more safe approach for altering the URL (in case there would be port number in URL etc.):
if (!context.Request.IsSecureConnection)
{
UriBuilder secureUriBBuilder = new UriBuilder(context.Request.Url);
secureUriBBuilder.Scheme = Uri.UriSchemeHttps;
//Ude default port for schema - alter this if your server is using custom port for HTTPS
secureUriBBuilder.Port = -1;
context.Response.Redirect(secureUriBBuilder.Uri.ToString(), false);
}
Also remember that the entire page will remain in non-safe mode if you will load the HTML in HTTP and try to load only JavaScript through HTTPS - you should consider redirection in Global.asax or usage of URL Rewrite module.