Recently, there is a need to bind multiple domain names to a website. After each domain name is entered, a different page is required. After this function is implemented, it can save a lot of money for users who have multiple domain names and virtual space, but the virtual space matches only one site.
I have read two articles, ASP. NET MVC implementing second-level domain names and ASP. NET MVC using second-level domain names to register Area areas. They are both continuous. The idea of this article is also to continue their ideas to develop, so we must first understand the content of the previous two articles. The solution is to use their code for improvement. Thank you for the two authors.
1. Simple implementation of multiple domain names for a single site
Create an MVC site by yourself. The general structure is as follows:
Circle 1. HomeController will correspond to the page of www.demo.com domain name; WebController will correspond to the page of www.web.com
Circle 2. Add the Area of Test to correspond to the page of test.web.com.
Circle 3: The domain name resolution class obtained from the above two articles
Then, add the following statement in global to bind multiple domain names to a website. There are certainly some problems. Let's talk about them later.
, {Controller =, action =, id =, {controller =, action =, id =View Code
2. simple principles
First, use the custom route resolution entry
The MapRoute method is usually used when we add custom routing rules, use routing rules such as "{controller}/{action}/{id}" or "{controller}/{action}/{yyyy}/{mm}/{dd }" string custom routing rules.
Take a closer look at the code. The MapRoute method returns a Route-type object, while routes is a RouteCollection-type object. We can understand that the MapRoute method generates a new Route object (Route ), and add it to the RouteCollection. In fact, the system also provides an Add method, which is more direct, that is, to Add a RouteBase object to routes. The Route type actually inherits RouteBase.
Now we understand that registering a new route rule is to add a new RouteBase object to the route set. We only need to implement a class that inherits RouteBase to resolve the domain name, and then add this type of object to the routing set, we can increase the resolution of the domain name. Using this class and the Add method, we can extend and implement various complex routing parsing functions.
Second, in order to achieve the second-level domain name and second-level domain name registration Area, we also need to prepare some basic knowledge
The RouteBase class mentioned above is an abstract class and requires two methods: GetRouteData and GetVirtualPath. Simply put, the GetRouteData method parses the url and stores the key-value pairs of the parsed result into RouteData. The GetVirtualPath method restores the url from RouteData.
The RouteData class provides two locations for storing the route key-value pairs, one Values and the other okokens. So what data is stored in Values and in DataTokens? Let's look at the Route class. The most complete constructor of parameters is as follows:
Route( url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler);
Url: corresponding to the custom route rule string
Defaults: Default Value of the key parsed in the route rule string
In general, the key-value pairs in ults will be placed in Values. constraints and dataTokens will not be discussed here. I think they should be placed in DataTokens. Please check the source code to confirm this. If you are interested, you can verify it by yourself. In the actual process, ults will be a little special. The "key" in ults (the key in the key-Value Pair) corresponds to the content in "{}" in the url, for example:
url: { controller = , action = , id = }
That is to say, the url specifies the keys. defaults indicates the values corresponding to these keys. The controller and action can be considered as a general key similar to a reserved word. The general key cannot be modified, while the id is a custom key, custom keys can be set and added by yourself, such as yyyy. After Area is added to the site project and the area key value is input in defaults, area can also be considered as a general key similar to reserved words, the routing will parse the area correctly. In addition, constraint keys such as Namespaces (in constraints) must be saved to DataTokens.
If we customize the domain name route resolution class, it can resolve the routing rule string representing the domain name, and save the corresponding key-value pairs to the correct RouteData, after the domain name is registered in the route set, the domain name can be correctly resolved.
3. Problems and Solutions of Area Registration for second-level domain names
From the knowledge above, we can use the following domain name rule string to register a route for a second-level domain name:
To register an area with a second-level domain name, add a routing rule in the simple example above to resolve the second-level domain name (note that the route name cannot be repeated ):
routes.Add(, { area = , controller = , action = , id =
After the program runs, it is very good. It perfectly solves the parsing of the test.web.com/Page/Index path. Is there any problem?
1. When we enter the second-level domain name page, it is okay to use the ActionLink method to generate the second-level domain name link address, but there is a problem to generate the link address under the top-level domain name.
2. Similarly, when we use ActionLink to generate the link address of the second-level domain name on the top-level domain name page
What are the causes of these problems? We have to go back and see:
The GetVirtualPath method of the RouteBase class is used to restore the RouteData to the url, and it only restores the path part of the url without restoring the host part. If excessive host parameters are input, the generated path is faulty. Therefore, in our custom DomainRoute class, when implementing the GetVirtualPath method, we need to remove the key-value pairs resolved by the domain name saved in RouteData, class is implemented through the RemoveDomainTokens method.
The DomainRoute class implements domain name resolution. Therefore, we also need to add a method to restore the domain name, which is implemented through the GetDomainData method in the class. The original method is to replace the corresponding value in RouteData by using the keys of "{}" in the domain name rule string. This leads to the above 1, 2 problems.
When we use the second-level domain name resolution rules on the second-level domain name page to restore the domain name, the "{area}" section of the domain name will always be replaced by "test, the top-level domain name cannot be returned. When we use the top-level domain name resolution rules on the top-level domain name page to restore the domain name, even if you pass in a value like area = "test", because the domain name string does not include "{}", www.web.com will always be returned, however, you cannot obtain the link address of the second-level domain name.
How can this problem be solved? What I want is: Can I use area = "www" or area = "" to match a top-level domain name? Parse the domain name string. Writing data to Values and DataTokens is ready-made and there is no problem at all. What we need to modify is the GetDomainData method, when area = "www" or area = "" is restored, the domain name is specially processed. The modified method is as follows:
Hostname = (KeyValuePair <,> pair (pair. key ==& & = hostname. replace (+ pair. key +, = hostname. replace (+ pair. key + (hostname. contains (= hostname. replace (, =View Code
We also need an override of the ActionLink method for generating links. This method is a custom Extension Method of HtmlHelper in the LinkExtensions class. The modified method is as follows:
MvcHtmlString ActionLink (HtmlHelper htmlHelper, linkText, actionName, controllerName, RouteValueDictionary routeValues, IDictionary <,> htmlAttributes, ==] =] = (routeValues. keys. contains (] = routeValues [= routeData. route (domainRoute! = DomainRoute. GetDomainData (htmlHelper. ActionLink (linkText, actionName, controllerName, domainData. Protocol, domainData. HostName, domainData. Fragment, routeData. Values,View Code
Generally, the generated link address needs to jump to another area to pass in the value of the area in the routeValues parameter. Otherwise, it does not need to be passed in. In addition, whether the requireAbsoluteUrl parameter needs to generate an address with a domain name, that is, an absolute address. The call method is as follows:
@Html.ActionLink(, , , { area = }, , )
In addition, I changed the return value of the original method from string to MvcHtmlString. Otherwise, a shell must be added when the returned value is string:
@Html.Raw(@Html.ActionLink(, , , { area = }, , ))
So far, our problems have been basically solved. In global, you only need to register a route. The default value is the homepage of the top-level domain name. In the RegisterArea method of TestAreaRegistration in the project Area, you can deregister the content of the registered area route.
routes.Add(, { area = , controller = , action = , id =
For local debugging, you must not only set the hosts file, but also bind both the top-level domain name and second-level domain name to the site on the IIS Site, for example:
Here is the source code download.
Finally, I can only bind top-level domain names to my website in my virtual space. The Space Provider limits that only the domain names except www can be entered. Although you can use a domain name such as www.test.web.com to implement a second-level domain name, it is a bit difficult to use. In addition, you can bind only three second-level subdomain names. I'm crying ...... Actually, the product is not as good as the product ......
Finally, Lao Zhao has a series of articles to describe and implement this function. His implementation method is more inclined to use the source code of MVC for implementation, relatively more elegant. Very nice. You can also study it.