How to handle AJAX calls after globalizating an MVC5 Website? - ajax

Background
I've inherited an e-commerce website using the usual aspnet's MVC5 / razor / jquery. The goal is gobalizating the website and add a language selector, to support the two main country/language targeting strategies. I've just accomplished the first step, wich is routing the website so it handles domain.com/en/, domain.com/es/, domain.com/es-mx/... Using http://jittuu.com/2014/3/17/AspNet-localization-routing After this, entering the domain with a non-existant locale redirects to the default one. There's a custom IRouteHandler that get's the httprequest, checks the locale and puts it if it needs to:
Public Class LocalizationRedirectRouteHandler
Implements IRouteHandler
Public Function GetHttpHandler(requestContext As RequestContext) As IHttpHandler Implements IRouteHandler.GetHttpHandler
Dim routeValues = requestContext.RouteData.Values
Dim cookieLocale = requestContext.HttpContext.Request.Cookies("locale")
If cookieLocale IsNot Nothing Then
routeValues("culture") = cookieLocale.Value
Return New RedirectHandler(New UrlHelper(requestContext).RouteUrl(routeValues))
End If
Dim uiCulture = CultureInfo.CurrentUICulture
routeValues("culture") = uiCulture.Name
Return New RedirectHandler(New UrlHelper(requestContext).RouteUrl(routeValues))
End Function
End Class
There are thousands of "inline" AJAX calls to routes across the website written without an #Html helper because the project has separate, bundled js files, failing. There are hundreds of routes failing because the project controllers are using attribute routing and it's not specifying locale. For example, a simple link to a product features is failing because the controller has a route specifying
/product/{id}
but the URL looks like
/es-MX/product/{id}
Question:
What's the right way to proceed here?
I was expecting those AJAX and links without locale to be redirected, but they are not.
Should I rewrite all those files so they specify current locale? Is there any other "healthy" way to do this, like extending BaseController and add culture as a route prefix?
Thank you in advance for your time, I'm terribly lost here.

Turns out, this answer by #NightOwl888 is the closest I've found to a solution to the problem I was facing. After including this changes to my project, I have almost everything working out and sporting a wonderful culture prefix on the URL. There's still a few things to work out though:
My default route sports no culture in the URL, wich may be against one of our goals. I can work on that toying (or removing) the defaultCulture constraint.
There are hundreds of windows.location on js files targeting routes without culture. I may be able to work on that via JS global variable storing the culture.
I still need to find a nice way to handle URL localization. Hopefully this project will help me through (also directed there by #NightOwl888).

Related

Accessing dynamic links in the format of domain.com/<dynamic_page_name> in CodeIgniter

I am using code Igniter for my PHP project. I want to give provision in my site such that users can create new pages of their own, and access them directly from domain.com/their_page_name.
But, my developers have raised a concern that, 1000's of dynamic links that are presented in the format of domain.com/ is "not good for site's performance". For some 10-15 pages, it is fine. But, beyond that, it would effect the site's performance.
So, they proposed that the URL format should be like www.domain.com/something/page_name (here, 'something' is the controller name, as they mentioned it)
But, I really can't sacrifice my framework nor my requirement.
Is there any way that I can achieve the format of "www.domain.com/page_name" without effecting the site's performance?
Thanks in advance.
No issues on
Www.domain.com\userpagename.
It's not a framework issues. Codeigniter support this type of URL.you can create n no of URL.
Performance will matter how you are handling that particular controller or that particular function.
If may be 10 may be 100 ,work around same way.
You just have to put route accordingly.
$route[default_controller]=userurl;
$route[userurl/(:any)]=userurl yourfunction/$1`;
What it seems you need is dynamic controller, which can be done using Codeigniter's build in function _remap().
A code example is:
public function _remap($method){
if($method != null){
$this->yourFunction($method);
} else {
// handle the error as you like
}
}
public function yourFunction($key){
// your code logic here
}
All this code block goes inside your controller.
Edit: the performance is exactlu the same as going with domain.com/controller/method. What it matters, as stated above, is how you handle the data.

Routing Conventions in Can.js

So I’m looking to make some routes within my super cool can.js application. Aiming for something like this…
#!claims ClaimsController - lists claims
#!claims/:id ClaimController - views a single claim
#!claims/new ClaimController - creates a new claim
#!claims/:id/pdf - do nothing, the ClaimController will handle it
#!admin AdminController - loads my Administrative panel with menu
#!admin/users - do nothing, the AdminController will handle it
#!admin/settings - do nothing, the AdminController will handle it
So how might we do this?
“claims route”: function() { load('ClaimsController'); },
“claims/:id route”: function() { load('ClaimController'); },
“admin”: function() { load(‘AdminController’); },
Cool beans, we’re off. So what if someone sends a link to someone like...
http://myapp#!claims/1/pdf
Nothing happens! Ok, well let’s add the route.
“claims/:id/pdf route”: function() { load('ClaimController'); },
Great. Now that link works. Here, the router’s job is only to load the controller. The controller will recognize that the pdf action is wanted, and show the correct view.
So pretend I’ve loaded up a claim claims/:id and I edit one or two things. Then I click the Print Preview button to view the PDF and change my route to claims/:id/pdf.
What should happen… the Claim Controller is watching the route and shows the pdf view.
What actually happens… the router sees the change, matches the claims/:id/pdf route we added, and reloads the Claim Controller, displaying a fresh version of the claim pulled from the server/cache, losing my changes.
To try and define the problem, I need the router to identify when the route changes, what controller the route belongs to, and if the controller is already loaded, ignore it. But this is hard!
claims //
claims/:id // different controllers!
claims/:id //
claims/:id/pdf // same controller!
We could just bind on the "controller" change. So defining routes like can.route(':controller') and binding on :controller.
{can.route} controller
// or
can.route.bind('controller', function() {...})
But clicking on a claim (changing from ClaimsController to ClaimController) won't trigger, as the first token claim is the same in both cases.
Is there a convention I can lean on? Should I be specifying every single route in the app and checking if the controller is loaded? Are my preferred route urls just not working?
The following is how I setup routing in complex CanJS applications. You can see an example of this here.
First, do not use can.Control routes. It's an anti-pattern and will be removed in 3.0 for something like the ideas in this issue.
Instead you setup a routing app module that imports and sets up modules by convention similar to this which is used here.
I will explain how to setup a routing app module in a moment. But first, it's important to understand how can.route is different from how you are probably used to thinking of routing. Its difference makes it difficult to understand at first, but once you get it; you'll hopefully see how powerful and perfect it is for client-side routing.
Instead of thinking of urls, think of can.route's data. What is in can.route.attr(). For example, your URLs seem to have data like:
page - the primary area someone is dealing with
subpage - an optional secondary area within the page
id - the id of a type
For example, admin/users might want can.route.attr() to return:
{page: "admin", subpage: "users"}
And, claims/5 might translate into:
{page: "claims", id: "5"}
When I start building an application, I only use urls that look like #!page=admin&subpage=users and ignore the pretty routing until later. I build an application around state first and foremost.
Once I have the mental picture of the can.route.attr() data that encapsulates my application's state, I build a routing app module that listens to changes in can.route and sets up the right controls or components. Yours might look like:
can.route.bind("change", throttle(function(){
if( can.route.attr("page") == "admin" ) {
load("AdminController")
} else if(can.route.attr("page") === "claims" && can.route.attr("id") {
load("ClaimController")
} else if ( ... ) {
...
} else {
// by convention, load a controller for whatever page is
load(can.capitalize(can.route.attr("page")+"Controller")
}
}) );
Finally, after setting all of that up, I make my pretty routes map to my expected can.route.attr() values:
can.route(":page"); // for #!claims, #!admin
can.route("claims/new", {page: "claims", subpage: "new"});
can.route("claims/:id", {page: "claims"});
can.route("admin/:subpage",{page: "admin"});
By doing it this way, you keep your routes independent of rest of the application. Everything simply listens to changes in can.route's attributes. All your routing rules are maintained in one place.

Change url of angular-ui-router without reloading page

I'd like for changes in the URL to drive my application, and for changes in the application to change the URL, but not actually change state.
I have a route like this. The country/city example is a bit contrived, hopefully that doesn't confuse things. The relationship in the real application is somewhat hierarchical. Child views don't seem a fit though because there's no need for nested views.
$stateProvider.state( 'viewMap', {
url: '/viewMap/:country/:city',
templateUrl: 'pages/viewMap/viewMap.html',
controller: 'ViewMapController'
};
In ViewMapController, I can construct the page based on $stateParams.country and .city. As these values change, my application reacts and I want the url to stay in sync. I don't want to reload the whole page, however. I just want to update the url and push a history state on to the stack.
I understand I could manually construct a string:
updateUrl = function() {
window.location.hash = '#/viewMap/'+ $stateParams.country +'/'+ $stateParams.city
}
This feels fragile, as the way I build the string is separate from the way the framework parses it. I would prefer for the framework to build me a string based on the current params, but $state.href('.') describes the current route, which doesn't include $stateParams that haven't yet been activated/navigated to.
I've also looked at reloadOnSearch, but I think it only applies to query params.
Is there a better way to model this? It feels like I'm fighting the framework over something simple.
You can pass state params to $state.href function to get the complete URL
$state.href('.', $stateParams)
To generate arbitrary urls you can pass non-current params and/or configuration:
$state.href('.', {country:'usa',city:'sf'}, {absolute:true})

Routing document-relative static urls in MVC3

I'm integrating a JavaScript library into an ASP.NET MVC3 web app. The library assumes it will be installed next to the page that references it, and so it uses document-relative URLs to find its components.
For example, the default directory layout looks like
container-page.html
jslibrary/
library.js
images/
icon.png
extensions/
extension.js
extension-icon.png
However, I want to reference the library from the view in /Home/edit. I install the library in the default Scripts\jslibrary\ When I reference the library in the view in Views\Home\edit.cshtml, the library's document-relative links like
images/icon.png
end up as requests to
http://localhost/Home/images/icon.png
which results in a File Not Found (404) error. How do I construct a route to look for
{anyControllerName}/images/{anyRemainingPathInfo}
and serve up
http://localhost/Scripts/jslibrary/images/{anyRemainingPathInfo}
?
(full disclosure: I'm still on IIS 6 in Production, and not much chance of going to IIS7 any time soon, so if this is better done at the IIS level, please account for IIS6. Thanks!)
You could create a controller for handling you redirect logic - for example an "Images"controller. Register a global route in your Global.asax file, using the pattern (more on this type of pattern here:
routes.MapRoute(
"Images", // Route name
"{xyz}/{controller}/{path}", // URL with parameters
new {controller = "Images", action = "Index", path= UrlParameter.Optional} // Parameter defaults);
In your controller:
public ActionResult Index(string path)
{
//format path, parse request segments, or do other work needed to Id file to return...
return base.File(path, "image/jpeg"); //once you have the path pointing to the right place...
}
Not sure if this solution will work for you, wish I could come up with something more elegant. Best of Luck!
Short of rewriting the library and having it check for the appropriate directory the only solution I can think of is to include the views, library and supporting files in a directory structure that the library can access. This of course would break MVC's convention over configuration way of finding views, so you would have to write a custom override of the way Razor looks for views, which is not too complex to do, but you might be making life more difficult for yourself down the road depending on your application. Your call which is the lesser of the two evils :) (I'd go for fixing the library)
Make a help function
#functions{
public string AbsoluteUrl(string relativeContentPath)
{
Uri contextUri = HttpContext.Current.Request.Url;
var baseUri = string.Format("{0}://{1}{2}", contextUri.Scheme,
contextUri.Host, contextUri.Port == 80 ? string.Empty : ":" + contextUri.Port);
return string.Format("{0}{1}", baseUri, VirtualPathUtility.ToAbsolute(relativeContentPath));
}
}
Calling
#AbsoluteUrl("~/Images/myImage.jpg") <!-- gives the full path like: http://localhost:54334/Images/myImage.jpg -->
This example are from
https://dejanvasic.wordpress.com/2013/03/26/generating-full-content-url-in-mvc/

SEO-friendly URLs in CodeIgniter without the use of slugs?

Is there a way (via routing) in CodeIgniter to change:
example.com/category/4 to example.com/category/foo-bar
where Foo Bar is the name of category 4 in the database?
Access from the SEO-friendly URL should be allowed, but access via the integer should cause a 404 error.
This should also work dynamically, where any integer is automatically converted to a URL-safe version of its corresponding category name.
I've seen a few solutions that use 'slugs'... is there a decent alternative?
Thanks.
I've only been working with CodeIgniter for the past couple of months in my spare time, so I'm still learning, but I'll take a shot at this (be gentle):
There is a url_title() function in the URL Helper (which will need loaded, of course) that will change Foo Bar to foo-bar.
$name = 'Foo Bar';
$seo_name = url_title($name, TRUE);
// Produces: foo-bar
http://codeigniter.com/user_guide/helpers/url_helper.html
The URL helper strips illegal characters and throws in the hyphens by default (underscores, by parameter) and the TRUE parameter will lowercase everything.
To solve your problem, I suppose you could either do a foreach statement in your routes.php file or pass the url_title() value to the URL, rather than the ID, and modify your code to match the url_title() value with its category name in the DB.
Afaik the link between 4 and "foo-bar" has to be stored in the DB, so you'll have to run some queries. This is usually not done via routing in CI. Also routing just points a URL to a controller and function and has little to do with url rewriting.
Why don't you want to use slugs?
You could try storing the search engine friendly route in the database using this method or this one.
I wouldn't recommend throwing a 404. Use the canonical link tag in the instead if your worried about Google indexing both http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html.
But if you really want to I guess you could write a function that is called during the pre_controller hook http://codeigniter.com/user_guide/general/hooks.html that checks to see if the URL has an integer as the second segment then call the show_404() method. Perhaps a better solution when writing this function would be to redirect to the SEO friendly version.
Is there a way (via routing) in CodeIgniter to change:
example.com/category/4 to example.com/category/foo-bar
where Foo Bar is the name of category 4 in the database?
Yes.
Using CI 3,
http://www.codeigniter.com/user_guide/general/routing.html
Use Callbacks, PHP >= 5.3
$route['products/([a-zA-Z]+)/edit/(\d+)'] = function ($product_type, $id)
{
return 'catalog/product_edit/' . strtolower($product_type) . '/' . $id;
};
You can route to call a function to extract the name of the category.
Hope I answered your question and can help more people to like codeigniter as I believe it's speedy and light.
Slugs usage is important to make web application more secure which i think is important.
A better recommendation will be to use route to give you a better solution.
$route['(:any)/method/(:num)'] = 'Class/method';
or
$route['(:any)/method/(:num)'] = 'Class/method/$1';
$route['(:any)/gallery/(:num)'] = 'Class/gallery/$1';
base_url()/business-services/gallery/6
base_url()/travel/gallery/12
how to modify routes in codeigniter
Have fun :)

Resources