CSS and image files not found after routing in ASP.NET MVC - asp.net-mvc-3

In my ASP.NET MVC (3) application, I have setup the following route in global.asax.cs:
routes.MapRoute(
"UniqueId",
"{uniqueId}",
new { controller = "Book", action = "DownloadBook" },
new { uniqueId = "[0-9a-zA-Z]{5}" }
);
The DownloadFile action method is:
public ActionResult DownloadBook(string uniqueId)
{
string path = Server.MapPath(String.Format("~/App_Data/Books/{0}/index.htm", uniqueId));
if (System.IO.File.Exists(path))
{
return File(path, "text/html");
}
return new EmptyResult();
}
The method correctly serves the index.htm file from the subdirectory in the /App_Data/Books directory with the name that corresponds to the uniqueId that is defined in the route. However, the CSS and image files in the index.htm file cannot be found, since the browser tries to find them in the original URL location (e.g. http://localhost/3Yru3/).
Is there anything I can do about this? I am probably overlooking something?
EDIT (also see my comments in the answers to my question):
The books are stored as HTML files (and not as MVC Views, which would make referring to the CSS and images less of a problem) because:
1. They will be uploaded as such by users.
2. I want to store the index.htm file and the resources it uses in an HTML5 appcache, to be available offline.
EDIT 2 I have found a solution to my own question and would like to know what you think of it. See my own answer in the answers below.

This must be happening because you are referencing your files in a wrong way (relatively), meaning that it will be served according to the root of your current page.
Ex:
<link rel="Stylesheet" href="/Styles/site.css" />
or
<link rel="Stylesheet" href="../../Styles/site.css" />
Instead of this way, use the following sytax to link you CSS files and your images
<link rel="Stylesheet" href="<%=Url.Content("~/Styles/site.css")%>" />
This should work fine and be evaluated correctly from any page regardless of it's location.

You can do something, but it's more of a hack. Put placeholders for the css link then, when serving the file, do a replace, something like this.
public ActionResult DownloadBook(string uniqueId)
{
string path = Server.MapPath(String.Format("~/App_Data/Books/{0}/index.htm", uniqueId));
if (System.IO.File.Exists(path))
{
var file=File.ReadAllText(path);
//this needs a bit more refining, it's just a proof of concept
//you can use Razor templating, there is a library for that
file=file.Replace("{CssHref}",UrlHelper.GenerateContentUrl("~/Content/site.css",HttpContext));
return Content(file);
}
return new EmptyResult();
}

I think I may have found a solution to my problem that is quite elegant, but I would like to hear from you what you think of it. I have been looking at 'classic' URL rewriting in trying to send the browser to the right location, when it attempts to GET CSS files and image files that are referred to by the index.htm file in the App_Data subfolder (that has become a long sentence, I hope it makes sense ;-).
First I have tried to use Context.RewritePath in the BeginRequest event handler of the global.asax.cs of the app. That gave some unexpected side effects. Then I created a custom route class that covers both the original route described in my question above and URL rewriting for the CSS and images files requests. The custom route uses the fact that during those requests the Request.UrlReferrer property contains the URL of the location of the index.htm file. Since this is becoming a very long story, I refer to a blog post that I have written on this subject for the technical details. I hope this information will save others some valuable time.

Related

If there is a real path under the public folder, same as the route path

I have some folders under public folder. For example
public/games/game1
public/games/game2
and this folder includes css, js, sprites etc.
And I have routes (in web.php)
Route::prefix('games')->group(function (){
Route::get('game1',[GameController::class, 'game1Index'])->name('game1');
Route::get('game2',[GameController::class, 'game2Index'])->name('game2');
});
Whenever I call these routes, it searches index.php under "public/games/game..n" folder without ever visiting gamecontroller. And it gives an error saying "page not found".
If i change the name of my route (games -> game-list). For example
Route::prefix('game-list')->group(function (){
Route::get('game1',[GameController::class, 'game1Index'])->name('game1');
Route::get('game2',[GameController::class, 'game2Index'])->name('game2');
});
It goes to the GameController.php and works correctly. but this time the external, relative links I gave for javascript and css files explode.
To solve this, I apply a solution to the links one by one (inside javascript path, and css path, adding to ../games/game1). This time it works completely, however, it bothers me that it is this way. for example:
s_oSpriteLibrary.addSprite("mask_slot","../games/game1/sprites/mask_slot.png");
Is there any other decent solution?
Your routes should not point to folders that already exist in your public path.
You cannot have a public path with the same name as your route because Laravel will not know whether "/games" is for the controller, or for the styles or js etc.
That is why the very first instance of your web.php was not working and the second one did. The games folder already exists in your public path.
Instead of doing that, you should put your css js sprites folders and files in an assets folder instead.

ASP.NET Core MVC : Razor pages routing, incorrectly re-using previous route data

I'm working on a dotnet 6 mvc application using RazorPages, and I'm having a problem with strange routing behavior.
I have a RazorPage /Pages/News.cshtml
This page is accessible using the default route /news
When called without any parameters this page will display an index of news articles.
I also want this page to be able to display a specific news article, via a path like this...
/news/1234-my-news-article
To achieve this, I've added a config like so...
builder.Services.AddRazorPages(options =>
{
options.Conventions.AddPageRoute("/News", "News/{id:regex(^\\d+\\-.*)?}");
});
In my templates, I can then use links like this...
<a asp-page="/News" asp-all-route-data="#(new Dictionary<string, string> { { "id", "1234-my-news-article" } })">My News Article</a>
or
<a asp-page="/News">All Articles</a>
However, once I've navigated to a specific article, the index link doesn't render correctly, and will instead link again to the same article. It appears to be re-using the current routing parameters.
Is there some way to avoid this?
update:
I've found a work-around, if I use this tag instead...
<a asp-page="/News" asp-route-id="">All Articles</a>
then it will link correctly to "/news".
It seems a bit counter-intuitive to have to explicitly set this to blank. I would have assumed it would default to unset, unless explicitly set otherwise.
This is known as ambient route values (https://www.learnrazorpages.com/razor-pages/tag-helpers/anchor-tag-helper#ambient-route-values), where the current route values are automatically added to outbound links which are generated by the anchor tag helper if the destination page is the same as the current page. In older versions of Razor Pages, this was the default behaviour for all pages.
As you have discovered, you can override this by setting the route value to an empty string, or as suggested elsewhere, to use a plain anchor tag for your link.
asp-page tag helper will add id value to the route by default.It is by design.If you don't want to add it,you can try to use href to replace asp-page:
All Articles

Single page application with Rails 4 and AngularJS

Ok, this idea might seem quite a bit crazy and it kindo' is (at least for me at my level).
I have a fairly standarad rails app (some content pages, a blog, a news block, some authentication). And I want to make it into a single page app.
What I want to accomplish is:
All the pages are fetched through AJAX like when using turbolinks, except that the AJAX returns only the view part (the yield part in the layout) withought the layout itself, which stays the same (less data in the responces, quicker render and load time).
The pages are mostly just static html with AngularJS markup so not much to process.
All the actual data is loaded separately through JSON and populated in the view.
Also the url and the page title get changed accordingly.
I've been thinking about this concept for quite a while and I just can't seem to come up with a solution. At this point I've got to some ideas on how this actualy might be done along with some problems I can't pass. Any ideas or solutions are greatly appreciated. Or might be I've just gone crazy and 3 small requests to load a page are worse then I big that needs all the rendering done on server side.
So, here's my idea and known problems.
When user first visits the app, the view template with angular markup is rendered regularly and the second request comes from the Angular Resource.
Then on ngClick on any link that adress is sent to ngInclude of the content wrapper.
How do I bind that onClick on any link and how can I exclude certain links from that bind (e.g. links to external authentication services)?
How do I tell the server not to render the layout if the request is comming from Angular? I though about adding a parameter to the request, but there might be a better idea.
When ngInclude gets the requested template, it fires the ngInit functions of the controllers (usually a single one) in that template and gets the data from the server as JSON (along with the proper page title).
Angular populates the template with the received data, sets the browser url to the url of the link and sets the page title to what it just got.
How do I change the page title and the page url? The title can be changed using jQuery, but is there a way through Angular itself?
Again, I keep thinking about some kind of animation to make this change more fancy.
Profit!
So. What do you guys think?
OK, in case enyone ever finds this idea worth thinking about.
The key can be solved as follows.
Server-side decision of whether to render the view or not.
Use a param in the ngInclude and set the layout: false in the controller if that param is present.
Have not found an easier way.
Client-side binding all links except those that have a particular class no-ajax
Here's a directive that does it.
App.directive('allClicks', function($parse) {
return {
restrict: 'A',
transclude: true,
replace: true,
link: function(scope, element, attrs) {
var $a = element.find('a').not($('a.no-ajax')),
fn = $parse(attrs['allLinks']);
$a.on('click', function(event) {
event.preventDefault();
scope.$apply(function() {
var $this = angular.element(event.target);
fn(scope, {
$event: event,
$href: $this.attr('href'),
$link: $this
});
});
});
}
};
})
And then use it on some wrapper div or body tag like <body ng-controller="WrapperCtrl" all-links="ajaxLink($href)"> and then in your content div do <div id="content" ng-include="current_page_template">
In your angular controller set the current_page template to the document.URL and implement that ajaxLink function.
$scope.ajaxLink = function(path) {
$scope.current_page_template = path+"?nolayout=true";
}
And then when you get your JSON with your data from the server don't forget to use history.pushState to set the url line and document.title = to setr the title.

How can integrate html template in codeigniter

I'm new to codeigniter. please tell me how can I integrate or install a html theme/template in codeigniter ? (my css folder path=news/css and application folder path=news/application where news is my main folder)
-thanks.
This is a very simple, very powerful way to do templates in codeigniter that is also very flexible.
http://news.dice.com/2013/02/18/how-to-build-a-to-do-app-with-codeigniter/
ignore the title, most of the lesson is about setting up templates in CI.
Note that i was first exposed to this method from a jeffrey way CI tutorial on net.tutsplus.com
All of them are worth checking out: http://net.tutsplus.com/sessions/codeigniter-from-scratch/
edit -- ok this is good enough addition to post. So in the tutorial, on the template.php page, you will see
$this->load->view($maincontent);
which is cool. but this is much better:
// load your header views
$templatefolder = 'beta/';
if(isset($content01))
$this->load->view($templatefolder.$content01);
if(isset($content02))
$this->load->view($templatefolder.$content02);
if(isset($content03))
$this->load->view($templatefolder.$content03);
// load your footer views
so instead of calling the view "maincontent", i've put in references to $content1, $content2, etc. Because we are doing if isset none of them are required. that way you can easily send more then one view file to the template. Or none at all if you are just showing a message, etc. Also notice that we have $templatefolder - that way you can easily reuse the template file for other site templates, even with the same content.
in your controller (similar to tutorial) it would be
$data['content01'] = 'codeigniterrawks';
$data['content02'] = 'mypetlion';
// beta template
$this->load->view( 'template_beta', $data );
note how easy it is if i want to bring in those same view files into a different template
$data['content01'] = 'codeigniterrawks';
$data['content02'] = 'mypetlion';
// alpha template
$this->load->view( 'template_alpha', $data );
I ran into this exact question about a week ago, this guide really helped me:
http://net.tutsplus.com/tutorials/php/an-introduction-to-views-templating-in-codeigniter/
To do the CSS url's, I added "uri" to my libraries in config/autoload.php (so it looks like this:
$autoload['libraries'] = array('uri', 'database');)
" type="text/css" media="screen" />
the base_url function automatically return whatever the base url of your site is, ie
http://localhost/news/
with the argument appended to the end.
The reason behind this is that if/when you ever need to migrate servers, you just change the base_url in the config file and it automatically updates across all of your templates and sources.
Try this,
I'm using this and it's very powerful.
https://github.com/philsturgeon/codeigniter-template

URL rewriting in MVC 3

I want to access a article with User friendly URL http://localhost/Content/Article/{id}/{title}, which internally process this url like http://localhost/Content/Article/{i}. I have created an Url routing rule for these url which is
routes.MapRoute(
"Article",
"{controller}/{action}/{id}/{title}",
new { controller = "content", action = "article", id = UrlParameter.Optional, title = UrlParameter.Optional }
);
However it is processing the request but, html respons is messing up. It is changing all image, anchor, css, javascript url. Replacing root url with /Content/Article and my html is being displayed messed up. All css formating gone. I have tried IIS7.0 rewriting module and Asp.nET routing, both giving me this issue.
Make sure that all references to external files are relative to the root of your website.
This isn't an issue with ASP.NET routing, it's an issue with how you're referencing your external files.
If you explicitly type the correct url into your browser do you get the correct page?
I have come across that problem a lot, especially when I have Areas. I have found that using T4MVC (NuGet Package) for all #Html.ActionLinks, etc, solves the problem. For example, using T4MVC, instead of:
<li>#Html.ActionLink("Home Page", "Index", "Home")</li>
...you can write:
<li>#Html.ActionLink("Home Page", MVC.Home.Index())</li>
Note the MVC.Home.Index(). This is how T4MVC allows you to do away with the magic strings (actionName and controllerName). In other words, T4MVC allows you to use the following overload of HtmlHelper.ActionLink:
HtmlHelper.ActionLink(string linkText, ActionResult result)
Instead of:
HtmlHelper.ActionLink(string linkText, string actionName, string controllerName)
For reasons that escape me right now (I have forgotten why!), this gets round the issue I think you are having.
Just check and see if typing the desired Url into the address bar works. If so, then it is the links that are going bad, not the routing.
If you still have a problem, then this isn't the answer...

Resources