Iam using to cache my asp.net mvc pages
[OutputCache(VaryByParam = "something", Duration = 1800, Location = System.Web.UI.OutputCacheLocation.Server)]
public ActionResult MyPage()
{
}
problem is cached page is not picked up when some one adds a trailing slash character("/")
eg:
//cache entry 1
mySite/MyPage
//cache entry2
mySite/MyPage/
To my knowledge it's not good to have one page accessible via many different URL's. So you could make you application to redirect when it encounters trailing slash.
Related
I have an Owin/NancyFx single-page application using AngularJs and UI Router.
Its hosted in IIS7 and for the most part everything is working. However there is one annoying issue with the root path that I can't seem to solve.
I would like a trailing slash on the root path, something like:
http://myserver.internaldomain.com/myapp/
This way when UI Router goes to handle the hashbang routing, all urls will look like:
http://myserver.internaldomain.com/myapp/#/mySpaRoute
However, I can't seem to get a trailing slash to append, so instead my URL looks like:
http://myserver.internaldomain.com/myapp#/mySpaRoute
I have tried to create an Owin middleware the looks at the URL and redirects if there's a missing / at the end. This works for all routes that are handled by the WebApi but not NancyFx. That seems reasonable since NancyFx takes over routing early to handle rendering its views.
Next I tried a NancyFx BeforeRequest pipeline lambda to do the same thing, interrogate the URL and append a / as needed. This however resulted in a redirect loop. The request would come in to the pipeline as: http://example.com/app, and then redirect to: http://example.com/app/, however at the next pipeline execution, the trailing / would be stripped and the pipeline handler would redirect again -- this is where the loop occured.
So I guess simply, how do I make NancyFx add a trailing / to the end of my routes?
Update:
Went to lunch, talked to the duck a bit, updated all the assemblies, then decided that its just the root get path that I really need to append the / to make hashbang routing look decent:
public class HomeModule : NancyModule
{
// note this works fine when running from localhost, but when running
// as an application in IIS, a redirect loop occurs
public HomeModule()
{
Get["/"] = _ =>
{
var requestUri = new Uri(Request.Url);
if (!requestUri.AbsoluteUri.EndsWith("/"))
{
var targetUri = requestUri.ToString() + "/";
return Response.AsRedirect(targetUri);
}
const string view = "views/home.cshtml";
var model = new { Title = Constants.ApplicationTitle };
return View[view, model];
}
}
}
Annnnnnd Redirect loop.
Ultimately this appears to have been caused by the Uri class. The Uri class does a very good job of removing trailing slashes in many cases. This means that I was, essentially, fixing any "malformed" urls by creating a new Uri out of them. Then I was breaking these nice Uri's by appending a / to them. On redirect the newly cast Uri would remove my extraneous /, then fail the if statement and the process would begin again, hence by redirect loop.
To fix the issue, I instead used the System.Web.HttpContextBase property provided in the owin environment context and checked the Request.Url property which seems to be the original requested Url with little or no post-processing.
These changes were made in my EnforceTrailingSlashMiddleware that I had written earlier. Here is the invoke method:
public override async Task Invoke(IOwinContext context)
{
var httpContext = context.Environment["System.Web.HttpContextBase"] as System.Web.HttpContextBase;
if (httpContext != null && httpContext.Request != null && httpContext.Request.Url != null)
{
var path = httpContext.Request.Url.ToString();
/*
formatter is a class ("SlashFormatter") with two methods:
"ShouldAppendSlash" which takes a path string and returns a boolean
(whether or not a slash should be appended)
"AppendSlash" which takes a string, safely appends a slash and
then returns the modified string.
*/
if (formatter.ShouldAppendSlash(path))
{
var url = formatter.AppendSlash(path);
context.Response.Redirect(url);
}
}
await Next.Invoke(context);
}
We are using Nancy framework for our application which is self-hosted in console application.
The problem appears when loading the URL without trailing slash.
Let's say we are hosting the page in
http://host.com:8081/module/
It then serves us am html page which has resources with a relative path like this:
content/scripts.js
Everything works well when you enter a url such as
// Generates a resource url 'http://host.com:8081/module/content/scripts.js'
// which is good
http://host.com:8081/module/
But when we leave out a trailing slash, the resource url is
// Generates a resource url 'http://host.com:8081/content/scripts.js'
// which is bad
http://host.com:8081/module
Is there any way to make redirection to trailing slash version? Or at least detect if trailing slash exists or not.
Thanks!
This feels a bit hacky but it works:
Get["/module/"] = o =>
{
if (!Context.Request.Url.Path.EndsWith("/"))
return Response.AsRedirect("/module/" + Context.Request.Url.Query, RedirectResponse.RedirectType.Permanent);
return View["module"];
};
The Request accessible from Context lets you see whether the path has the trailing slash or not and redirect to a 'slashed' version.
I wrapped this up as an extension method (which works for my very simple use case):
public static class NancyModuleExtensions
{
public static void NewGetRouteForceTrailingSlash(this NancyModule module, string routeName)
{
var routeUrl = string.Concat("/", routeName, "/");
module.Get[routeUrl] = o =>
{
if (!module.Context.Request.Url.Path.EndsWith("/"))
return module.Response.AsRedirect(routeUrl + module.Request.Url.Query, RedirectResponse.RedirectType.Permanent);
return module.View[routeName];
};
}
}
To use in a module:
// returns view "module" to client at "/module/" location
// for either "/module/" or "/module" requests
this.NewGetRouteForceTrailingSlash("module");
This is worth reading though before going with a solution such as this
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
I have 1 website on IIS ("myWebsite") and another inside this one ("secondWebsite") as an application. Both are ASP.NET Mvc websites.
I have a method who works perfectly on the first one :
public static string AbsolutePath(this UrlHelper url, string path)
{
Uri requestUrl = url.RequestContext.HttpContext.Request.Url;
string absoluteAction = string.Format("{0}{1}", requestUrl.GetLeftPart(UriPartial.Authority), path);
return absoluteAction;
}
The result is : http://myWebsite.com/path
I have the same method in the second Website, the result is the same, that's logic, but I don't want it !
The result should be : myWebsite.com/secondWebsite/path. (miss the http:// cause of spam prevention ^^).
Is there a good way to do that ?
Thanks.
You could try using
string absoluteAction = string.Concat(Request.Url.Authority,
Request.ApplicationPath, path);
Can you not use Server.ResolveUrl("~/Path"); as that rebases from application root.
I'm getting myself acquainted with ASP.Net-MVC, and I was trying to accomplish some common tasks I've accomplished in the past with webforms and other functionality. On of the most common tasks I need to do is create SEO-friendly urls, which in the past has meant doing some url rewriting to build the querystring into the directory path.
for example:
www.somesite.com/productid/1234/widget
rather than:
www.somesite.com?productid=1234&name=widget
What method do I use to accomplish this in ASP.Net-MVC?
I've search around, and all I've found is this, which either I'm not understanding properly, or doesn't really answer my question:
SEO URLs with ASP.NET MVC
MVC stands for "Model View Controller" and while those concepts aren't what you're asking about, you generally can wire up URL's like you see above quite easily
So for example by default the URL's look like the following
http://www.somesite.com/controller/view/
where controller refers to the controller class within your project, and view refers to the page/method combination within the controller. So for example you could write the view to take in an input and look something like the following
http://www.somesite.com/widget/productid/1234/
Now as for SEO Friendly URL's, that's just useless sugar. You author your controller such that it adds harmless cruft to the end of the URL.
So for example, you'll notice that the following three ways to get to this question produce the same result:
How do I create SEO-Friendly urls in ASP.Net-MVC
How do I create SEO-Friendly urls in ASP.Net-MVC
How do I create SEO-Friendly urls in ASP.Net-MVC
Stack Overflow has authored their route values such that the bit that occurs after the question ID isn't really necessary to have.
So why have it there? To increase Google PageRank. Google PageRank relies on many things, the sum total of which are secret, but one of the things people have noticed is that, all other things being equal, descriptive text URL's rank higher. So that's why Stack Overflow uses that text after the question number.
Create a new route in the Global.asax to handle this:
routes.MapRoute(
"productId", // Route name
"productId/{id}/{name}", // URL with parameters
new { controller = "Home", action = "productId", id = 1234, name = widget } // Parameter defaults
);
Asp.Net MVC has routing built in, so no need for the Url Rewriter.
Be careful when implementing routes with names in them, you need to validate that the name coming in is correct or you can end up harming your SEO-Ranking on the page by having multiple URIs share the same content, either set up a proper canonical or have your controller issue 301s when visiting the 'wrong' URI.
A quick writeup I did on the latter solution can be found at:
http://mynameiscoffey.com/2010/12/19/seo-friendly-urls-in-asp-net-mvc/
Some info on canonicals:
http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html
I think what you are after is MVC Routing
have a look at these
MVC Routing on www.asp.net
Book: ASP.NET MVC in Action - Routing
It is also important to handle legacy urls. I have a database of old and new urls and I redirect with the following code;
public class AspxCatchHandler : IHttpHandler, IRequiresSessionState
{
#region IHttpHandler Members
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
if (context.Request.Url.AbsolutePath.Contains("aspx") && !context.Request.Url.AbsolutePath.ToLower().Contains("default.aspx"))
{
string strurl = context.Request.Url.PathAndQuery.ToString();
string chrAction = "";
string chrDest = "";
try
{
DataTable dtRedirect = SqlFactory.Execute(
ConfigurationManager.ConnectionStrings["emptum"].ConnectionString,
"spGetRedirectAction",
new SqlParameter[] {
new SqlParameter("#chrURL", strurl)
},
true);
chrAction = dtRedirect.Rows[0]["chrAction"].ToString();
chrDest = dtRedirect.Rows[0]["chrDest"].ToString();
chrDest = context.Request.Url.Host.ToString() + "/" + chrDest;
chrDest = "http://" + chrDest;
if (string.IsNullOrEmpty(strurl))
context.Response.Redirect("~/");
}
catch
{
chrDest = "/";// context.Request.Url.Host.ToString();
}
context.Response.Clear();
context.Response.Status = "301 Moved Permanently";
context.Response.AddHeader("Location", chrDest);
context.Response.End();
}
else
{
string originalPath = context.Request.Path;
HttpContext.Current.RewritePath("/", false);
IHttpHandler httpHandler = new MvcHttpHandler();
httpHandler.ProcessRequest(HttpContext.Current);
HttpContext.Current.RewritePath(originalPath, false);
}
}
#endregion
}
hope this is useful
i think stackoverflow is best practice.
because when you remove a word at end of url, it redirect with 301 status to current url.
domain.com/product/{id}/{slug}
it had some benefits:
when you change slug of page it will redirect to correct url
your url is short but have seo friendly words at end.
in backend you use id to find product but you have slug for search engine
you can have same slug for different page. I now its not good for seo but you can have same slug for 2 products
the code will be like #Martin
routes.MapRoute(
name: "Cafes",
url: "cafe/{id}/{FriendlyUrl}",// how send array in metod get
defaults: new { controller = "cafe", action = "index", id = UrlParameter.Optional, FriendlyUrl = UrlParameter.Optional }//, id = UrlParameter.Optional
);
in action controller you should check database slug of product with the parameter that you receive. if they are not same redirect with status 301 to correct url.
the code for redirect:
return RedirectToActionPermanent("index", new
{
id = CafeParent.CafeID,
FriendlyUrl = CafeParent.CafeSlug.Trim()
});
RedirectToActionPermanent redirect with status 301. when you do this, the search engine find that this url is changed to what you redirected.