.NET MVC DisplayModeProvider fallback - asp.net-mvc

I am currently using DisplayModeProvider to check if a mobile request is coming in and serving up a Page.mobile.cshtml file if I detect a mobile request otherwise I'm serving the default page Page.cshtml. This also works as a fall-back - if there is a mobile request for PageX but PageX.mobile.cshtml does not exist but there is a PageX.cshtml, I serve PageX.cshtml. This is working as intended.
I would like to add to the fall-back behavior as I include support for tablet requests. So when a tablet device request is detected, if I have a Page.tablet.cshtml, it will go ahead and serve that file. If there isn't a ...tablet.cshtml file, I'd like it to try to serve the Page.mobile.cshtml file and if a Page.mobile.cshtml does not exist, we would serve the Page.cshtml file.
Is there a way to do this without having to create a ...tablet.csthml file for every page and Html.Partial'ing a ...mobile.cshtml within it?

You can do that by changing the route preference dynamically. Define the hierarchy as you want like tablet first then mobile and then web pages.
Here is a sample how CustomViewEngine can do that:
public class MyViewEngine : RazorViewEngine
{
public MyViewEngine()
: base()
{
ViewLocationFormats = new[] {
"~/Views/tab/{1}/%1/{0}.cshtml",
"~/Views/mobile/{1}/%1/{0}.cshtml",
"~/Views/{1}/%1/{0}.cshtml",
"~/Views/Shared/{0}.cshtml"
};
PartialViewLocationFormats = new[] {
"~/Views/tab/%1/{1}/{0}.cshtml",
"~/Views/mobile/%1/{1}/{0}.cshtml",
"~/Views/%1/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml"
};
}
}
Here the view will be searched in the /Views/tab/ folder first then /Views/mobile/ followed by /Views/ and the /Views/Shared/ folders.
Detail of the implementation have been discussed here: ASP.NET MVC Custom View Routing

Related

MVC 5 how to work with custom URLs or Url Rewriting

I have a gallery website. That developed in asp.net. Now I want to port it to asp.net MVC5. While doing it I run into an issue without solving it I cant go into further development. So here is the requirements:
a) Every gallery has its own independent folder and has photos in it. the url for the photo galleries will be: localhost/gallery1, localhost/gallery2 and so on.
b) Each gallery has two types of layouts and options the commbo is given bellow:
localhost/gallery1/still/grid
localhost/gallery1/still/slide
localhost/gallery1/gifs/grid
localhost/gallery1/gifs/slide
c) change of [gallery1] to any other name should serve the photos from within the new name folder.
d) we also have a way to configure gallery for each folder by accessing admin
localhost/gallery1/admin
localhost/gallery2/admin
I am new to MVC. And I dont know how to work with that... I just created a brand new MVC5 project using Visual Studio with builtin example. Can anyone help me how to deal with this?
EDIT:
The controller has to be a universal control for anything immediately after localhost/ so in my example urls gallery1 and gallery2 or anything like that will be caught by a universal controller, may be GalleryController.
After the name of the gallery, the next two url segments are going to work in conjugation. I am not sure how to catch them in a universal controller and then segment them out on the basis of what is after still and gifs.
I have found answer to my own question. Here is what i did.
Inside RouteConfig.cs I simply added following mapping:-
routes.MapRoute(
name: "Dynamic",
url: "{name}/{action}/{layout}",
defaults:
new {
controller = "Gallery",
action = "Display" ,
layout = UrlParameter.Optional
});
This is how my GalleryController.cs looks like:
public class GalleryController : Controller
{
// GET: Gallery
public ActionResult Display()
{
return View("Index");
}
public ActionResult Admin()
{
return View();
}
public ActionResult Gifs()
{
if(Request.Url.AbsoluteUri.Contains("gifs/slide"))
return View("GifsSlide");
else
return View("GifsGrid");
}
public ActionResult Still()
{
if (Request.Url.AbsoluteUri.Contains("stil/slide"))
return View("StillSlide");
else
return View("StillGrid");
}
}
Inside my Gallery folder in Views I have following .cshtml structure
Views/Gallrey/gifsgrid.cshtml
Views/Gallrey/gifsslide.cshtml
Views/Gallrey/stillgrid.cshtml
Views/Gallrey/stillslide.cshtml
Views/Gallrey/admin.cshtml
Achievement:
localhost/gallery1/still/grid
localhost/gallery1/still/slide
localhost/gallery1/gifs/grid
localhost/gallery1/gifs/slide
localhost/gallery1/admin

Project subdirectory as root for static files

New to ASP.NET MVC, I am creating a web application using the Visual Studio 2013 wizard. It creates several folders from where static files are served: Content, Scripts, etc.
Frameworks in other languages (e.g. TurboGears) have an explicit directory only for static content, removing the risk of serving the source code of a page instead of processing it which is a typical configuration mistake of PHP sites.
ASP.NET however is happy to deliver anything in the application's root directory, e.g. http://localhost:1740/Project_Readme.html as long as it has the right extension. Only the Views folder is protected with a Web.config.
How do I configure the application to use another directory than the project's root directory for static files. E.g. if the file favicon.ico is put into the subdirectory Content, it should be accessible as http://localhost:1740/favicon.ico, but nothing outside of the Content directory unless returned by a controller.
Nothing should ever be executed in this directory, that is, if *.cshtml files are put into this directory, the files' contents (the source code) should be delivered as text/plain.
Final application will run using mod_mono on Linux.
Update:
Ben,
The proposed solution works only with Owin. To get it working in an MVC application you have to use asp.net MVC 6 (part of asp.net core or asp.net 5) only. But, with Web API you can use the older versions too. To setup the application please use the following steps:
Create an empty project using visual studio templates(don't select Web API or MVC)
Add the following Nuget packages to the project:
Microsoft.AspNet.WebApi
Microsoft.AspNet.WebApi.Owin
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.StaticFiles
Add a Startup.cs file and decorate the namespace with the following
[assembly: OwinStartup(typeof(Startup))]
Add the following code to the Stratup.cs class
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Optional}
);
//Configure the file/ static file serving middleware
var physicalFileSystem = new PhysicalFileSystem(#".\client");
var fileServerOptions = new FileServerOptions
{
EnableDefaultFiles = true,
RequestPath = PathString.Empty,
FileSystem = physicalFileSystem
};
fileServerOptions.DefaultFilesOptions.DefaultFileNames = new[] {"index.html"};
fileServerOptions.StaticFileOptions.ServeUnknownFileTypes = true;
fileServerOptions.StaticFileOptions.FileSystem = physicalFileSystem;
app.UseFileServer(fileServerOptions);
app.UseWebApi(config);
}
This should do the magic. Now you can host the application in IIS. IIS will serve the static assets only from client folder. Add Server folder and add controllers.
The Microsoft.Owin.Host.SystemWeb is what facilitates the hosting of Owin application in IIS. The file serve options help IIS to serve static files only from client folder.
Please let me know if you have any questions.
Based on your question, the project structure that you want to achieve should be like the following.
Basically you will have two folders only, Client and Server. Static files are served from client folder only. Server folder is not accessible. If this is what you need then it can be achieved easily with Owin Self Host with Static File Serving middleware.
Self host works with out any dependency on IIS. But, if your planning to host this application on Linux, you could use Asp.NET CORE 1.0. Later if you decide to host the application on IIS inside windows that can be achieved easily by adding the Microsot.Owin.Host.SystemWeb nuget package.
There are great blog posts on this topic. This is the link for one of them. Here is the link for achieving the same in Asp.NET Core.
I hope this solves your issues and please let me know if you have any questions.
Thank you,
Soma.
The best solution I found is to ignore asp.net normal way and write a new way
public override void Init()
{
BeginRequest -= OnBeginRequest;
BeginRequest += OnBeginRequest;
}
protected void OnBeginRequest(object sender, EventArgs e)
{
if (Request.Url.AbsolutePath.StartsWith("/endPoint"))
{
Context.RemapHandler(endPoint);
}
else
{
Context.RemapHandler(staticHandler);
}
}
Let endPoint and staticHandler implement IHttpHandler
it works but every static file moves through c# so there might be a solution with better performance

Allow IIS serving selected static files through ASP.NET pipeline

In my project we change one image depending on domain which was requested. Of course we can serve different files (css, js, html), but much more easy is to handle this one file and serve user correct one (let's call it logo.ico)
In web.config we set <modules runAllManagedModulesForAllRequests="false"> which causes that that IIS serve all static files for us.
Is there a way to add exception for `logo.ico' file?
To make example clear: when browser request http://mydomian.com/logo.ico I would like to run custom handler. For every other file I would like to use IIS native solution (which means <modules runAllManagedModulesForAllRequests="false">)
There is no way to add exception just for one file, but you could serve that image with ashx handler, for example url should look like (add your own logic to select image)
http://mydomian.com/ServeImage.ashx?image=logo.ico
And then just serve icon for that domain:
public class ServeImage : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/x-icon";
using (var fs = new FileStream(context.Server.MapPath("~/App_Data/logo.ico"), FileMode.Open))
{
fs.CopyTo(context.Response.OutputStream);
}
}
public bool IsReusable
{
get
{
return false;
}
}
}

ASP.net MVC SPA routing

I'm planning to build a SPA with asp.net MVC4 but I'm not quite sure how I have to Setup my Project because of the Routing. Most SPA's work with hashrouting like this mypage/#/News/today for instance.
What would happen if the browses directly to mypage/News/today if I haven't specified a Controller named News with an action today?
The App should handle both types of Routing, how can I achieve this?
Do I have to build my App in a classic way, like Adding several Controllers with appropriate Actions and views and also build a clientside MVC structure with knockout, jquery etc?
You'll have to let all routes to "pages" fall through to let your SPA handle them (including essentially fake 404s if it's not to a real page in your SPA), but at the same time, need to make sure that you get the correct responses for API calls and/or file requests.
Below is the setup I have (I am using Vue as the js framework but that doesn't matter much for this, and not at all for the server-side piece).
First, add this to your Startup.cs, in addition to your default route setup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.Use(async (context, next) =>
{
await next();
var path = context.Request.Path.Value;
// If there's no available file and the request doesn't contain an extension, we're probably trying to access a page
if (context.Response.StatusCode == (int)HttpStatusCode.NotFound && !Path.HasExtension(path) && !path.StartsWith("/api"))
{
context.Request.Path = "/Home/SpaRedirect"; // attempts to redirect to the URL within the SPA
context.Response.StatusCode = (int)HttpStatusCode.OK; // Make sure we update the status code, otherwise it returns 404
await next();
}
});
...
}
So the newly added SpaRedirect to HomeController looks like this, and just stores the requested URL in ViewData...
public IActionResult SpaRedirect()
{
ViewData["RequestUrl"] = HttpContext.Request.Path;
return View("Index");
}
Then in Index.cshtml, just capture that requested url in session storage so we have it available on the client-side:
<script src="~/dist/main.js" asp-append-version="true">
sessionStorage.setItem("redirectAttempt", #ViewData["RequestUrl"]);
</script>
Then in your boot script file (the entry-point for your SPA), add something like:
let redirectAttemptUrl = sessionStorage.getItem("redirectAttempt");
if (redirectAttemptUrl) {
router.push(redirectAttemptUrl);
sessionStorage.removeItem("redirectAttempt");
}
Which just checks for the presence of a requested url, and then the SPA's router attempts to navigate to it (in the example above it is a vue-router), then removes it from storage.
So this way, if a user attempts to navigate directly to a URL by entering it in the url bar (or via a bookmark) the app will load and take them to the right place, IF it exists... which takes us to the last piece...
Finally, you have to handle "404s" within your SPA, which is done by adding a catch-all route to your routes defs that takes user to a 404 component page you set up, which for Vue would look like this:
// adding an explicit 404 path as well for programmatically handling when something is not found within the app, i.e. return this.$router.push('/404')
{ path: '/404', component: NotFound, name: '404', alias: '*' }, // remove alias to not show the actual url that resulted in our little 404 here
{ path: '*', redirect: '/404' }, // this is the catch-all path to take us to our 404 page
Caveat: I'm no expert so could be missing something, would love additional comments on how to improve this. One thing that this doesn't handle is if the user is ALREADY in the SPA and for some reason edits the URL directly to navigate to someplace else, it would still trigger a server call and full reload, which ideally wouldn't be the case, but this is a pretty trivial issue I'd say.

best way to redirect .cfm locations to new locations .net mvc 3 IIS 7.5

I am trying to determine the best way to redirect a set of files I have from an old site (coldfusion) to new locations on my new site (asp.net mvc 3).
I want to redirect these pages with a 301 status to let engines know that this is a permanent change.
Currently I have in my web.config a custom errors section set up to redirect any 404 to the home page, which works great for all of the old links that are no longer in service, but it's sending a 302 status which I don't want and it's sending all my redirects to home thereby not giving me the SEO that was getting from my old links.
I thought about just adding .cfm as a module mapping in IIS to my .net Isapi and creating all of my pages as cfm with a redirect by adding headers, but then realized that'd be a LOT of work...
is there another "easier" solution to achieve this?
Yes, HttpHandler. in your web.congig you register it to handle all *.cfm URLs ( http://msdn.microsoft.com/en-us/library/46c5ddfy%28v=vs.71%29.aspx ), and implementation will be just your 301 redirect. 10 lines of code at most.
using System.Web;
public class CfmHandler : IHttpHandler {
public void ProcessRequest(HttpContext context) {
// redirect here, you have HttpContext ready
}
public bool IsReusable {
// To enable pooling, return true here.
// This keeps the handler in memory.
get { return true; }
}
}
More at : http://msdn.microsoft.com/en-us/library/5c67a8bd%28v=vs.71%29.aspx

Resources