Webbplats med flera domäner i ASP.NET Core

I det här inlägget beskrivs hur du kan skapa en webbplats med flera domännamn i ASP.NET Core. En webbplats med flera domäner är billigare att hosta, lättare att underhålla och lättare att administrera jämfört med flera webbplatser. För en multidomän-webbplats kan du anpassa innehåll, språk och design för varje domän.

En multidomän-webbplats gör att du kan översätta din webbplats till olika språk och/eller ha flera butiker på en webbplats genom att koppla produkter till domäner. Du kan koppla dina modeller till en domän, till ett språk och/eller till en butik. Du kan kombinera kopplingar, vissa modeller kan kopplas till ett språk och vissa modeller kan kopplas till en domän.

En webbplats med flera domäner lagrar domänmodeller i en databas, hämtar den aktuella domänen vid en begäran och använder egenskaperna i domänmodellen för att hämta andra resurser (exempelvis: statiska texter, en sida och/eller en produkt).

Modell

En domänmodell innehåller de egenskaper som behövs för att korrekt ansluta resurser till domännamnet. Egenskapen domain_name används för att hitta den aktuella domänen.

public class WebDomain
{
    #region Variables

    public Int32 id { get; set; }
    public string website_name { get; set; }
    public string domain_name { get; set; }
    public string web_address { get; set; }
    public Int32 front_end_language { get; set; }
    public Int32 back_end_language { get; set; }
    public string analytics_tracking_id { get; set; }
    public string facebook_app_id { get; set; }
    public string facebook_app_secret { get; set; }
    public string google_app_id { get; set; }
    public string google_app_secret { get; set; }
    public bool noindex { get; set; }

    #endregion

    #region Constructors

    public WebDomain()
    {
        // Set values for instance variables
        this.id = 0;
        this.website_name = "";
        this.domain_name = "";
        this.web_address = "";
        this.front_end_language = 0;
        this.back_end_language = 0;
        this.analytics_tracking_id = "";
        this.facebook_app_id = "";
        this.facebook_app_secret = "";
        this.google_app_id = "";
        this.google_app_secret = "";
        this.noindex = false;

    } // End of the constructor

    public WebDomain(SqlDataReader reader)
    {
        // Set values for instance variables
        this.id = Convert.ToInt32(reader["id"]);
        this.website_name = reader["website_name"].ToString();
        this.domain_name = reader["domain_name"].ToString();
        this.web_address = reader["web_address"].ToString();
        this.front_end_language = Convert.ToInt32(reader["front_end_language"]);
        this.back_end_language = Convert.ToInt32(reader["back_end_language"]);
        this.analytics_tracking_id = reader["analytics_tracking_id"].ToString();
        this.facebook_app_id = reader["facebook_app_id"].ToString();
        this.facebook_app_secret = reader["facebook_app_secret"].ToString();
        this.google_app_id = reader["google_app_id"].ToString();
        this.google_app_secret = reader["google_app_secret"].ToString();
        this.noindex = Convert.ToBoolean(reader["noindex"]);

    } // End of the constructor

    #endregion

} // End of the class

Arkivklass

Vårt arkivklass för webbdomäner innehåller alla metoder som vi behöver för att hantera webbdomäner på vår hemsida. Vi använder MS SQL som databas och distribuerad cache för att korta ned tiden det tar att hämta den aktuella domänen.

public class WebDomainRepository : IWebDomainRepository
{
    #region Variables

    private readonly IMsSqlDatabaseRepository database_repository;
    private readonly IDistributedCache distributed_cache;
    private readonly CacheOptions cache_options;

    #endregion

    #region Constructors

    public WebDomainRepository(IMsSqlDatabaseRepository database_repository, IDistributedCache distributed_cache, IOptions<CacheOptions> cache_options)
    {
        this.database_repository = database_repository;
        this.distributed_cache = distributed_cache;
        this.cache_options = cache_options.Value;

    } // End of the constructor

    #endregion

    #region Insert methods

    public Int32 Add(WebDomain post)
    {
        // Create the int to return
        Int32 idOfInsert = 0;

        // Create the sql statement
        string sql = "INSERT INTO dbo.web_domains (website_name, domain_name, web_address, front_end_language, back_end_language, "
            + "analytics_tracking_id, facebook_app_id, facebook_app_secret, google_app_id, google_app_secret, noindex) "
            + "VALUES (@website_name, @domain_name, @web_address, @front_end_language, @back_end_language, "
            + "@analytics_tracking_id, @facebook_app_id, @facebook_app_secret, @google_app_id, @google_app_secret, @noindex);SELECT CAST(SCOPE_IDENTITY() AS INT);";

        // Create parameters
        IDictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("@website_name", post.website_name);
        parameters.Add("@domain_name", post.domain_name);
        parameters.Add("@web_address", post.web_address);
        parameters.Add("@front_end_language", post.front_end_language);
        parameters.Add("@back_end_language", post.back_end_language);
        parameters.Add("@analytics_tracking_id", post.analytics_tracking_id);
        parameters.Add("@facebook_app_id", post.facebook_app_id);
        parameters.Add("@facebook_app_secret", post.facebook_app_secret);
        parameters.Add("@google_app_id", post.google_app_id);
        parameters.Add("@google_app_secret", post.google_app_secret);
        parameters.Add("@noindex", post.noindex);

        // Insert the post
        this.database_repository.Insert<Int32>(sql, parameters, out idOfInsert);

        // Return the id of the inserted item
        return idOfInsert;

    } // End of the Add method

    #endregion

    #region Update methods

    public void Update(WebDomain post)
    {
        // Create the sql statement
        string sql = "UPDATE dbo.web_domains SET website_name = @website_name, domain_name = @domain_name, web_address = @web_address, "
            + "front_end_language = @front_end_language, back_end_language = @back_end_language, "
            + "analytics_tracking_id = @analytics_tracking_id, facebook_app_id = @facebook_app_id, facebook_app_secret = @facebook_app_secret, "
            + "google_app_id = @google_app_id, google_app_secret = @google_app_secret, noindex = @noindex WHERE id = @id;";

        // Create parameters
        IDictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("@website_name", post.website_name);
        parameters.Add("@domain_name", post.domain_name);
        parameters.Add("@web_address", post.web_address);
        parameters.Add("@front_end_language", post.front_end_language);
        parameters.Add("@back_end_language", post.back_end_language);
        parameters.Add("@analytics_tracking_id", post.analytics_tracking_id);
        parameters.Add("@facebook_app_id", post.facebook_app_id);
        parameters.Add("@facebook_app_secret", post.facebook_app_secret);
        parameters.Add("@google_app_id", post.google_app_id);
        parameters.Add("@google_app_secret", post.google_app_secret);
        parameters.Add("@noindex", post.noindex);

        // Update the post
        this.database_repository.Update(sql, parameters);

    } // End of the Update method

    #endregion

    #region Count methods

    public Int32 GetCountBySearch(string[] keywords)
    {
        // Create the sql statement
        string sql = "SELECT COUNT(id) AS count FROM dbo.web_domains WHERE 1 = 1";
        for (int i = 0; i < keywords.Length; i++)
        {
            sql += " AND (website_name LIKE @keyword_" + i.ToString() + " OR domain_name LIKE @keyword_" + i.ToString() + ")";
        }
        sql += ";";

        // Create parameters
        IDictionary<string, object> parameters = new Dictionary<string, object>();
        for (int i = 0; i < keywords.Length; i++)
        {
            parameters.Add("@keyword_" + i.ToString(), "%" + keywords[i].ToString() + "%");
        }

        // Get the count
        Int32 count = this.database_repository.GetCount<Int32>(sql, parameters);

        // Return the count
        return count;

    } // End of the GetCountBySearch method

    #endregion

    #region Get methods

    public WebDomain GetCurrentDomain(HttpContext context)
    {
        // Get the domain name
        string domain_name = context.Request.Host.ToString();

        // Replace www.
        domain_name = domain_name.Replace("www.", "");

        // Get the web domain post
        WebDomain domain = GetFromCache(domain_name);

        // Make sure that the domain not is null
        if (domain == null)
        {
            domain = new WebDomain();
            domain.id = 0;
            domain.domain_name = "localhost";
            domain.web_address = "https://localhost:80";
            domain.front_end_language = 2;
            domain.back_end_language = 2;
            domain.analytics_tracking_id = "";
            domain.facebook_app_id = "";
            domain.facebook_app_secret = "";
            domain.google_app_id = "";
            domain.google_app_secret = "";
            domain.noindex = true;
        }

        // Return the domain
        return domain;

    } // End of the GetCurrentDomain method

    public KeyStringList GetDomainImageUrls(IHostingEnvironment environment, Int32 domain_id, bool showNoImageIcon)
    {
        // Create the list to return
        KeyStringList imageUrls = new KeyStringList(5);

        // Create paths
        string directoryPath = "/domains/" + domain_id.ToString() + "/images/";

        // Add images to the key string list
        imageUrls.Add("background_image", directoryPath + "background_image.jpg");
        imageUrls.Add("default_logotype", directoryPath + "default_logotype.png");
        imageUrls.Add("mobile_logotype", directoryPath + "mobile_logotype.png");
        imageUrls.Add("big_icon", directoryPath + "big_icon.png");
        imageUrls.Add("small_icon", directoryPath + "small_icon.png");

        if (showNoImageIcon == true)
        {
            // Create the no image path
            string noImagePath = "/images/no_image_wide.jpg";

            // Get all the keys in the dictionary
            List<string> keys = imageUrls.dictionary.Keys.ToList<string>();

            // Loop all the keys
            for (int i = 0; i < keys.Count; i++)
            {
                // Get the url
                string url = environment.WebRootPath + imageUrls.Get(keys[i]);

                // Check if the file exists
                if (System.IO.File.Exists(url) == false)
                {
                    imageUrls.Update(keys[i], noImagePath);
                }
            }
        }

        // Return the list
        return imageUrls;

    } // End of the GetDomainImageUrls method

    public WebDomain GetOneById(Int32 id)
    {
        // Create the sql statement
        string sql = "SELECT * FROM dbo.web_domains WHERE id = @id;";

        // Create parameters
        IDictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("@id", id);

        // Get a post
        WebDomain post = this.database_repository.GetModel<WebDomain>(sql, parameters);

        // Return the post
        return post;

    } // End of the GetOneById method

    public WebDomain GetFromCache(string domain_name)
    {
        // Create the post to return
        WebDomain post = null;

        // Get the cached settings
        string data = this.distributed_cache.GetString(domain_name);

        if (data == null)
        {
            // Get the post
            post = GetOneByDomainName(domain_name);

            // Make sure that something was found in the database
            if (post != null)
            {
                // Create cache options
                DistributedCacheEntryOptions cacheEntryOptions = new DistributedCacheEntryOptions();
                cacheEntryOptions.SetSlidingExpiration(TimeSpan.FromMinutes(this.cache_options.ExpirationInMinutes));
                cacheEntryOptions.SetAbsoluteExpiration(TimeSpan.FromMinutes(this.cache_options.ExpirationInMinutes));

                this.distributed_cache.SetString(domain_name, JsonConvert.SerializeObject(post), cacheEntryOptions);
            }
        }
        else
        {
            post = JsonConvert.DeserializeObject<WebDomain>(data);
        }

        // Return the post
        return post;

    } // End of the GetFromCache method

    public WebDomain GetOneByDomainName(string domain_name)
    {
        // Create the sql statement
        string sql = "SELECT * FROM dbo.web_domains WHERE domain_name = @domain_name;";

        // Create parameters
        IDictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("@domain_name", domain_name);

        // Get a post
        WebDomain post = this.database_repository.GetModel<WebDomain>(sql, parameters);

        // Return the post
        return post;

    } // End of the GetOneByDomainName method

    public IList<WebDomain> GetAll(string sort_field, string sort_order)
    {
        // Make sure that sort variables are valid
        sort_field = GetValidSortField(sort_field);
        sort_order = GetValidSortOrder(sort_order);

        // Create the sql statement
        string sql = "SELECT * FROM dbo.web_domains ORDER BY " + sort_field + " " + sort_order + ";";

        // Create parameters
        IDictionary<string, object> parameters = new Dictionary<string, object>();

        // Get the list of posts
        IList<WebDomain> posts = this.database_repository.GetModelList<WebDomain>(sql, parameters, 10);

        // Return the list of posts
        return posts;

    } // End of the GetAll method

    public IList<WebDomain> GetBySearch(string[] keywords, Int32 page_size, Int32 page_number, string sort_field, string sort_order)
    {
        // Make sure that sort variables are valid
        sort_field = GetValidSortField(sort_field);
        sort_order = GetValidSortOrder(sort_order);

        // Create the sql statement
        string sql = "SELECT * FROM dbo.web_domains WHERE 1 = 1";
        for (int i = 0; i < keywords.Length; i++)
        {
            sql += " AND (website_name LIKE @keyword_" + i.ToString() + " OR domain_name LIKE @keyword_" + i.ToString() + ")";
        }
        sql += " ORDER BY " + sort_field + " " + sort_order + " OFFSET @pageNumber ROWS FETCH NEXT @pageSize ROWS ONLY;";

        // Create parameters
        IDictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("@pageNumber", (page_number - 1) * page_size);
        parameters.Add("@pageSize", page_size);
        for (int i = 0; i < keywords.Length; i++)
        {
            parameters.Add("@keyword_" + i.ToString(), "%" + keywords[i].ToString() + "%");
        }

        // Get the list of posts
        IList<WebDomain> posts = this.database_repository.GetModelList<WebDomain>(sql, parameters, page_size);

        // Return the list of posts
        return posts;

    } // End of the GetBySearch method

    #endregion

    #region Delete methods

    public Int32 DeleteOnId(Int32 id)
    {
        // Create the sql statement
        string sql = "DELETE FROM dbo.web_domains WHERE id = @id;";

        // Create parameters
        IDictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("@id", id);

        // Delete the post
        Int32 errorNumber = this.database_repository.Delete(sql, parameters);

        // Return error code
        return errorNumber;

    } // End of the DeleteOnId method

    #endregion

    #region Helper methods

    public void RemoveFromCache()
    {
        // Get all domains
        IList<WebDomain> posts = GetAll("domain_name", "ASC");

        // Loop domains
        for (int i = 0; i < posts.Count; i++)
        {
            // Get the data
            string data = this.distributed_cache.GetString(posts[i].domain_name);

            // Only remove the cache if it exists
            if (data != null)
            {
                // Remove data from cache
                this.distributed_cache.Remove(posts[i].domain_name);
            }
        }

    } // End of the RemoveFromCache method

    #endregion

    #region Validation

    public string GetValidSortField(string sort_field)
    {
        // Make sure that the sort field is valid
        if (sort_field != "id" && sort_field != "website_name" && sort_field != "domain_name")
        {
            sort_field = "id";
        }

        // Return the string
        return sort_field;

    } // End of the GetValidSortField method

    public string GetValidSortOrder(string sort_order)
    {
        // Make sure that the sort order is valid
        if (sort_order != "ASC" && sort_order != "DESC")
        {
            sort_order = "ASC";
        }

        // Return the string
        return sort_order;

    } // End of the GetValidSortOrder method

    #endregion

} // End of the class

Förfrågningar

Vi hämtar den aktuella domänen varje gång det görs en förfrågan på vår hemsida. Den aktuella domänen används för att hämta andra resurser, såsom statiska texter och bilder.

[HttpGet]
[Authorize(Roles = "Administrator,Editor,Translator")]
public IActionResult Index()
{
    // Get the current domain
    WebDomain current_domain = this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

    // Get translated texts
    KeyStringList tt = this.static_text_repository.GetFromCache(current_domain.back_end_language, "id", "ASC");

    // Set form data
    ViewBag.CurrentDomain = current_domain;
    ViewBag.TranslatedTexts = tt;
    ViewBag.QueryParams = new QueryParams(ControllerContext.HttpContext.Request);

    // Return the view
    return View();

} // End of the index method

Lämna ett svar

E-postadressen publiceras inte. Obligatoriska fält är märkta *