first:
I did read and tried to implemented this and this and that, but I failed completely :(
my route is like:
routes.MapRoute(
"DefaultRoute",
"{calurl}/{controller}/{action}/{id}",
new { calurl = "none", controller = "Subscriber", action = "Index", id = UrlParameter.Optional }
);
and I'm trying to use as
"{calurl}.domain.com",
"{controller}/{action}/{id}"
so the routed value calurl would always come from the subdomain.
and I could have links like:
http://demo.domain.com/Subscriber/Register
as of today I have
http://domain.com/demo/Subscriber/Register
What I have tried
I tried to create my own CustomRoute using the example of the links above (all 3, one at a time), and I end up always screwing everything.
and I'm keep thinking that it's to much code just to change RouteValue["calurl"] to the subdomain.
What/How can I do this?
I am not sure if Routing extends to the actual domain name, as the domain or sub-domain should'nt have any effect on the working of a site/application.
I would suggest building your own sub-domain detection on each request. This keeps Routing and the Sub-domain detection separate and will help with testing etc..
This may help:
public static string GetSubDomain()
{
string subDomain = String.Empty;
if (HttpContext.Current.Request.Url.HostNameType == UriHostNameType.Dns)
{
subDomain = Regex.Replace(HttpContext.Current.Request.Url.Host, "((.*)(\\..*){2})|(.*)", "$2").Trim().ToLower();
}
if (subDomain == String.Empty)
{
subDomain = HttpContext.Current.Request.Headers["Host"].Split('.')[0];
}
return subDomain.Trim().ToLower();
}
If you have IIS7, why not use a URL Rewrite rule?
This would probably be better than doing a hack job with your routes, and IIS would do what it does best.
Probably something like this:
<rule name="rewriteSubdomains" stopProcessing="true">
<match url="(.*).domain.com/(.*)" />
<action type="Rewrite" url="domain.com/{R:1}/{R:2}" />
</rule>
This way, your route will handle the subdomain correctly, as it comes into the app differently.
Related
I have created a Web API 2 project and although the APIs work fine, I must enter a trailing slash for them to do so.
This results in a 404
http://www.myURL.com/api/v1/get/addressfromlatlong/UK/50.9742794/-0.1146699
This shows the JSON response as intended
http://www.myURL.com/api/v1/get/addressfromlatlong/UK/50.9742794/-0.1146699/
I have another controller with a custom action that works fine. The only difference is that this has one parameter that is an integer...
It seems to be something to do with the decimal type as if I make a slight variation in the URL and use a parameter, the API returns the results without issue:
This variation also shows the JSON response as intended
http://www.myURL.com/api/v1/get/addressfromlatlong/UK/50.9742794/?longitude=-0.1146699
It's not the end of the world but I also use Swagger to generate my API documentation and that automatically uses the first of the above URLs and includes built-in testing which, of course, fails. That's not so good for any developers who are referencing the API docs.
Can anyone explain why this may be happening and how I get it to work without the trailing slash?
Route Config
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Custom attributes and Controller Action
[Route("get/addressfromlatlong/UK/{latitude:decimal=0}/{longitude:decimal=0}")]
public AddressDetails GetAddressDetailsByLatLong(decimal latitude, decimal longitude)
{
AddressDetails addressDetails = repository.GetAddressDetailsByLatLong(latitude, longitude);
return addressDetails;
}
Use runAllManagedModulesForAllRequests. Without it IIS thinks it is a file request with extension as number part after decimal point. File is not found, 404.
You can use the below code in your web.cong file:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
</modules>
</system.webServer>
The problem
I'm trying to achieve the following:
Host a static website on http://www.somedomain.com.
Host an ASP.NET MVC app on https://secure.somedomain.com
(Note: This domain/subdomain is for illustrative purposes only.)
I've obtained an SSL/TLS certificate from my host, WinHost, and I'm applying it only to the subdomain (secure.somedomain.com), not the primary domain (somedomain.com).
When I publish the MVC app, the files are actually stored in the directory somedomain.com/secure.
Getting this all to work has proven much more difficult then I thought it would be.
What I've tried so far
I've found a number of questions about how to have different subdomains for different purposes (e.g., one subdomain per language, one subdomain per company, etc.), but my needs are much simpler--I just want my entire MVC app to be hosted from a single subdomain.
I've attempted two basic approaches so far (both independently and in combination):
Use IIS Manager to set up URL rewrites that basically translate requests for secure.somedomain.com -> somedomain.com/secure.
Write custom routes and/or constraints within my MVC app and add them to the RouteCollection in my Global.asax.cs file.
I've had some luck with Approach #1. I eventually managed to come up with a couple rules that would (1) not interfere with the website on the primary domain, (2) block any requests for somedomain/secure (because the security certificate is not in effect for the primary domain), and (3) serve up the correct page upon requests for secure.somedomain.com.
The major failure of this approach (at least in isolation) is that static content no longer gets served, and requests by unauthenticated users result in incorrect URLs, such as "https://secure.somedomain.dom/secure/somecontroller/someaction/?ReturnUrl=%2fsomecontroller" (note the extra "secure").
For the record, here are my URL rewrites:
<system.webServer>
<rewrite>
<rules>
<rule name="subdomain" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^(secure.)somedomain.com$" />
</conditions>
<action type="Rewrite" url="\secure\{R:0}" />
</rule>
<rule name="subfolder" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{URL}" matchType="Pattern" pattern="^(http://|https://)?(www)?[^/]*/secure" ignoreCase="true" negate="false" />
</conditions>
<action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." statusDescription="You do not have permission to view this directory or page using the credentials that you supplied." />
</rule>
</rules>
</rewrite>
</system.webServer>
As for Approach #2, I honestly can't say that anything I've tried has made much of a difference (whether in combination with the URL rewrites or alone).
First, I tried to write the following custom route, which I basically stole from https://stackoverflow.com/a/15287579/129164:
public class SubdomainRoute : Route
{
private const string _subdomain = "secure";
public SubdomainRoute(string url) : base(url, new MvcRouteHandler()) { }
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var routeData = base.GetRouteData(httpContext);
if (routeData == null) return null; // Only look at the subdomain if this route matches in the first place.
// A subdomain specified as a query parameter takes precedence over the hostname.
var subdomain = httpContext.Request.Params[_subdomain];
if (subdomain == null)
{
var host = httpContext.Request.Headers["Host"];
var index = host.IndexOf('.');
if (index >= 0) subdomain = host.Substring(0, index);
}
if (subdomain != null) routeData.Values[_subdomain] = subdomain;
return routeData;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
var subdomainParam = requestContext.HttpContext.Request.Params[_subdomain];
if (subdomainParam != null) values[_subdomain] = subdomainParam;
return base.GetVirtualPath(requestContext, values);
}
}
I attempted to hook it up like this in Global.asax.cs:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(
"Default",
new SubdomainRoute("{controller}/{action}/{id}")
{
Defaults = new RouteValueDictionary(
new { controller = MVC.Home.Name, action = MVC.Home.ActionNames.Index, id = UrlParameter.Optional }),
Constraints = new RouteValueDictionary(
new { controller = #"[^\.]*" })
});
}
(I basically replaced routes.MapRoute() with routes.Add().)
I also attempted to create a custom route constraint instead of a custom route, as suggested by https://stackoverflow.com/a/15234839/129164. My class looked like this:
public class SubdomainRouteConstraint : IRouteConstraint
{
private readonly string _subdomain;
public SubdomainRouteConstraint(string subdomain)
{
_subdomain = subdomain;
}
public bool Match(
HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection)
{
return httpContext.Request.Url != null && httpContext.Request.Url.Host.StartsWith(_subdomain);
}
}
And I updated my RegisterRoutes method to this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default" /* Route name */,
"{controller}/{action}/{id}" /* URL with parameters */,
new { controller = MVC.Home.Name, action = MVC.Home.ActionNames.Index, id = UrlParameter.Optional } /* Parameter defaults */,
new { controller = #"[^\.]*", subdomain = new SubdomainRouteConstraint("secure") } /* Parameter constraints */);
}
Again, I saw no difference when I tried this.
Some of the other answers I came across suggested writing extension methods to basically modify Html.ActionLink and Url.Content to fix the URLs as necessary, but I'm really resistant to this approach because it would involve a lot of refactoring to hook it up (and given my luck so far, I'm not confident it will work).
Anyway, if you are still with me, I could really use some advice on how to approach setting up my secure subdomain (hopefully) without having to make major sweeping changes to my MVC app.
Also, if you see any glaring mistakes in the solutions I've attempted so far, or you have some thoughts on specific modifications or combinations I should try, please let me know.
I may not fully understand your setup/requirements but if you have two separate domains for two entities with separate purposes, why don't you use two web sites rather than a single one.
Just set the host header in the bindings and have the root of the secure site pointing to the secure folder. If you need some static files from the static site, just bring them in via a virtual directory. No need for URL rewrites or routing.
If you need to store the secure folder under the static site but don't want users to see it there, just hide it:
<system.webServer>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="secure" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
A similar issue here- somedomain.com and abc.subdomain.com. (both in ASP.Net 4.0)
Static html files in somedomain.com
Deployed a simple aspx file into the abc.subdomain.com. - Works fine.
Deployed a simple mvc3 application into the abc.subdomain.com. - Not working. Showing the message Forbidden - you dont have permission to access / on this server. (error 403).
No changes made to global.asax and webconfig.
Deployment mode - publish to a local folder and ftp upload. (all dependencies added).
How can I have this simple Route:
http://domain.com/Calendar/Unsubscribe/my#email.com
I have a route that looks like:
routes.MapRoute(
"Unsubscribe",
"Calendar/Unsubscribe/{subscriber}",
new {
controller = "Calendar",
action = "Unsubscribe",
subscriber = "" }
);
and my action is:
public ActionResult Unsubscribe(string subscriber)
{
...
}
Without any parameters, like http://domain.com/Calendar/Unsubscribe/ works fine, but soon I add the email, I get a 404 page :(
Is there any trick I have to do?
Thank you
Try adding a trailing slash to the url http://domain.com/Calendar/Unsubscribe/my#email.com/ without changing the routing rules.
If you still want to avoid adding the trailing slash and you have URL Rewrite available, you could add a rewrite rule into the web.config
<system.webServer>
<rewrite>
<rules>
<rule name="Fix Unsubscribe emails route" stopProcessing="true">
<match url="^(Calendar/Unsubscribe/.*#.*)$" />
<action type="Rewrite" url="{R:1}/" />
</rule>
</rules>
</rewrite>
</system.webServer>
You could also write a better regular expression than the one I provided for the sake of readability.
You could also try to reorder your route params like /Calendar/my#email.com/Unsubscribe so that the e-mail is not the last param.
I think this should do it.
routes.MapRoute(
"Unsubscribe",
"Calendar/Unsubscribe/{subscriber}",
new {
controller = "Calendar",
action = "Unsubscribe"
}
new { subscriber = #"\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*" }
);
I tried it in default home controllor and working with no error
_http://localhost:64718/home/index/a.b#email.com
Welcome to ASP.NET MVC! a.b#email.com
public ActionResult Index(string id)
{
ViewModel.Message = "Welcome to ASP.NET MVC! " + id;
return View();
}
No changes in defaultroute-MVC.
do you have any other routes defined before Unsubscribe which will match same route
Try removing the default empty string for subscriber.
The # symbol is a reserved character in URLs:
http://en.wikipedia.org/wiki/Percent-encoding
Try encoding it; Thusly, your new url would be:
http://domain.com/Calendar/Unsubscribe/my%40email.com
you don't need to add new rote! I tried it in Asp.Net Mvc4 application without any new routes and it worked. you can check email in Unsubscribe Method. I think it's better to do that.
good luck!
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.
I'd like to have dashes separate words in my URLs. So instead of:
/MyController/MyAction
I'd like:
/My-Controller/My-Action
Is this possible?
You can use the ActionName attribute like so:
[ActionName("My-Action")]
public ActionResult MyAction() {
return View();
}
Note that you will then need to call your View file "My-Action.cshtml" (or appropriate extension). You will also need to reference "my-action" in any Html.ActionLink methods.
There isn't such a simple solution for controllers.
Edit: Update for MVC5
Enable the routes globally:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapMvcAttributeRoutes();
// routes.MapRoute...
}
Now with MVC5, Attribute Routing has been absorbed into the project. You can now use:
[Route("My-Action")]
On Action Methods.
For controllers, you can apply a RoutePrefix attribute which will be applied to all action methods in that controller:
[RoutePrefix("my-controller")]
One of the benefits of using RoutePrefix is URL parameters will also be passed down to any action methods.
[RoutePrefix("clients/{clientId:int}")]
public class ClientsController : Controller .....
Snip..
[Route("edit-client")]
public ActionResult Edit(int clientId) // will match /clients/123/edit-client
You could create a custom route handler as shown in this blog:
http://blog.didsburydesign.com/2010/02/how-to-allow-hyphens-in-urls-using-asp-net-mvc-2/
public class HyphenatedRouteHandler : MvcRouteHandler{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.RouteData.Values["controller"] = requestContext.RouteData.Values["controller"].ToString().Replace("-", "_");
requestContext.RouteData.Values["action"] = requestContext.RouteData.Values["action"].ToString().Replace("-", "_");
return base.GetHttpHandler(requestContext);
}
}
...and the new route:
routes.Add(
new Route("{controller}/{action}/{id}",
new RouteValueDictionary(
new { controller = "Default", action = "Index", id = "" }),
new HyphenatedRouteHandler())
);
A very similar question was asked here: ASP.net MVC support for URL's with hyphens
I've developed an open source NuGet library for this problem which implicitly converts EveryMvc/Url to every-mvc/url.
Uppercase urls are problematic because cookie paths are case-sensitive, most of the internet is actually case-sensitive while Microsoft technologies treats urls as case-insensitive. (More on my blog post)
NuGet Package: https://www.nuget.org/packages/LowercaseDashedRoute/
To install it, simply open the NuGet window in the Visual Studio by right clicking the Project and selecting NuGet Package Manager, and on the "Online" tab type "Lowercase Dashed Route", and it should pop up.
Alternatively, you can run this code in the Package Manager Console:
Install-Package LowercaseDashedRoute
After that you should open App_Start/RouteConfig.cs and comment out existing route.MapRoute(...) call and add this instead:
routes.Add(new LowercaseDashedRoute("{controller}/{action}/{id}",
new RouteValueDictionary(
new { controller = "Home", action = "Index", id = UrlParameter.Optional }),
new DashedRouteHandler()
)
);
That's it. All the urls are lowercase, dashed, and converted implicitly without you doing anything more.
Open Source Project Url: https://github.com/AtaS/lowercase-dashed-route
Here's what I did using areas in ASP.NET MVC 5 and it worked liked a charm. I didn't have to rename my views, either.
In RouteConfig.cs, do this:
public static void RegisterRoutes(RouteCollection routes)
{
// add these to enable attribute routing and lowercase urls, if desired
routes.MapMvcAttributeRoutes();
routes.LowercaseUrls = true;
// routes.MapRoute...
}
In your controller, add this before your class definition:
[RouteArea("SampleArea", AreaPrefix = "sample-area")]
[Route("{action}")]
public class SampleAreaController: Controller
{
// ...
[Route("my-action")]
public ViewResult MyAction()
{
// do something useful
}
}
The URL that shows up in the browser if testing on local machine is: localhost/sample-area/my-action. You don't need to rename your view files or anything. I was quite happy with the end result.
After routing attributes are enabled you can delete any area registration files you have such as SampleAreaRegistration.cs.
This article helped me come to this conclusion. I hope it is useful to you.
Asp.Net MVC 5 will support attribute routing, allowing more explicit control over route names. Sample usage will look like:
[RoutePrefix("dogs-and-cats")]
public class DogsAndCatsController : Controller
{
[HttpGet("living-together")]
public ViewResult LivingTogether() { ... }
[HttpPost("mass-hysteria")]
public ViewResult MassHysteria() { }
}
To get this behavior for projects using Asp.Net MVC prior to v5, similar functionality can be found with the AttributeRouting project (also available as a nuget). In fact, Microsoft reached out to the author of AttributeRouting to help them with their implementation for MVC 5.
You could write a custom route that derives from the Route class GetRouteData to strip dashes, but when you call the APIs to generate a URL, you'll have to remember to include the dashes for action name and controller name.
That shouldn't be too hard.
You can define a specific route such as:
routes.MapRoute(
"TandC", // Route controllerName
"CommonPath/{controller}/Terms-and-Conditions", // URL with parameters
new {
controller = "Home",
action = "Terms_and_Conditions"
} // Parameter defaults
);
But this route has to be registered BEFORE your default route.
If you have access to the IIS URL Rewrite module ( http://blogs.iis.net/ruslany/archive/2009/04/08/10-url-rewriting-tips-and-tricks.aspx ), you can simply rewrite the URLs.
Requests to /my-controller/my-action can be rewritten to /mycontroller/myaction and then there is no need to write custom handlers or anything else. Visitors get pretty urls and you get ones MVC can understand.
Here's an example for one controller and action, but you could modify this to be a more generic solution:
<rewrite>
<rules>
<rule name="Dashes, damnit">
<match url="^my-controller(.*)" />
<action type="Rewrite" url="MyController/Index{R:1}" />
</rule>
</rules>
</rewrite>
The possible downside to this is you'll have to switch your project to use IIS Express or IIS for rewrites to work during development.
I'm still pretty new to MVC, so take it with a grain of salt. It's not an elegant, catch-all solution but did the trick for me in MVC4:
routes.MapRoute(
name: "ControllerName",
url: "Controller-Name/{action}/{id}",
defaults: new { controller = "ControllerName", action = "Index", id = UrlParameter.Optional }
);