Dynamic URL route - c#

This app has several routes configured in RouteConfig.cs. For instance, I have the two following routes defined:
routes.MapRoute(
name: "MyPage-Demo",
url: "pages/page-title/demo",
defaults: new { controller = "Root", action = "PageDemo" }
);
routes.MapRoute(
name: "MyPage",
url: "pages/page-title/{resource}",
defaults: new { controller = "Root", action = "Page", resource = UrlParameter.Optional }
);
Each page someone visits has a link to a "demo". A page could be accessed by visiting http://localhost/pages/page-title. This works fine.
When a user clicks the "demo" link, they are redirected to a page located at http://localhost/pages/page-title/demo. This works fine.
My problem is the demo page may reference a complex nested structure. The structure consists of JavaScript, css, images, etc. Content used for the purpose of the demo. None of these nested resources can be found. However, I'm not sure how to setup my routing to account for these nested files.
I'm confident I'm going to need to update my controller's PageDemo action. However, I'm not sure
a) how to do so in a way that will allow for differing structures and
b) how to update my route configuration to account for these nested structures.
Is there a way to do this? In reality, I'm going to have multiple pages and multiple demos. For that reason, I want to have something a little more reusable than a hard-coded approach.

If you just need to serve files physically stored in a path, you should be able to just ignore the route, e.g.:
routes.IgnoreRoute("pages/page-title/demo/resources/{*resource}");
That will bypass MVC trying to route the request to a controller.
Or you could go by file extension:
routes.IgnoreRoute("{file}.js");
routes.IgnoreRoute("{file}.css");
(Code is untested, but it looks like you're trying to do something similar here :)
https://stackoverflow.com/a/3112192/486620

IF I understand:
The problem seems to be that your MyPage-Demo route:
routes.MapRoute(
name: "MyPage-Demo",
url: "pages/page-title/demo",
defaults: new { controller = "Root", action = "PageDemo" }
);
is NOT {resource} specific, while your MyPage route IS.
If you change your route to take a {resource}
routes.MapRoute(
name: "MyPage-Demo",
url: "pages/page-title/demo/{resource}",
defaults: new { controller = "Root",
action = "PageDemo", resource = UrlParameter.Optional });
Then your action method can
return specific Views with proper resource settings
set a Viewbag property with path to your specific resource
If this is inline with your intent, these routes can be consolidated into
routes.MapRoute(
name: "MyPage-Demo",
url: "pages/{action}/{resource}",
defaults: new { controller = "Root",
action = "PageDemo", resource = UrlParameter.Optional });
/pages/PageDemo/{resource} resolves to Controller=pages, action = PageDemo
/pages/demo/{resource} resolves to Controller=pages, action = demo.
This convention allows you flexibility to create more {resource} dependant links

In the Browser, Right Click Demo page => Choose View Page Source.
Here, you have the link for the CSS and Js files in your Demo page. Click on those js/css file links. Check if there are redirecting you to the correct/expected location. Otherwise you could make the Css/Js file URL accordingly Because, as per the demo page each PageDemo will have its own unique structure of JS/Images/css, etc

How are you referencing your JS and CSS files ?
If you use the tilde character like : ~/Content/Styles/Site.css you won't have any problem no matter where you are in your virtual path.

Also not 100% sure I am directly answering your question, but making the assumption that the resources you are trying to access are nested in a folder structure that mirrors the page structure - and the issue you are having is how to ignore the routes to these without having to know what they might be in advance?
This does a good job of explaining that: https://stackoverflow.com/a/30551/1803682
I would ask:
As #PKKG notes in his answer - do the links in the page source match what you expect?
How is this per-demo content served: e.g. by a service and not a static file?

this answer contains two approaches. the second one may be more suitable for your scenario. the first may be more suitable for a general mvc project
approach one
i suggest creating a organized structure in your content folder to store the scripts and css files, ie
/Content/Demos/Page-Title-1/
/Content/Demos/Page-Title-2/
/Content/Demos/Page-Title-3/
and
/Content/Demos/Common/
and then make a bundle to render the scripts and css files for each page title
ie.
bundles.Add(new StyleBundle("~/Demo/page-title/css").Include(
"~/Content/Demos/Page-Title-1/csscontent1.css",
"~/Content/Demos/Page-Title-1/csscontent2.css",
"~/Content/Demos/Page-Title-1/csscontent3.css",
"~/Content/Demos/Page-Title-1/csscontent4.css"));
bundles.Add(new StyleBundle("~/Demo/page-title/js").Include(
"~/Content/Demos/Page-Title-1/jscontent1.css",
"~/Content/Demos/Page-Title-1/jscontent2.css",
"~/Content/Demos/Page-Title-1/jscontent3.css",
"~/Content/Demos/Page-Title-1/jscontent4.css"));
this will allow you to render the scripts on the demo page using a few line approach, ie.
#Styles.Render("~/Demo/page-title/css");
#Scripts.Render("~/Demo/page-title/jss");
#Styles.Render("~/Demo/common/css");
#Scripts.Render("~/Demo/common/css");
you will have to update the files in global .asax as you change the files in your /Content/Demos/Page-Title/ folder.
there is the benefit that if you choose, you may bundle and minify the files to save bandwidth and load time for the first page load.
approach two.
(still use the following folder structure
/Content/Demos/Common/
and
/Content/Demos/Page-Title-1/
/Content/Demos/Page-Title-2/
/Content/Demos/Page-Title-3/)
make an html helper to reference all the scripts & contents in a folder
its usage would be
#Asset.RenderAssets( '~/folderdirectory')
and the helper would do something like
#helper RenderAssets (stirng directory){
#* scrape the directory for all script files*
var scripts = find all scripts in the directory
#* include the script files *#
for each script
<script src=" ... .js"></script>
#* scrape the directory for all cssfiles*
var styles = all css in the directory
#* include the css files *#
for each style
<link rel='stylesheet' type="text/css" href=" ... .css">
}
this would be a few line usage in each demo view
#Asset.RenderAssets( '~/Content/Demos/Common')
#Asset.RenderAssets( '~/Content/Demos/Page-Title')
you may or may not need to pair this with an extra few line or two in your global.asax or RouteConfig.cs file (see source 3)
routes.IgnoreRoute("/Content/Demos/{page}/{script}.js");
routes.IgnoreRoute("/Content/Demos/{page}/{style}.css");
relevant sources
to create html helpers see
http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx
to use bundling and minifcation (the scripts.render approach) see
http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification
phill haakk says may not need to pair this with an ignore route!
https://stackoverflow.com/a/30551/1778606
commentary and edits are encouraged.

All static content (.js, .css, .html, .png) is not seen by MVC (unless modules/runAllManagedModulesForAllRequests is set to true in web.config). Static content extensions are defined in IIS configuration "module mapping", and is using the StaticFileHandler module (and not the .NET module).
So static content must be referenced by its physical path relative to the current path (the path of the current html page).
The best solution is to use absolute link from the root of the website. Like /content/demo1/demo1.html, put all js,css in /content/demo1/, and in demo1.html use path relative to the /content/demo1/ folder (where the .html is). Ie: with demo1.css being in the same folder.
The link to demo1.html would be demo 1

Related

ActionLink and relative path

I can't find out how to solve this. I have two URLs. These are /my-url-1 and /my-url-2. Both going to different views.
The thing is that I have an ActionLink on /my-url-1's view which should make /my-url-2 and go to that view.
The problem is that ActionLink makes /my-url-1/my-url-2 as the URL and not just /my-url-2.
I was searching two days about how to fix it but couldn't find anything.
PD: I'm not using Areas so please don't tell me that I just should put the "area" parameter as a "".
These are two urls which goes to different controllers and different actions.
View which has the ActionLink (URL:/my-url-1) :
<div class="btn-index-container">
#Html.ActionLink("Url 2", "MyAction", "MyController")
</div>
This ActionLink should render:
Url 2
But it's rendering:
Url 2
where /my-url-1 is my current URL
Route Config
routes.MapRoute(
name: "route1",
url: "my-url-2", //without parameters
defaults: new { controller = "MyController", action = "MyAction" },
);
routes.MapRoute(
name: "route2",
url: "my-url-1", //without parameters too
defaults: new { controller = "MyController2", action = "MyAction2" }
);
So, when I go to localhost:port/my-url-1 it loads MyAction2 which renders a view. This view has inside an ActionLink(described above) which should render a /my-url-2 link.
Well, I've worked inside the MVC framework and I could told you about how Url.RouteUrl or Html.RouteLink works. At the end, the method which create the URL is GetVirtualPathForArea (this method is called before UrlUtil.GenerateClientUrl, which receive the VirtualPathData.cs created by GetVirtualPathForArea, as a parameter) from System.Web.Routing.RouteCollection.cs.
Here I left a link to the MVC source code:
https://github.com/aspnet/AspNetWebStack/blob/master/src/System.Web.Mvc
I found that, my Request.ApplicationPath was changing when I loaded /my-url-1. It was crazy because the application path was /.
At last, the problem was that the /my-url-1 was pointing to a virtual directory created on the IIS some time ago by error.
To know where your IIS configuration file is, please follow the link below:
remove virtual directory in IIS Express created in error
The solution was remove the .vs directory (which contains the config .vs\config\applicationhost.config) and rebuild
I think most of the Helpers that render URLs works more or less in the same way, so I hope it'll useful for all of you!
In your case, maybe no its necesary, just pass the parameters with null values, E.G.
#Html.ActionLink("Español", null, null, new { Lng = "es" }, null)
In this way, the parameters change, and the view is relative, depending on where you are.

MVC manipulate URL (routing), is it possible?

I have a website that use this pattern.
http://www.domain.com/product/...
My question is now, i need to create a subsite that going to be with this URL pattern, i have tried to change the routing without success.
http://www.domain.com/companyname/product/...
How can i inject the companyname in the URL without breaking my current routing?
Thanks
Niden
Three ways:
If it's relatively static, you can follow Andy's advice in the comments and publish the site in a virtual directory, companyname. Assuming you've properly used the UrlHelper extensions to generate URLs, instead of just hard-coding paths, then everything will just work.
You can create a "companyname" area. The default routing for an area is /area/controller/action. So that would get you the URL structure you want. However, areas are somewhat segregated, so you would need to copy controllers and views to the area's directory. Although, you could subclass controllers from the main app in the area to reuse code.
Just change the default route/add a new route:
routes.MapRoute(
"CompanyDefault",
"{company}/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
// default route here

asp.net MVC secure root folder only for authorized users

I am having this small extranet service where users can log in, get all sorts of info and download few files.
Is it possible to secure root folder in MVC asp.net project? I am having a project where users have to log in before using any material. How ever if I use for example "/material" folder for every pdf, jpg, etc. files, other unauthorized users can see those files also.
For example everybody can see this file if they type www.example.com/material/pdf-file.pdf So I want only authorized / logged users to see this file. Is this possible?
I managed to get it work. Here is how I did it.
The first I added this line to Web.config file:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
This allows dot chars in .pdf, .png, etc... in url's.
I added to RouteConfig.cs new routing for controller.
routes.MapRoute(
name: "Material",
url: "Material/Download/{file}",
defaults: new { controller = "Material", action = "Download", file = UrlParameter.Optional }
);
I created a new controller "Material".
// GET: Material
[Authorize]
public ActionResult Download(string file)
{
string path = Server.MapPath(String.Format("~/App_Data/Material/{0}", file));
if(System.IO.File.Exists(path))
{
string mime = MimeMapping.GetMimeMapping(path);
return File(path, mime);
}
return HttpNotFound();
}
And also transfered material folder inside app_data.
This seems to work nicely. Only authorized users can access to material folder.
It's possible to do that, but there are a lot ways to accomplish that.
A simplified scenario could be:
Disable directory listing on IIS
Create a custom "download wrapper" controller+action for the purpose of serving of those files.
Then wherever you create Action links, generate them using a HtmlHelper which would redirect the client to the "wrapper" controllers action. You can pass the filename in a parameter.
On the "wrapper" controller you could utize the [Authorize] attribute or better yet, without using such attributes everywhere you could use FluentSecurity for handling the authorization.
After you create the "wrapper" controller your URL for getting a file could look like:
www.example.com/download/file/pdf-file.pdf
This example URL assumes controller name is 'download' and action name is 'file'.

How to ignore all characters after "controller/action" in an ASP.NET MVC route?

I would like my ASP.NET MVC4 application to only serve the base HTML markup for a specific page, and after that I'm processing everything else on client-side with knockout.js/history.js/AJAX, including the initial page load.
So when someone refers to URL http://example.com/products/list/food/fruits, the MVC router should simply ignore everything what is behind "products/list" and route the request to ProductsController and List action. Then on client-side I will handle the rest and load the requested data accordingly.
I was playing with the route definitions, I tried to completely skip the "products/list" route, I also tried to add a "products/list/*" route, but didn't have success yet.
You can use an asterisk as part of the last variable in a route. For example, when configuring your routes:
routes.MapRoute(
"ProductRoute",
"products/list/{*otherArgs}",
new { controller = "Products", action = "List" });
You can learn more in MSDN's Documentation on routing under the section "Handling a Variable Number of Segments in a URL Pattern"
You will need to create your own route.
Something like this should do the trick:
routes.MapRoute("Products", "Products/{List}",
new {controller = "Products", action = "List"}
);
Note: I´m not sure if the other parameters are required in the route.

Dynamic Routing in asp.net webforms

Can we add dynamically routes to global.asax file
Suppose if I have multiple routes for the same page for example
http://website.com/about
http://website.com/en/about
http://website.com/en/about-us
While my actual URL for the page is like http://website.com/en/about-us.
My question now is: is there a way I can dynamically define these routes in global.asax file in such a way that it reads the URL entered by users like http://website.com/about and then compares it with database table and redirects it to the correct page which is http://website.com/en/about-us?
Taking into consideration following Table Structure:
Id URL_Name URL Actual_URL Page_Handler
1 Home http://website.com/ http://website.com/ Default.aspx
2 About Us http://website.com/about http://website.com/en/about-us About.aspx
3 About Us http://website.com/about-us http://website.com/en/about-us About.aspx
4 About Us http://website.com/en/about http://website.com/en/about-us About.aspx
5 Contact http://website.com/contact http://website.com/en/contact-us Contact.aspx
6 Contact http://website.com/en/contact http://website.com/en/contact-us Contact.aspx
Right now I have to configure each route manually in the global.asax:
if(HttpContext.Current.Request.Url.ToString().ToLower().Equals("http://website.com/about")
{
HttpContext.Current.Response.Status = "301 Moved Permanently";
HttpContext.Current.Response.Redirect("http://website.com/en/about-us");
}
if(HttpContext.Current.Request.Url.ToString().ToLower().Equals("http://website.com/en/about")
{
HttpContext.Current.Response.Status = "301 Moved Permanently";
HttpContext.Current.Response.Redirect("http://website.com/en/about-us");
}
A pointer to a good example or a solution is highly appreciated.
I find routes in global.asax are great for static resources especially if you want a nice, sexy extensionless URLs for SEO.
For dynamic pages/URLs though, I tend to have a catch all route that handles the request if it doesn't match any static routes.
eg
// ignore
routes.Add(new System.Web.Routing.Route("{resource}.axd/{*pathInfo}", new System.Web.Routing.StopRoutingHandler()));
// sexy static routes
routes.MapPageRoute("some-page", "some-sexy-url", "~/some/rubbish/path/page.aspx", true);
// catch all route
routes.MapPageRoute(
"All Pages",
"{*RequestedPage}",
"~/AssemblerPage.aspx",
true,
new System.Web.Routing.RouteValueDictionary { { "RequestedPage", "home" } }
);
So, when a request comes in it checks each static route in turn and executes the specified page. If no match is found, it drops through to the catch all and then AssemblerPage.aspx handles the request. This AssemblerPage will analyse the requested URL and redirect, rewrite path or stick some controls on the page to render - basically, it can do whatever you want it to do.
In your case, I'd have the AssemblerPage check the DB and compare the requested URL with the URLs in your table. Then simply redirect or rewrite path.
Would ASP.NET Routing help you here ?
Have a look at this: - ASP.net URL rewrite based off query string ID

Categories