Routing document-relative static urls in MVC3 - asp.net-mvc-3

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/

Related

How to handle AJAX calls after globalizating an MVC5 Website?

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).

.net core mvc routing and controllers

So I am brand new to .net. I am learning .net core right now. I am trying to figure out routing. I can not seem to get the routing to look in any folder except Home and Shared. I have looked all over the internet and tried many things. There seems to be something I am missing. Here is what I got
app.UseMvc(routes =>
{
routes.MapRoute(
name: "test",
template: "Register/test",
defaults: new { controller = "Register", action = "test"}
);
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
I have a Register folder with a test.cshtml file in just to try t figure this routing out. And this is is in my HomeController.cs file
public IActionResult test()
{
return View();
}
on my _Layout page I have this link
<li><a asp-area="" asp-controller="Register" asp-action="test">Test</a></li>
It works fine when I put it in the home folder, but I want to keep things separate. I know there is something I am missing. I've poured through all kinds on articles online including Stack Overflow, and I just don't understand what I am missing. From what I read its suppose to be like the Parent folder/File/ then and id that may be attached to that like a user name I have tried other formats for the routing with no luck, this was just my most recent attempt. I just can't help but think I need some bit of code somewhere else.
From your question it looks like you have the following code in your HomeController.
public IActionResult test()
{
return View();
}
That actually belongs in your RegisterController because the route template you defined is an explicit capture with defaults to the "Register" controller and the "test" action.
The view called "test.cshtml" - which should be named as such because of the default convention - should reside in your \Views\Register folder, next door to \Views\Home.
There are a couple of reasons why this may have worked in some fashion. First, the view is discoverable for any controller if it's in shared. Without knowing more about the requests you tried, it's difficult to determine if routing kicked in on the first route or second, but if that method was truly on HomeController requests to /home/test would have worked.
It looks to me like you're exploring routing. That is great - I 100% encourage the experimentation - so long as you know that routing isn't necessarily the lowest hanging fruit to learn. It's also something that you shouldn't have to touch 93.7% of the time. For example, the route you have defined about wouldn't be required for the controller and action you're adding with RegisterController and test.
Cheers.

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})

Node.js, restify and proper routing

I'm still wrapping my head around Node, but I have a very simple question. I see a lot of node examples where people are declaring their routes and all their logic in a single app.js file (or sometimes splitting them off into subfiles).
My question is basically: is it better to keep all your route declarations in the app or bootstrap a generic route that maps to your file structure. This may seem like a primitive question but my goal is to grasp what's most efficient within node.
I'm currently building an API handler with Restify but I have another app that uses Express (so this question will likely answer both questions).
In my route I can either declare a single route bootstrap like so:
app.all('/api/:package/:controller', function(request, response) {
var controller = require(
'../' + request.params.package + '/api/' + request.params.controller
);
controller.index(request, response);
response.end();
});
This basically accepts all calls from the API and targets the proper api controller. Alternatively I can declare each route individually or perhaps even write an loop that goes through each of my controllers and declares them on init. So:
for (var i in packages.controllers) {
app.all('api/' + package + '/' + controllers[i].name, function(request, response) {
var controller = require(
'../' + request.params.package + '/api/' + request.params.controller
);
controller.index(request, response);
}
}
packages.controllers being an array of all possible controllers. Note the above code is not accurate, I have an HMVC folder structure so the code is a bit more complicated than the above. But you get the point.
I'm wondering what the consequences of either are and if it really matters at all?
Thanks!
I would not recommend a single app.js at all. You will end up with a 5,000+ line file which is a nightmare to maintain.
The largest issue I see with your snippet is that even though require() gets cached, it has to perform a synchronous IO request. It's just a bad habit to get into.
Similar to what Don recommends, I have had the best luck splitting out routes into modules which export a single function which accept an instance of the app. You can think of it as "decorating" the app instance:
// app.js
var app = express.createServer();
app.configure(function(){ //... });
require('./foo')(app);
// foo.js
exports = module.exports = function(app){
app.get('/whatever', function(req, res){});
};
The exploding app.js file prompted a couple of us to create a small reference app to codify a standard Express app structure. It's not rocket science, but rather a set of conventions that makes things more organized.
You can find it here: https://github.com/EAAppFoundry/tableau
We'd love suggestions/pull requests if there something we got wrong or is missing.
I don't think there should be any real problem with looping through the directory tree and generating the routes. However, it'll be hard to define route based middleware and other routing features (such as variables in the routes) in a nice way.
I wrote a library that I use to define my routes declaratively and with minimal repetition which you might be interested in. It's inspired by Rails resourceful routing and is pretty flexible - the idea is to build a hash of routes and subroutes; there are also facilities to define groups of routes, middleware and variables.
https://github.com/cheesun/express-declarative-routing
Although it doesn't automatically generate the routes based on your directory structure, I think that'd be a cool feature and welcome you add it to the library.

T4MVC causing incorrect URL's

Using T4MVC on my new MVC Razor Project and I will have an action link like so
#Html.ActionLink(ViewRes.SharedStrings.HomeLink, MVC.Home.Views.Index, null, new { rel = "dropmenu7" })
so i would expect a url like
http://localhost:52122/Home/Index
but what i am getting is
http://localhost:52122/Home/~/Views/Home/Index.cshtml
looking into the t4mvc template file, i see where the "~/Views/Home/Index.cshtml" is coming from but I dont wanna touch it because it was made that way and i would guess i shouldnt have to change anything there.
Asking a friend, he says that i should use RouteLink instead of ActionLink because i am sometimes going to locations outside of the controller. However when i do that, i get: "A route named '~/Views/Home/Index.cshtml' could not be found in the route collection." when i try to run the app.
I guess i should also note that the links i am using are in the _Layout.cshtml
What am i doing wrong?
You need to change 'MVC.Home.Views.Index' to 'MVC.Home.Index()':
#Html.ActionLink(ViewRes.SharedStrings.HomeLink, MVC.Home.Index(), null, new { rel = "dropmenu7" })

Resources