How do I set push-state in durandaljs 2.0 the works on refresh? - durandal

I'm using durandaljs 2.0. I've installed the durandal starter-kit as suggested and explained here. In the shell I'm returning router.activate({ pushState: true } ); as explained in the relevant documentation (see the bottom of the page).
Happily, the URL is indeed in a 'push state' format, e.g. http://localhost:61285/flickr - the problem is that when I refresh the page I get a 404 error telling me "the resource cannot be found". If I set push-state to false ({pushState: false} above) I get a hashed URL, e.g. http://localhost:61285/#flickr - and now a refresh does work. So, how do I set up a push state mode in durandaljs 2.0 that will work with refresh?
Thanks,
Elior

Maybe to late...but
just change the routes config.
simple as this :
routes.MapRoute(
name: "Default",
url: "{*url}",
defaults: new { controller = "Home", action = "Index" }
);

When you refresh the page, the browser will make a request to the server with that URL http://localhost:61285/flickr.
What's probably happening is that if you are using ASP.NET MVC, the server is trying to locate a Controller called flickr and it throws an exception because obviously there isn't any resource with that name.
In order to get rid of this exception you should configure the server to serve the same HTML of the APP but for unknown URL's. This can be achieved using IIS URL Rewrite in ASP.NET.
So after setting up properly the server, by requesting an unknown URL it would return the initial view for the app plus whatever you pass in the query string parameters so the router can do its job at client side.
In this blog post you will find more information about how to configure ASP.NET to handle this scenarios. In the article the author uses AngularJS, however it will be the same for Durandal.

RainerAtSpirit and margabit, you're both right, thank you. Here is how I implemented the server side:
First I should note that all the interaction with the server is done via WebApi controllers.
so, for example, if the URL is:
http://localhost:61285/home/category2/subCategory22 (for a localhost), the server tries to look for a controller called 'home' and an action in it called 'category2'. Since there's no such action, I get a 404 error.
What I wanted is that the server WILL call the 'home' controller, but send the rest of the URL as parameters to the client. My solution was to add a hash after the controller's name, so that the URL will look like this: http://localhost:61285/home/#/category2/subCategory22. If this would happen then the client will take care of the hashed part with no 404 error.
For this to happen:
I added the following to 'web.config':
<customErrors mode="On" defaultRedirect="Error">
<error statusCode="404" redirect="Error" />
</customErrors>
Then I create a controller named 'ErrorController' with the following class in it:
public class ErrorController : ApiController
{
[HttpGet, HttpPost, HttpPut, HttpDelete, HttpHead, HttpOptions, AcceptVerbs("PATCH"), AllowAnonymous]
public HttpResponseMessage Handle404()
{
string [] parts = Request.RequestUri.OriginalString.Split(new[] { '?' }, StringSplitOptions.RemoveEmptyEntries);
string parameters = parts[ 1 ].Replace("aspxerrorpath=","");
var response = Request.CreateResponse(HttpStatusCode.Redirect);
response.Headers.Location = new Uri(parts[0].Replace("Error","") + string.Format("#{0}", parameters));
return response;
}
}
what happens is that when the server get a URL with no relevant action as I mentioned above, it redirects it to this controller in the following format: http://localhost:61285/Error?aspxerrorpath=home/category2/subCategory22
as you can see, I manipulate this to add the hash and remove the unnecessary info: http://localhost:61285/home/#/category2/subCategory22 and the redirect the server to the 'home' controller.
You might wonder why I do all of this - the reason is that Durandal, a wonderful platform, enables me to use push state, but in order for that to happen I have to work-around the server getting a non-hashed URL and pass it to the client despite the fact there's no relevant controller/action; Durandal get's a hashed URL but removes the hash automatically and the user eventually sees a hash-less URL while Durandal provides all the necessary push state functionality.
Elior

Related

ASP.NET MVC Routing catch all

I'm using ASP.NET Core MVC and using attribute routing in my controllers.
In my Configure method in Startup.cs I currently call app.UseMvc() to start my MVC application.
Everything works as expected.
Now I'm trying to get a catchall going, but the 404 always gets the best of the situation.
I changed my app.UseMvc to the following:
app.UseMvc(routes =>
{
routes.MapRoute("Default",
"{*catchall}",
new { controller = "Index", action = "Index" },
new { catchall = #"^(.*)?$" });
});
but no dice.
I even tried to remove the catchall regular express as well, but I still get forwarded to my 404 page.
Any help is appreciated!
For anyone having the same problem, Tom Droste pushed me into the right direction
Adding a conventional route and using attribute routing has subtle side effects
The catch-all route was added first in the route dictionary and the attribute routes are added afterwards.
The AttributeRouting class just creates an instance of AttributeRoute which is an IRouter responsible for building the attribute routes. This is done on the first time AttributeRoute is invoked
Having that, the catchall was thus never called because it's not the last route.
putting everything back into conventional routing fixed the problem.
I suggest reading https://luisfsgoncalves.wordpress.com/2015/08/18/asp-net-5-routing-part-ii/ to understand how attribute routing works
Have you tried this?
routes.MapRoute(
"Default",
"{*.}",
new { controller = "Home", action = "Index" }
);
In your Startup.cs file, place this line of code just above app.UseMvc() in the Configure() Method. Obviously you can redirect to whatever page you want.
app.UseStatusCodePagesWithRedirects("~/Home/Index");
EDIT:
Per you comment, as far as I know, you'd have to write your own middleware to redirect and keep the original url/query parameters. If you're not familiar with developing middleware, I posted a demo project on GitHub that demonstrates how to develop middleware in three easy steps. You can download the project here.
So if I understand you correctly you just want a route that will be the one when the rest isn't matched. In my knowledge there are atm two ways to fix this. The correct one would be to write some routing middleware, however the dirty solution is to create a route with all nullable arguments that will be matched as last.
routes.MapRoute(
"DirtyRoutingSolution",
"{notUsed?}/{notUsedTwo?}/{notUsedThree?}/{notUsedFour?}",
new { controller = "Home", action = "Index"}
);

Hook up Auth0AccountController in Umbraco 7

Has anyone successfully integrated Auth0 with Umbraco 7 for member (front-end public users, not CMS back-end users) authentication?
I've successfully integrated with owin startup and dealing with signin-auth0 response. However I'm stuck on hooking up the Auth0AccountController to work with Umbraco (I'm getting a 404). Any suggestions?
I've tried adding ~/Auth0Account to the "umbracoReservedPaths" appSetting, but I just get a slightly different looking 404 (not controlled by Umbraco by looks of it).
I've also tried mapping the route in Startup.cs using
RouteTable.Routes.MapRoute(
"Auth0Account",
"Auth0Account/{action}",
new
{
controller = "Auth0Account"
}
);
but that results in a "No route in the route table matches the supplied values" error.
Any ideas?
Mapping the Auth0Account route in Startup.cs was correct:
RouteTable.Routes.MapRoute(
"Auth0Account",
"Auth0Account/{action}",
new
{
controller = "Auth0Account"
}
);
Turns out my problem was with the default redirect RedirectToLocal method in the Auth0AccountController controller. It was doing a
return RedirectToAction("Index", "Home");
which I didn't have a controller hooked up for. I replaced this with an Umbraco compatible redirect instead.
Also, instead of Auth0AccountController inheriting from Controller it might be useful to inherit from Umbraco.Web.Mvc.SurfaceController or Umbraco.Web.Mvc.RenderMvcController to expose useful Umbraco methods to the code.

Understanding ASP.net mvc4 code

public ActionResult Index()
{
return RedirectToAction("Index", "Login");
}
What does this code do? Index and login are views here?
This tells MVC to redirect to specified action instead of rendering HTML. In this case, browser receives the redirect notification and make a new request for the specified action. This acts like as Response.Redirect() in Asp.Net WebForm.
Moreover, RedirectToAction construct a redirect url to a specific action/controller in your application and use the route table to generate the correct URL.
For more about this look https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.redirecttoaction(v=vs.118).aspx

ASP.NET MVC intercepting route handling

So I am facing this little problem. In my route config I have configured two routes. One default route, which handles controller/action request and one custom route which is basically website.com/my_route where my_route is received by some controller's action as an argument. the problem is that whenever I want to give that action a parameter which contains a '/' in it, the route handler resolves request to controller/action route config, so if looks like this website.com/my_route/my_subroute, you get what happens. the my_routeController is searched and of course it does not exist, now the number of '/' in url can be more than one as you see and I want to do such thing. if the appropriate controller with appropriate action does not exist I want this request to be handled but that one controller which's action will recieve my_route/my_subroute as an argument. What is the best practice in this situation. If need I can provide the route config.
Try following way in your route.config
routes.MapRoute(
"my_route",
"my_subroute/x/y/z",
new { controller = "controllername", action = "actionname" }
);
Let me know if this fixed your issue. :)

Redirection to original URL having hash tag (#) broken in MVC4

I am developing an SPA application using AngularJS working with REST Web API, on top of a very small layer of ASP.NET MVC4. For reasons not important here, I am not using the default Account Controller of MVC4.
basically, I want to share "tasks" between users. My goal is to be able send the URL of a specific "task" entity to any user, via email. Clicking on that URL should launch the authentication. Following a successful authentication, I want to display the real task page info.
AngularJS causes my URLs to have # sign, or a URL of a page displaying the task "XYZ123" is:
http://hostname.com/#/tasks/XYZ123
ASP.NET redirects the unauthorized access to that URL to:
http://hostname.com/Home/Login?ReturnUrl=%2f#/tasks/XYZ123
This is OK, but the relevant controller method "cuts out" the path from #, so in:
public ActionResult Login(string returnUrl)
the value of 'returnUrl' will be just "/"
So, I am losing the path: I would like to build a "Connect with Facebook" link having the original URL, like:
http://hostname.com/Login/ExternalLogin?ReturnUrl=%2F#/tasks/XYZ123
but I cannot.
What is the right way to solve this issue?
I can think of creating my own redirection service URL without # tag, but this solution implies additional work, and covers only a case when the system is sending a message with task URL - humans will still try to copy/paste the location URL from the browser.
Thanks for any hint.
Max
Yes. A browser cuts '#/tasks/XYZ123' and requests page without that hash.
Although the hash itself apears on the logon page - it's the browser's work again.
Hash is not traveling to the server.
So when a browser loads the logon page with ?ReturnUrl=%2f#/tasks/XYZ123 we can rewrite Form action and encode the hash.
If the form looks like:
<form action="/Home/Login" method="post" >
...
</form>
The javascript code should look like:
<script src="~/js/jquery.js"></script>
<script type="text/javascript">
$(function() {
var search = $(location).attr('search') || '';
var hash = $(location).attr('hash') || '';
if (hash.length === 0) {
if (window.history.pushState) {
window.history.pushState('login', 'Login', '/Home/Login');
}
} else if (search === '?ReturnUrl=%2f') {
$('form').attr('action', '/Home/Login' + search + encodeURIComponent(hash) );
}
});
</script>
The part with window.history.pushState is required for the following:
If there is no hash, then for a SPA its URL (more likely) will be:
http://hostname.com/Home/Login?ReturnUrl=%2f
so here we try to replace URL (without page reload) with more accurate
http://hostname.com/Home/Login
You can use the properties of Request (like .Urlor .QueryString) to get the original url (and url parameters), instead of relying on the automatic binding of returnUrl parameter.
Replace # in the returnUrl with %23