精华内容
下载资源
问答
  • 配置ARR方式的三层部署体系 概览 本篇文章指导你用ARR来配置部署三层结构。三层部署体系需要由Web层、应用程序层和数据层组成,如下图: 一般在这种部署场景中,静态内容是由Tier1层的服务器host的,而动态内容是由...

    配置ARR方式的三层部署体系

     

    概览

    本篇文章指导你用ARR来配置部署三层结构。三层部署体系需要由Web层、应用程序层和数据层组成,如下图:

    http://learn.iis.net/file.axd?i=1042

     

    一般在这种部署场景中,静态内容是由Tier1层的服务器host的,而动态内容是由Tier2层来host的。

     

    先决条件

    本演示需要以下几个先决条件:

    装有IIS7Win2008,或者更高版本

    Microsoft Application Request Routing和相关联的模块

    至少2台部署有工作站点和应用程序的内容服务器

    ARR服务其上,静态内容必须是能访问的

     

     

    步骤1 – 为静态内容更改相应的URL Rewrite规则

    在本步骤中,URL rewrite规则会根据静态内容的扩展名的不同来作出筛选。能够根据文件扩展名来辨认出是否为静态内容,如.jpg或者.gif。如果静态文件是放在某个目录中的,比如/images/目录,则URL rewrite规则也能根据目录名来进行筛选。

     

    在本演示中,我们会设置URL Rewrite规则根据扩展名.jpg/.css以及目录/images/来筛选。如果所请求的资源扩展名是.jpg或者.css,则这个请求会直接由ARR服务器处理掉(不转发到内容服务器)。类似的,如果请求的资源包含/images/路径,也是这样处理。而所有其它的请求则会被ARR服务器转发给各个内容服务器来处理。

     

    在继续之前,确保静态内容在ARR服务器上是能够被访问得到的。这些静态内容既可以是ARR服务器本地的,也可以是由共享目录实现的。

     

    通过图形界面更改URL Rewrite规则:

    1.       打开IIS管理器

    2.       打开农场myServerFarm(前几篇文章中创建的一个农场名称)

    3.       显示如下:

    a)         http://learn.iis.net/file.axd?i=1199

    4.       双击Routing Rules

    a)         Requests with following extensions are not forwarded中输入*.jpg, *.css(逗号分隔开多个)

    b)         Requests with following patterns are not forwarded中输入*/images/*

    c)         http://learn.iis.net/file.axd?i=1206

    5.       要确认静态图片已经被ARR服务器处理了,可以查看日志文件。日志文件默认的位置是c:\inetpub\logs\LogFiles\。在日志文件中能看到这些jpg, css, images没有被内容服务器处理。

     

     

     

    命令行方式更改URL rewrite规则:

    1.       administrator身份打开命令行

    2.       导航到目录%windir%\system32\inetsrv

    3.       输入下列命令清除所有URL Rewrite规则:

    a)         appcmd.exe clear config -section:system.webServer/rewrite/globalRules

    b)         接着更改规则来把请求这些扩展名*.jpg, *.css以及路径/images/的资源不转发到内容服务器:

                             i.              appcmd.exe set config -section:system.webServer/rewrite/globalRules /+"[name='ARR_myServerFarm_loadbalance',patternSyntax='Wildcard',stopProcessing='True']" /commit:apphost

                           ii.              appcmd.exe set config -section:system.webServer/rewrite/globalRules /[name='ARR_myServerFarm_loadbalance',patternSyntax='Wildcard',stopProcessing='True'].match.url:"*" /commit:apphost

                          iii.              appcmd.exe set config -section:system.webServer/rewrite/globalRules /+"[name='ARR_myServerFarm_loadbalance',patternSyntax='Wildcard',stopProcessing='True'].conditions.[input='EXT_{URL}',negate='True',pattern='*.jpg']" /commit:apphost

                          iv.              appcmd.exe set config -section:system.webServer/rewrite/globalRules /+"[name='ARR_myServerFarm_loadbalance',patternSyntax='Wildcard',stopProcessing='True'].conditions.[input='EXT_{URL}',negate='True',pattern='*.css']" /commit:apphost

                           v.              appcmd.exe set config -section:system.webServer/rewrite/globalRules /+"[name='ARR_myServerFarm_loadbalance',patternSyntax='Wildcard',stopProcessing='True'].conditions.[input='{URL}',negate='True',pattern='*/images/*']" /commit:apphost

                          vi.              appcmd.exe set config -section:system.webServer/rewrite/globalRules /[name='ARR_myServerFarm_loadbalance',patternSyntax='Wildcard',stopProcessing='True'].action.type:"Rewrite" /[name='ARR_myServerFarm_loadbalance',patternSyntax='Wildcard',stopProcessing='True'].action.url:"http://myServerFarm1/{R:0}" /commit:apphost

    转载于:https://www.cnblogs.com/aarond/archive/2011/04/12/2014057.html

    展开全文
  • Web应用程序设计()考试试题(A卷) 班级: 姓名_ 学号_ 题号 一 二 四 五 六 七 八 九 十 总分 分数 一填空题每空2分共32分 1目前在应用开发领域中主要分成两大编程体系一种是C/S结构另一种是基于浏览器 ...
  • web应用开发复习题

    2013-01-08 18:01:28
    WEB应用程序开发复习要点(部分) 1、 发送请求和响应信息使用的协议?(P6) ...2、 WEB应用程序的三层体系结构?(P11) 目前的WWW主要由Web客户机、Web服务器以及二者之间的通信协议三部分组成。
  • 本文为集成Web应用程序的三个不同但至关重要的部分提供了启示。即客户端,服务端和服务。将客户端和服务端代码区域称为可能是错误的,但是为了命名约定的统一,让我们将它们视为多层系统的特定,服务端...

    目录

    介绍

    目标听众

    期待什么

    示例代码概述

    总览

    Handlebars和模板

    使用代码

    起步

    第1步


    GitHub下载

    介绍

    Web应用程序的开发趋势不时发生变化。几年前我们用来构建的应用程序体系结构现在已经开始或已经过时且效率较低。您将要阅读的文章适用于相同的变化趋势。

    本文为集成Web应用程序的三个不同但至关重要的部分提供了启示。即客户端层,服务端层和服务层。将客户端和服务端代码区域称为层可能是错误的,但是为了命名约定的统一,让我们将它们视为多层系统的特定层,服务端位于其中,然后是服务,然后是客户端(如果您愿意,您甚至可以改变排序,那完全可以)。

    目标听众

    我将假设您已经熟悉使用Web API以及Handlebars。如果您处理的大型应用程序可能达到企业规模,那就太好了,因为这样您就可以更轻松地将我尝试在示例代码中实现的关注点分离联系起来。

    期待什么

    本文绝不尝试提出某种新形式的Web应用程序开发模式。Web模板已经存在很长时间了,只是我们处理模板中内容生成的方式发生了变化。我们曾经在不同位置将服务器端变量和嵌入式代码放在HTML标记中,以实现与模板的数据绑定。即使在今天,我们中的许多人还是会喜欢并严重依赖动态代码生成技术,例如ASP.NET MVC中的Razor View,这是可以理解的,因为它完美地解决了内容生成和视图模型绑定的问题。但是,如果您是像我这样的头脑简单的开发人员,并且不太愿意将太多内容混淆在一起,那么您将了解维护一个将我们正确分离所有内容的应用程序是多么容易。而且,我一直认为在模板代码中放入过多的领域逻辑是一个坏主意。

    本文旨在提供一个使用JavaScriptWeb APIWeb模板代码实现,以便希望对这些应用程序中的事物如何运行有基本了解的开发人员可以从此处获得参考。

    如果您愿意进行Google的快速搜索,则可以在Internet上轻松找到其他此类代码示例,但是最好参考多种做同一方法的引用,因为我们每个人都有不同的要求。

    示例代码概述

    本文中的代码用于示例购物车布局。在此应用程序中,我们基本上必须在预定义的网页设计中更新信息。为此,最好使用HTML模板,因为我们只需要将输入数据与模板结合起来并显示已编译的HTML结果。

    总览

    我试图使事情保持简单,我的重点主要放在Web API服务调用和已编译模板的代码生成以及正确分离所有内容上。您将找不到用于数据库CRUD操作的任何代码,为此,我创建了一个非常简单的数据映射器以使用内存中对象。您可以随意编写数据映射器的代码,以根据需要在附加的示例代码中支持其他提供程序。

    此实现的优点在于,将特定类型的数据映射器注入到Web API控制器中,以便我们可以轻松地在开发数据和测试数据之间切换。这种方法使我们很容易将UI与单独的测试模块集成在一起。我们需要做的就是使用另一个Web.config文件来使用另一个数据映射器。然后,映射器可以从诸如内存对象“XML或测试数据库之类的任何源返回数据。

    本文的篇幅将有些长,因此我将请您忍受,您将发现如何正确构造Web应用程序框架,并且在编写摘要时可以减少编写新功能的代码。

    首先,让我们简要了解一下Handlebars模板引擎,以及有关所有模板的介绍。我假设您已经熟悉ASP.NET应用程序和Web API,并且对模板有一些基本的了解。如果您不是,那么我建议您先进一步了解它们,然后再继续阅读本文。

    Handlebars和模板

    模板作为结构实践已经使用了很长时间。模板只是表示输出蓝图或线框,其中有一些标记被替换为输入数据中包含的实际值。然后将模板与数据合并以生成所需的输出。

    对于Handlebars,我将仅使用Wikipedia上给出的定义,因为它已经非常清楚了,我认为不需要重新发明另一个定义。

    引用:

    Handlebars是一个语义Web模板系统,由Yehuda Katz2010年启动。Handlebars.jsMustache的超集,除了Handlebars模板外,还可以呈现Mustache模板。虽然Mustache​​一种无逻辑的模板语言,但是Handlebars增加了可扩展性和最小的逻辑,例如#if,#unless,#with#each帮助器。

    使用代码

    起步

    在开始理解客户端代码之前,我们应该首先了解服务器代码中的情况。该示例代码具有一个单独的数据层,其中包含用于从您决定使用的任何数据源获取数据的类。我已经创建了一个接口IMapper,并将其放在另一个层中,该层在所有地方都可以共享,以便我们可以管理应用程序不同部分之间的依赖项注入。

    1

    我们将从在Visual Studio 2012中创建一个空的ASP.NET Web窗体应用程序开始(您可以从此处下载一个)。这个空的应用程序没有任何与导航或身份验证相关的预生成代码,而这正是我们此示例应用程序所需要的。

    这是项目结构的快照。我将逐一解释其中的所有模块:

    添加一个新的.aspx文件,并将其命名为Default.aspx。另外,添加一个母版页并将其命名Master.master。到目前为止,我们不需要在母版页上添加任何代码,稍后我们将添加对所需脚本和样式表的引用。

    将我们刚刚添加的Default.aspx文件中的代码替换为以下内容:

    %@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" 
       Inherits="TestCart.Default" MasterPageFile="~/Master.Master" %>
    
    <asp:Content ID="index" runat="server" ContentPlaceHolderID="MainPlaceHolder">
        <div id="divGrid">
        </div>
        <script>      
        </script>
    </asp:Content>

    默认页面将包含所有可用产品的表格。

    添加另一个aspx页面并将其命名为Product.aspx。在此示例应用程序中,我们将只有两个aspx页面,它们足以传达对使用HandlebarsASP.NET中进行模板制作的理解。将以下代码添加到Product.aspx页。

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Product.aspx.cs" 
    
        Inherits="TestCart.Product"  MasterPageFile="~/Master.Master" %>
    
    <asp:Content ID="product" runat="server" ContentPlaceHolderID="MainPlaceHolder">
        <div id="divProduct">
        </div>
        <script>        
        </script>
    </asp:Content>

    在进一步深入客户端之前,让我们首先了解服务器代码结构。客户端只需调用Web Api即可获取数据,然后将数据与模板进行合并。现在,我们可以遵循以下两种方法之一:

    1. 第一个是将领域逻辑直接写入Web Api类,初始化Global.asax文件中的数据访问模块,并将其绑定到会话。Web Api类将在会话中使用该数据访问模块来获取数据。这种方法有很多缺点,例如服务层内部不应有任何业务逻辑(或者应该是最低限度),并且我们还将服务与数据访问模块紧密结合在一起,这使得集成测试模块或其他生产数据提供程序非常困难。
    2. 第二个是当我们将适当的数据访问模块依赖项注入Web Api而不在其中放置任何域逻辑代码时。

    现在很明显,你们已经找出了哪种方法更好,所以让我们开始实施第二种方法。

    添加一个新的类库项目并将其命名Data。在Data项目中添加一个类,并为其命名Data。(请参阅上面的解决方案资源管理器图像)。

    Data类上,我们将首先照顾我们的内存产品存储库。该存储库将用于将静态产品对象存储在内存中。为此,请将以下代码添加到Data.cs文件中:

    #region Static Data
    
    /// <summary>
    /// Product repository class that contains the static product information
    /// </summary>
    public class ProductRepository
    {
        private List<Product> _products;
    
        /// <summary>
        /// Get all products
        /// </summary>
        public List<Product> Products { get { return _products; } }
    
        public ProductRepository()
        {
            Product product;
            _products = new List<Product>();
    
            product = new Product(1, "iPhone 6", 38999, "iphone.jpg",
                "Apple iPhone 6 exudes elegance and excellence at its best. With iOS 8,
                the world’s most advanced mobile operating system, and a larger and
                superior 4.7 inches high definition Retina display screen,
                this device surpasses any expectations you might have with a mobile phone. ",
                "This new generation device comes with improved Wi-Fi speed to enhance
                your surfing experience. Its sleek body and seamless design along with
                a perfect unison between its software and hardware system gives you
                marvellous navigation results. Just double tap the home screen
                and the entire screen shifts down to your thumb for easy operation.
                In terms of security, this iPhone is a class apart from its predecessors
                as it allows you finger print lock. The multitude of features in
                Apple iPhone 6 makes it powerful, efficient, smooth and super easy to use.
                With this phone in your hands, you can manage your world with
                just a single touch!",
                true);
            product.Reviews = new List<Review>();
            product.Attributes = new List<ProductAttribute>();
    
            product.Reviews.Add(new Review("John", "Very good phone!!", 4));
            product.Attributes.Add(new ProductAttribute("Brand", "Apple"));
    
            _products.Add(product);
        }
    
        /// <summary>
        /// Get product by id
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public Product GetById(Int32 id)
        {
            return _products.Find(p => p.Id == id);
        }
    }
    
    #endregion Static Data

    上面是添加内存对象的非常丑陋的方法,但这将对本示例应用程序有用。ProductRepository类构造函数中,我们将产品信息添加到Products列表中。随附的代码将更多产品添加到列表中。视图模型类ProductReviewsAttributes被包含在共享模块中。

    可以直接从只读Products属性访问产品列表。有一种方法GetById可以使用整数类型Id在产品列表中搜索产品,并在找到后返回product对象。

    现在,我们需要添加抽象和具体的映射器类以获取数据。在此之前,我们需要在解决方案中为共享数据添加另一个逻辑层。添加一个新的类库项目并将其命名Shared。在共享层添加三个类和它们命名为ModelsSharedInterfacesTypes

    让我们在Models类中为视图模型类添加如下代码:

    /// <summary>
    /// Product class
    /// </summary>
    public class Product
    {
        public Int32 Id { get; set; }
        public String Name { get; set; }
        public Decimal Price { get; set; }
        public String Image { get; set; }
        public String SmallDescription { get; set; }
        public String LargeDescription { get; set; }
        public List<Review> Reviews { get; set; }
        public List<ProductAttribute> Attributes { get; set; }
        public Boolean InStock { get; set; }
    
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="image"></param>
        /// <param name="smallDescription"></param>
        /// <param name="largeDescription"></param>
        /// <param name="inStock"></param>
        public Product(Int32 id, String name, Decimal price, String image,
                       String smallDescription,
                       String largeDescription, Boolean inStock)
        {
            Id = id;
            Name = name;
            Price = price;
            Image = image;
            SmallDescription = smallDescription;
            LargeDescription = largeDescription;
            InStock = inStock;
        }
    }
    
    /// <summary>
    /// Product review class
    /// </summary>
    public class Review
    {
        public String ReviewedBy { get; set; }
        public String ReviewText { get; set; }
        public Int32 Rating { get; set; }
    
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="reviewedBy"></param>
        /// <param name="reviewText"></param>
        /// <param name="rating"></param>
        public Review(String reviewedBy, String reviewText, Int32 rating)
        {
            ReviewedBy = reviewedBy;
            ReviewText = reviewText;
            Rating = rating;
        }
    }
    
    /// <summary>
    /// Product attribute class
    /// </summary>
    public class ProductAttribute
    {
        public String Name { get; set; }
        public String Value { get; set; }
    
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public ProductAttribute(String name, String value)
        {
            Name = name;
            Value = value;
        }
    }

    上面的代码是不言自明的,我们只是添加带有public属性的简单类来保存产品数据。这些视图模型将返回到JavaScript代码,然后与我们的模板合并。

    我们将需要在应用程序之间共享接口协定,以执行依赖项注入。因此,将它们放在一个共同的地方很有意义。将以下代码添加到SharedInterfaces类:

    /// <summary>
    /// Mapper interface
    /// </summary>
    public interface IMapper
    {
        Object Get(DataType type);
        void SetId(Int32 id);
    }
    
    /// <summary>
    /// Product Mapper interface
    /// </summary>
    public interface IProductMapper
    {
        List<Product> GetAllProducts();
        Product GetProductById(Int32 id);
    }

    IMapper是所有数据映射器的接口。Get方法将根据类型返回数据。SetId方法是设置我们需要的记录的IDDataType是一个enum,它将指导Get方法获取我们所需的特定信息。

    还有另一个特定于产品的IProductMapper接口。IMapper将注入到IProductMapper的构造函数。然后,IProductMapper方法实现将在IMapper内部调用方法以执行Crud操作。GetAllProducts将获得所有可用的产品,并且GetProductById根据提供的ID返回单个产品。当我们使用数据库时,具体的产品映射器方法实现将需要包装在事务范围内。

    共享中只剩下添加DataType enum实现:

    /// <summary>
    /// Data type enum
    /// </summary>
    public enum DataType
    {
        AllProducts,
        IndividualProduct
    }

    到目前为止,我们只需要两种类型;AllProductsIndividualProducts。当前的映射器实现需要处理所有类型,以返回所请求数据的类型。您可以根据需要随意添加更多内容。

    有了共享代码后,现在我们可以开始实现具体的映射器类了。我们将有三个映射器类:AbstractMapperInMemoryMapperProductMapper

    AbstractMapper将包含所有不同的具体映射器通用的代码。将以下代码添加到Data.cs类中,以实现此目的:

    #region Mappers
    
    /// <summary>
    /// Abstract data mapper.
    /// </summary>
    public abstract class AbstractMapper : IMapper
    {
        protected Int32 _id;
        public abstract Object Get(DataType type);
        public void SetId(Int32 id)
        {
            _id = id;
        }
    }

    在上面的代码中,我们将Get方法声明为abstract,以便继承abstract映射器的具体类可以执行该实现。SetId方法只是设置受保护_id变量的值。

    InMemoryMapper将通过实例化ProductRepository类从我们将创建的内存数据中获取数据。以下代码适用于此映射器类:

    /// <summary>
    /// Product mapper to get data from the objects in the memory.
    /// </summary>
    public class InMemoryMapper : AbstractMapper
    {
        /// <summary>
        /// Get the data
        /// </summary>
        /// <returns></returns>
        public override Object Get(DataType type)
        {
            Object retVal = null;
    
            switch (type)
            {
                case DataType.AllProducts:
                    retVal = new ProductRepository().Products;
                    break;
    
                case DataType.IndividualProduct:
                    retVal = new ProductRepository().GetById(_id);
                    break;
            }
    
            return retVal;
        }
    }

    以上是Get方法的实现。根据请求的数据类型,代码初始化ProductRepository的新实例,然后获取数据。需要注意的一件事是,您可以将内存数据设置为static避免每次都创建新对象。这是我留给你们练习的一项练习。

    ProductMapper是产品的特定映射器。它将使用IMapper接口执行不同的代码指令以获取用户所需的数据。将以下代码添加到Data.cs文件:

    /// <summary>
    /// Concrete mapper class
    /// </summary>
    public class ProductMapper : IProductMapper
    {
        private IMapper _mapper = null;
    
        public IMapper Mapper
        {
            get { return _mapper; }
            set { _mapper = value; }
        }
    
        public ProductMapper(IMapper mapper)
        {
            _mapper = mapper;
        }
    
        public List<Product> GetAllProducts()
        {
            return _mapper.Get(DataType.AllProducts) as List<Product>;
        }
    
        public Product GetProductById(Int32 id)
        {
            _mapper.SetId(id);
            return _mapper.Get(DataType.IndividualProduct) as Product;
        }
    }

    如果您熟悉最简单的依赖项注入的工作方式,那么上面的代码将很简单。如果您不熟悉,那么我强烈建议您阅读一下。

    我们正在做的只是将IMapper接口的特定实现注入到ProductMapper构造函数中。ProductMapper只需要知道IMapper的成员,而不是实现的类型。然后,我们将在Web Api控制器中注入ProductMapper实现。

    我们已经创建了所有必需的类来获取产品数据,现在是时候进行Web Api了。添加一个新的Web Api控制器并将其命名ProductController。这一产品将带有空GET/PUT/POST/DELETE方法。我们只需要从服务器获取数据的GET方法。将以下代码添加到ProductController构造函数中,该构造函数接受IProductMapper类型的mapper 作为参数。我们还需要添加一个private字段来保存GET方法的映射器引用和修改后的签名:

    public class ProductController : ApiController
    {
        private readonly IProductMapper _mapper;
    
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="mapper">Mapper dependency</param>
        public ProductController(IProductMapper mapper)
        {
            _mapper = mapper;
        }
    
        // GET api/<controller>
        public List<Shared.Product> Get()
        {
            return _mapper.GetAllProducts();
        }
    
        // GET api/<controller>/5
        public Shared.Product Get(int id)
        {
            return _mapper.GetProductById(id);
        }
    
        // POST api/<controller>
        public void Post([FromBody]string value)
        {
        }
    
        // PUT api/<controller>/5
        public void Put(int id, [FromBody]string value)
        {
        }
    
        // DELETE api/<controller>/5
        public void Delete(int id)
        {
        }
    }

    Api控制器类将使用private _mapper调用具体映射器类的IMapper类型实现。这些方法无需担心正在使用哪种类型的映射器,因此UI完全不知道并且已从数据提供者中取消插入。这使得将定制测试与我们的应用程序集成非常容易。

    现在出现了一个问题,我们将如何在Web Api控制器类中实际注入映射器。我们将通过创建IDependencyResolver接口的实现来做到这一点。IDependencyResolver用于解析Web Api服务依赖关系,我们可以根据请求的服务类型返回服务。在下面的类中对此进行了说明。添加一个新的文件夹App_Code和一个类MyDependencyResolver,并将此代码添加到类文件中:

    public class MyDependencyResolver : IDependencyResolver
    {
        private static readonly IProductMapper Mapper =
                new ProductMapper(DataHelper.CreateMapper(WebConfigurationManager.AppSettings
                ["MapperType"].ToString()));
    
        public Object GetService(Type serviceType)
        {
            return serviceType == typeof(ProductController) ?
                                  new ProductController(Mapper) : null;
        }
    
        public IEnumerable<Object> GetServices(Type serviceType)
        {
            return new List<Object>();
        }
    
        public IDependencyScope BeginScope()
        {
            return this;
        }
    
        public void Dispose()
        {
    
        }
    }

    GetService方法实现将用于根据请求的服务类型返回适当的Api控制器。当请求的类型为ProductController时,我们将根据存储在配置文件中的映射器类型注入适当的映射器依赖项,从而返回ProductController类的新实例。

    映射器类型的值需要存储在web.config文件中:

    <appSettings>
        <add key="MapperType" value="InMemoryMapper" />
    </appSettings>

    现在,我们需要使用应用程序配置连接依赖解析器,可以在Global.asax文件中完成此操作,如下所示:

    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.MapHttpRoute(
                        name: "DefaultApi",
                        routeTemplate: "api/{controller}/{id}",
                        defaults: new { id = System.Web.Http.RouteParameter.Optional }
                        );
            GlobalConfiguration.Configuration.DependencyResolver = new MyDependencyResolver();
        }
    }

    哦,当我们将Web Api控制器显式添加到Web窗体应用程序时,我们还需要将api控制器路由模板与路由表进行映射。

    我们仍然缺少重要的代码,这些代码将用于使用String配置中存储的值来创建映射器类型实例。将此代码添加到Data.cs文件中:

    #region Helper
    
    public static class DataHelper
    {
        /// <summary>
        /// Creates the mapper object from the given type.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IMapper CreateMapper(String type)
        {
            return Activator.CreateInstance(Type.GetType
                             (String.Format("Data.{0}", type))) as IMapper;
        }
    }
    
    #endregion Helper

    上面,我们使用反射创建映射器和类型,然后在返回对象之前将其类型转换为IMapper然后,该mapper对象将被传递到具体的产品映射器类构造函数中。

    这一切都与服务器代码有关,现在我们可以自由地继续讨论客户端代码。您必须已经添加了母版页和aspx页,现在我们必须添加支持的JavaScript代码。为此,我们应该在JavaScript中创建一个应用程序结构,该结构应可从任何地方轻松访问。为此,我将要附加一个名为App窗口的对象。从我们的App对象,我们可以做不同的事情,例如使用Handlebars模板,事件绑定等调用Web Api

    我将添加与不同任务关联的单独的JavaScript文件,以使将来的代码更改更加容易。要将App对象加载到窗口中,我将使用一个简单的加载器函数。同样,我将为每个模块使用单独的加载器功能。

    Content\Scripts文件夹中添加一个新的JavaScript文件,并将其命名为App.js

    ;
    (function (w, undefined)
    {
        function loadApp()
        {
            var app =
                {
                    'Events': new w['AppEventsLoader'](),
                    'Service': new w['AppServiceLoader'](),
                    'Constants': new w['AppConstantsLoader'](),
                    'Templates': new w['AppTemplatesLoader']()
                };
    
            w['App'] = app;
    
            //There is no going back now!!
            if (w['LoadApp']) w['LoadApp'] = undefined;
            if (w['AppEventLoader']) w['AppEventLoader'] = undefined;
            if (w['AppServiceLoader']) w['AppServiceLoader'] = undefined;
            if (w['AppConstantsLoader']) w['AppConstantsLoader'] = undefined;
            if (w['AppTemplatesLoader']) w['AppTemplatesLoader'] = undefined;
        }
        
        w['LoadApp'] = loadApp;
    })(window);

    加载程序完成其工作后,由于不再使用它们,我们将其从内存中删除。在这一点上,您必须考虑如何调用LoadApp函数,当我们到达下面的母版页实现时,您会发现这一点。

    让我们添加App对象的不同模块。添加一个新的JavaScript文件,并将其命名为Service.js。我们将使用它通过一个入口点来调用Web Api

    ;
    (function (w, $)
    {
        var serviceLoader = function ()
        {
            this.Call = function (type, args)
            {
                var url = w.App.Constants.ServiceMap[type];
    
                if (!args) return $.getJSON(url);
                else return $.getJSON(url, args);
            };
        };
        
        w['AppServiceLoader'] = serviceLoader;
    })(window, jQuery);

    在上面的代码中,type用于确定Web ApiURL。如果args不是null,那么我们将通过服务调用将其发送。我已将服务类型和模板url的元数据信息存储在代码中,该代码将在App初始化时加载。Constants.js用于此目的:

    ;
    (function (w)
    {
        var constantsLoader = function ()
        {
            this.ServiceMap =
            {
                'GetAllProducts': '/api/Product/',
                'GetProduct': '/api/Product/'
            };
    
            this.TemplateMap =
            {
                'ProductGrid': 'Content/Templates/ProductGrid.html',
                'ProductItem': 'Content/Templates/ProductItem.html'
            };
        };
    
        w['AppConstantsLoader'] = constantsLoader;
    
    })(window);

    最后一个JavaScript模块将用于编译Handlebars模板。现在,在尝试实现代码之前,我们应该确保如何将所有内容放在一起。所以就这样。我们需要:

    1. 输入数据
    2. handlebars模板,它只是HTML字符串和标记在这里和那里的{{value}}形式,和
    3. 最后,我们需要将两者结合起来以获取最终的HTML,并将其显示给用户。这意味着输出HTML将始终基于用户请求的信息。

    例如,如果我们以Json的形式输入数据如下:

    var data = {'FirstName': 'Mark', 'LastName': 'Twain'};

    我们有以下handlebars模板:

    <div id='placeholder'></div>
    <script id="my-template" type="text/x-handlebars-template">
        <div>First Name: {{FirstName}}</div>
        <div>Last Name: {{LastName}}</div>
    </script>

    然后我们将编写以下JavaScript代码以将模板与数据结合起来:

    var myTemplate = Handlebars.compile($('#my-template').html());
    var myHtml = myTemplate(data);
    $('#placeholder').html(myHtml);

    我们还可以将json对象数组与模板绑定,为此,我们需要使用以下示例中给出的{{#each}}令牌:

    var contact = {'Name': 'Jason', 'Phones':[{'Number': 111111}, {'Number': 222222}]};
    
    <script id="my-template" type="text/x-handlebars-template">
        <div>Name: {{Name}}</div>
        Phone Numbers: <br />
        {{#each Phones}}
            <div>Last Name: {{Number}}</div>
        {{/each}}     
    </script>

    就这么简单。但是,我们不会在示例应用程序中编写如此简单的代码。我们将在需要时将模板加载到浏览器缓存中,然后将使用输入数据获取输出HTML

    添加一个新的JavaScript文件并将其命名为Templates.js 并将以下内容粘贴到其中:

    ;
    (function (w, $, h)
    {
        var templatesLoader = function ()
        {
            this.GetTemplateHtmlAjax = function (name, jsonData, secondCallback)
            {
                var source;
                var template;
                var html;
    
                $.ajax({
                    url: App.Constants.TemplateMap[name],
                    cache: true  
                }).then(function (data)
                {
                    source = data;
                    template = h.compile(source);
                    html = template(jsonData);
                    secondCallback(html);
                });
            }        
        };
    
        w['AppTemplatesLoader'] = templatesLoader;
    })(window, jQuery, Handlebars);

    GetTemplateHtmlAjax函数接受template namejsonData并和callback作为参数。模板名称用于解析模板文件路径,将提供jsonData作为已加载和已编译模板的源,并且secondCallback将在传递输出HTML的同时调用该函数。

    因此,当我们想将输入数据与模板绑定时,只需调用上面的GetTemplateHtmlAjax代码,并将HTML编写代码放在回调函数中,一旦获得HTML响应,该函数便会执行。

    我们的小型客户端框架几乎已完成,我们现在要做的就是定义模板HTML并将所有内容放在一起。将两个HTML文件添加到Templates文件夹(请参见上面的解决方案资源管理器图像),并将它们命名为ProductGrid.htmlProductItem.html

    ProductGrid.html

    <div>
        {{#each Products}}
            <div class="ProductGridItem">
                <div>
                    <div style="display:block;overflow:hidden;margin:0 auto;">
                        <a href="Product.aspx?id={{Id}}">
                        <img src="../../Content/Images/{{Image}}" /></a>
                    </div>
                </div>
                <hr />
                <div><a href="Product.aspx?id={{Id}}"><b>{{Name}}</b></a></div>            
                <hr />
                <div style="text-align:center;"><b>Rs. {{Price}}</b></div>    
                <hr />      
                <div><a class="AddToCartGridButton" href="#">Add To Cart</a></div>  
            </div>
        {{/each}}
    </div>

    ProductItem.html

    <div style="overflow:hidden;">
        <div class="ProductItemLeftDiv">
            <img src="../../Content/Images/{{Image}}" />
        </div>
        <div class="ProductItemRightDiv">
            <div><h2>{{Name}}</h2></div>
            <hr />
            <b>Price</b> {{Price}}
            <hr />
            {{SmallDescription}}
            <hr />
        </div>
    </div>
    <div id="divLargeDescription">
        <h3>Description</h3>
        <hr />
        {{LargeDescription}}
    </div>
    <div id="divAttributes">
        <h3>Attributes</h3>
        <hr />
        <table>
            {{#each Attributes}}
                <tr>
                    <td><b>{{Name}}</b></td>
                    <td>{{Value}}</td>
                </tr>    
            {{/each}}
        </table>
    </div>
    <div id="divReviews ">
        <h3>Reviews</h3>
        <hr />
        {{#each Reviews}}
            <span class="ReviewedBySpan"><b>{{ReviewedBy}}</b></span>
            <span class="RatingSpan">Rating: {{Rating}}</span>
            <div>{{ReviewText}}</div>
        {{/each}}
    </div>

    我们将需要HandlebarsjQuery库文件。尽管jQuery不是强制性的,但我们大多数人最终还是会在某种程度上使用jQuery,以避免为各种浏览器处理每种代码方案。

    下载最新的HandlebarsjQuery库脚本并将其引用添加到母版页中(或者,您可以使用cdn链接,因为它们可能已经被浏览器缓存了)。我们还需要添加所有JavaScript文件的引用,并添加一个单独的脚本块来初始化我们的客户端App对象。

    <%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Master.master.cs" 
        Inherits="TestCart.Master" %>
    
    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    
        <link href="Content/Style/Style.css" rel="stylesheet"/>
    
        <script src="Content/Scripts/jquery-1.11.3.min.js"></script>
        <script src="Content/Scripts/handlebars-v4.0.2.js"></script>
        <script src="Content/Scripts/App.js"></script>
        <script src="Content/Scripts/Constants.js"></script>
        <script src="Content/Scripts/Events.js"></script>
        <script src="Content/Scripts/Service.js"></script>
        <script src="Content/Scripts/Templates.js"></script>
    
        <script>
            $(document).ready(function ()
            {
                if(!window.App) window.LoadApp();
            });
        </script>
    
        <asp:ContentPlaceHolder ID="head" runat="server">
        </asp:ContentPlaceHolder>
    
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:ContentPlaceHolder ID="MainPlaceHolder" runat="server">
            
            </asp:ContentPlaceHolder>
        </div>
        </form>    
    </body>
    </html>

    在上面的代码中,如果浏览器内存中尚不存在App对象,我们将调用LoadApp函数来创建该对象。

    将以下脚本块添加到Default.aspx以加载产品表格:

    <script>
            $(document).ready(function ()
            {
                var app = window.App;
                if (app)
                {
                    app.Service.Call('GetAllProducts').then(function (data)
                    {
                        $divGrid = $('#divGrid');
    
                        //Our callback
                        function setHtml(html)
                        {
                            $divGrid.html(html);
                        }
    
                        app.Templates.GetTemplateHtmlAjax('ProductGrid'
                        , { 'Products': data }, setHtml.bind(this));
                    })
                }
            });
        </script>

    上面的代码应该很容易理解。我们正在调用GetAllProducts Web Api服务,并在成功回调中,正在从Templates模块调用GetTemplateHtmlAjax函数以获取输出html字符串。该setHtml是一个回调函数,它作为参数传递给GetTemplateHtmlAjax,并在HTML响应后调用。Web Api服务的路径将从我们编码为Constants.js文件的元数据信息中解析。

    这里要注意的一件事是,我们在setHtml函数上使用bind 来保留函数范围。如果您不知道这意味着什么,那么您应该了解有关JavaScript范围的更多信息,因为这不在本文的讨论范围之内(无双关!)。

    同样,将以下脚本块添加到Product.aspx文件中:

    <script>
        $(document).ready(function ()
        {
            var app = window.App;
            if (app)
            {
                app.Service.Call('GetProduct', { 'Id': getParameterByName('id') }).then
                (function (data)
                {
                    $divProduct = $('#divProduct');
    
                    //Our callback
                    function setHtml(html)
                    {
                        $divProduct.html(html);
                    }
    
                    app.Templates.GetTemplateHtmlAjax('ProductItem', data, setHtml.bind(this));
                })
            }
    
            //To read id from the url
            //http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
            function getParameterByName(name)
            {
                name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
                var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
                    results = regex.exec(location.search);
                return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
            }
        });
    </script>

    getParameterByName是否可以读取url中任何查询字符串参数的值。我们正在从网址中读取id的值,然后将其作为参数传递给Web api服务调用。

    就是这样,现在您所需要做的就是下载示例代码并运行(如果尚未运行),并将此处给出的所有步骤与示例代码相关联,以更好地了解所有工作方式。我一般不会对HandlebarsWeb Api进行详细介绍,因为我的目的是提供一个工作样本,您可以在该样本上建立代码,或者可以从不同的角度更好地了解实现时如何设计应用程序HTML模板。

    展开全文
  • 概述 本章Web架构分层指南,参考了“Microsoft应用程序体系结构指南”(该书是在2009年出版,当时出版是为了帮助开发人员和架构师更快速,更低风险地使用Microsoft平台和.NET Framework设计和构建有效,高质量...

    一.概述

      本章Web架构分层指南,参考了“Microsoft应用程序体系结构指南”(该书是在2009年出版的,当时出版是为了帮助开发人员和架构师更快速,更低风险地使用Microsoft平台和.NET Framework设计和构建有效,高质量的应用程序)。虽然已过去十年了,技术架构已更新(如流行的DDD/CQRS模式,微服务,容器),但web分层思想还是一样可取,下面是一个“传统N分层设计”架构图,该架构在2010年左右是最流行的,包括了表现层presentation,服务层services,业务层business,数据访问层data,横切关注点cross,如下所示:

    736d4d1ada733479b5c8e5328972977d.png

      对比传统多层或三层.net web架构,下图是当前流行的.net web微服务架构,在web程序分层之上还包含了容器,web api网关,各服务对应的数据存储(sqlserver,redis,mongoDB),web程序有web api并结合应用了DDD\CQRS分层模式,以及系统各种中间件。

    12ee06bb4a25d7e99bd73beeb97b7788.png

      下图是一个订单微服务站点,包含了简化的cqrs分层,蓝色长方格是表示cqrs分层的职责,包括了查询 Queries viewModels和命令Command Domain-Model以及上层的应用服务层Application,如下所示

    2e2d81fbcc5b14bc4024350b7b8b13f7.png

      1.1 逻辑分层设计架构类型

        (1) 最传统的分层是经典三层设计,包括表现层,业务层,数据层.

        (2) 基于服务的解决方案SOA,公开应用程序业务功能的服务层,服务层在业务层之上。

        (3) 基于领域驱动设计的DDD\CQRS分层模式

        (4) 微服务架构。

         这4种web分层架构是不断的演化改变 ,每一种分层架构并不是独立的思想,它包含了演化之前的架构分层思想。从以前三层架构到现在的微服务架构,是适应每个时代互联网业务实现的需求。

    功能

    SOA

    微服务

    组件大小

    大块业务逻辑

    单独任务或小块业务逻辑

    耦合

    通常松耦合

    总是松耦合

    公司架构

    任何类型

    小型、专注于功能交叉团队

    管理

    着重中央管理

    着重分散管理

    目标

    确保应用能够交互操作

    执行新功能、快速拓展开发团队

    二.Web分层设计步骤

      1.分层策略
        (1)分层粒度是确定分层策略的关键第一步.
        (2)在逻辑分层中, 多层是在同一进程中运行,这可以利用更高性能的通信机制。例如通过组件接口直接调用。必须小心保持层之间的封装和松散耦合。
        (3)在物理分层中,确定合适的通信机制,该机制考虑到通信延迟并保持之间的松散耦合。
        (4)多层中,考虑它们之间如何相互影响,将确保性能和灵活性之间的良好平衡。

      2.确定需要层
        最常用的方法是将表现层,服务层,业务层和数据访问层功能分离到单独的层中。某些应用程序还引入了各种组件像缓存、日志、消息队列等。

      3.决定如何分发各层和组件
        对于web体系架构,一般都是在一个物理层,只有在必要时,才应在不同的物理层上分布层和组件。这是实现分布式部署的常见原因包括安全策略,物理约束,共享业务逻辑和可伸缩性。

      4.确定是否需要折叠层
        一般规则是您应始终将功能分组到层中。在某些情况下,一个层可以充当代理或传递层,提供封装和松散耦合,而不提供大量功能。但是,通过分离该功能,您可以稍后对其进行扩展,而对设计中的其他层几乎没有影响,如:应用服务层。

      5.确定层之间引用的规则
        在分层策略时,您必须定义层如何相互交互的规则(交互是指:各层引用的关系)。指定交互规则的主要原因是最小化依赖性并消除循环引用。因此应该遵循以下方法之一:
        (1)自上而下的交互
          较高级别的层可以与下面的层交互,但是较低级别的层不应该与上面的层交互。此规则将帮助您避免层之间的循环依赖关系,以及要降低层之间的依赖性。
        (2)严格的互动
          每个层必须仅与下面的层进行交互。此规则将强制严格分离关注点,其中每个层仅知道下面的层。此规则的好处是对层界面的修改只会影响上面的层。如果您正在设计一个将随着时间的推移进行修改以引入新功能并且您希望最大限度地减少这些更改的影响的应用程序,或者您正在设计可能分布在不同物理层上的应用程序,请考虑使用此方法。
        (3)松散的互动
          较高级别的层可以绕过层直接与较低级别的层交互。这可以提高性能,但也会增加依赖性。换句话说,对较低级别层的修改可以影响上面的多个层。

        下图是一个示例:该web架构示例是使用了 cqrs 模式,涉及到了事件源es, 事件源实现本因该分离到命令域和查询域, 而项目中应用服务层直接引用了底层数据访问层Dapper(绕过了领域层),  这样底层Dapper接口方法的修改或换成EF将影响顶层应用服务层,这属于第三种"松散的互动"。 应该推荐使用第一种自上而下的交互。

    ee77791e79a7b4ca2109d5c3dd9844d5.png

      6.确定跨领域问题
        定义层后,必须标识跨越层的功能。此功能通常被描述为横切关注点,包括日志记录,缓存,验证,身份验证和异常管理。确定应用程序中的每个横切关注点非常重要,并设计单独的组件以尽可能地管理这些问题。此方法可帮助您实现更好的可重用性和可维护性。
        如下图所示:NLog与Redis是具体实现组件,实现了Common层中的日志和缓存接口,Common层就是横切组件,是跨层可重用的。像Ioc也是横切组件。下图层的名称没有标识跨越层的功能,如果是GFNetCore.Infra.CrossCutting.IoC 这样命名会更好。

    aff69fad6ca19a84f191e816243ea6fd.png

      7.定义层之间的接口
        为层定义接口时,主要目标是强制层之间的松散耦合。这意味着层不应暴露另一层可能依赖的内部细节。相反,层的接口应设计为通过提供隐藏层内组件细节的公共接口来最小化依赖性。这种隐藏称为抽象,有许多不同的方法来实现它。以下设计方法可用于定义层的接口:

        (1)抽象接口
          通过定义抽象基类或接口类来实现,该类充当具体类的类型定义。该类型定义了一个公共接口,该层的所有使用者都使用该接口与该层进行交互。这是一种面向接口编程,例如:表现层调用应用服务层的接口。表现层在CustomerController控制器中如下所示(通过依赖注入后,构造方法来实例):

         //表现层调用应用服务层ApplicationServiceprivate readonly ICustomerAppService _customerAppService;public CustomerController(ICustomerAppService customerAppService, 
    INotificationHandler
    notifications) : base(notifications)
    {
    _customerAppService
    = customerAppService;
    }

          但在项目中,为了简化开发量,表现层调用应用服务层的实现类(违反了面向接口编程)。表现层在CustomerController控制器中如下所示:

            //调用应用服务层ApplicationServiceprivate readonly CustomerAppService _customerAppService = null;//日志对象public readonly ILoggerEX _logger;public CustomerController(INotificationHandler notifications,
    ILoggerEX logger,CustomerAppService customerAppService)
    :
    base(notifications)
    {
    _customerAppService
    = customerAppService;
    _logger
    = logger;
    }

        (2)依赖倒置

          这是一种编程风格,是面向接口编程的实现,依赖倒置的应用如:DDD\CQRS, 层依赖于层接口,而不是层依赖于另一个层的实现。依赖注入模式是依赖性反转的常见实现。依赖性反转方法提供了灵活性,可以帮助实现可插入设计,因为依赖性是通过配置而不是代码组成的。它还可以最大化可测试性,因为您可以轻松地将具体测试类注入到设计的不同层中。

          依赖性是通过配置的,如下代码所示,由CommandRepository类来实现ICommandRepository接口:

            services.AddScoped, CommandRepository>();
    services.AddScoped
    , QueryRepository>();

        (3) 基于消息

          可以使用基于消息的通信来实现接口并提供层之间的交互,.net技术如:wcf, web services, msmq它们支持跨物理和进程边界的交互(以xml的soap格式传输),但这是对于09年流行的web架构。现在基于消息多数用web api技术,是面向微服务开发(以json的rest api)。

     参考资料

       分层应用程序指南 

    原文链接:https://www.cnblogs.com/MrHSR/p/11272400.html

    .NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

    bc9d8c0bb5070215f3c28583f096ec6f.png

    展开全文
  • 花阴偷移cnblogs.com/MrHSR/p/11272400.html一、概述本章Web架构分层指南,参考了"Microsoft应用程序体系结构指南"(该书是在2009年出版,当时出版是为了帮助开发人员和架构师更快速,更低风险地使用Microsoft平台...

    (给DotNet加星标,提升.Net技能)

    转自:花阴偷移cnblogs.com/MrHSR/p/11272400.html

    一、概述

    本章Web架构分层指南,参考了"Microsoft应用程序体系结构指南"(该书是在2009年出版的,当时出版是为了帮助开发人员和架构师更快速,更低风险地使用Microsoft平台和.NET Framework设计和构建有效,高质量的应用程序)。

    虽然已过去十年了,技术架构已更新(如流行的DDD/CQRS模式,微服务,容器),但web分层思想还是一样可取,下面是一个"传统N分层设计"架构图,该架构在2010年左右是最流行的,包括了表现层presentation,服务层services,业务层business,数据访问层data,横切关注点cross,如下所示:

    1d0dfa4935d5fab5cbaa84ccc3b166e1.png

    对比传统多层或三层.net web架构,下图是当前流行的.net web微服务架构,在web程序分层之上还包含了容器,web api网关,各服务对应的数据存储(sqlserver,redis,mongoDB),web程序有web api并结合应用了DDD\CQRS分层模式,以及系统各种中间件。

    a48225c8a6b6f9f331af215424cc16bd.png

    下图是一个订单微服务站点,包含了简化的cqrs分层,蓝色长方格是表示cqrs分层的职责,包括了查询 Queries viewModels和命令Command Domain-Model以及上层的应用服务层Application,如下所示

    0ea51d1c6578494acd1bd6405a9ba07f.png

    1.1、逻辑分层设计架构类型

    1. 最传统的分层是经典三层设计,包括表现层,业务层,数据层.

    2. 基于服务的解决方案SOA,公开应用程序业务功能的服务层,服务层在业务层之上。

    3. 基于领域驱动设计的DDD\CQRS分层模式

    4. 微服务架构。

    这4种web分层架构是不断的演化改变 ,每一种分层架构并不是独立的思想,它包含了演化之前的架构分层思想。从以前三层架构到现在的微服务架构,是适应每个时代互联网业务实现的需求。

    82bcbc86bcb1fc1238af7f0280ea3044.png

    二、Web分层设计步骤

    1、分层策略

    (1)、分层粒度是确定分层策略的关键第一步.

    (2)、在逻辑分层中, 多层是在同一进程中运行,这可以利用更高性能的通信机制。例如通过组件接口直接调用。必须小心保持层之间的封装和松散耦合。

    (3)、在物理分层中,确定合适的通信机制,该机制考虑到通信延迟并保持之间的松散耦合。

    (4)、多层中,考虑它们之间如何相互影响,将确保性能和灵活性之间的良好平衡。

    2、确定需要层

    最常用的方法是将表现层,服务层,业务层和数据访问层功能分离到单独的层中。某些应用程序还引入了各种组件像缓存、日志、消息队列等。

    3、决定如何分发各层和组件

    对于web体系架构,一般都是在一个物理层,只有在必要时,才应在不同的物理层上分布层和组件。这是实现分布式部署的常见原因包括安全策略,物理约束,共享业务逻辑和可伸缩性。

    4、确定是否需要折叠层

    一般规则是您应始终将功能分组到层中。在某些情况下,一个层可以充当代理或传递层,提供封装和松散耦合,而不提供大量功能。但是,通过分离该功能,您可以稍后对其进行扩展,而对设计中的其他层几乎没有影响,如:应用服务层。

    5、确定层之间引用的规则

    在分层策略时,您必须定义层如何相互交互的规则(交互是指:各层引用的关系)。指定交互规则的主要原因是最小化依赖性并消除循环引用。因此应该遵循以下方法之一:

    (1)、自上而下的交互

    较高级别的层可以与下面的层交互,但是较低级别的层不应该与上面的层交互。此规则将帮助您避免层之间的循环依赖关系,以及要降低层之间的依赖性。

    (2)、严格的互动

    每个层必须仅与下面的层进行交互。此规则将强制严格分离关注点,其中每个层仅知道下面的层。此规则的好处是对层界面的修改只会影响上面的层。如果您正在设计一个将随着时间的推移进行修改以引入新功能并且您希望最大限度地减少这些更改的影响的应用程序,或者您正在设计可能分布在不同物理层上的应用程序,请考虑使用此方法。

    (3)、松散的互动

    较高级别的层可以绕过层直接与较低级别的层交互。这可以提高性能,但也会增加依赖性。换句话说,对较低级别层的修改可以影响上面的多个层。

    下图是一个示例:该web架构示例是使用了 cqrs 模式,涉及到了事件源es, 事件源实现本因该分离到命令域和查询域, 而项目中应用服务层直接引用了底层数据访问层Dapper(绕过了领域层),  这样底层Dapper接口方法的修改或换成EF将影响顶层应用服务层,这属于第三种"松散的互动"。应该推荐使用第一种自上而下的交互。

    7174ef301777d54f76ececb4826eb4e7.png

    6、确定跨领域问题

    定义层后,必须标识跨越层的功能。此功能通常被描述为横切关注点,包括日志记录,缓存,验证,身份验证和异常管理。确定应用程序中的每个横切关注点非常重要,并设计单独的组件以尽可能地管理这些问题。此方法可帮助您实现更好的可重用性和可维护性。

    如下图所示:NLog与Redis是具体实现组件,实现了Common层中的日志和缓存接口,Common层就是横切组件,是跨层可重用的。像Ioc也是横切组件。下图层的名称没有标识跨越层的功能,如果是GFNetCore.Infra.CrossCutting.IoC 这样命名会更好。

    359621318c8e56e577c0b0d69e64f58a.png

    7、定义层之间的接口

    为层定义接口时,主要目标是强制层之间的松散耦合。这意味着层不应暴露另一层可能依赖的内部细节。相反,层的接口应设计为通过提供隐藏层内组件细节的公共接口来最小化依赖性。这种隐藏称为抽象,有许多不同的方法来实现它。以下设计方法可用于定义层的接口:

    (1)、抽象接口

    通过定义抽象基类或接口类来实现,该类充当具体类的类型定义。该类型定义了一个公共接口,该层的所有使用者都使用该接口与该层进行交互。这是一种面向接口编程,例如:表现层调用应用服务层的接口。表现层在CustomerController控制器中如下所示(通过依赖注入后,构造方法来实例):

    //表现层调用应用服务层ApplicationService
    private readonly ICustomerAppService _customerAppService;

    public CustomerController(ICustomerAppService customerAppService,
    INotificationHandler notifications
    ) : base(notifications)
    {
    _customerAppService = customerAppService;
    }

    但在项目中,为了简化开发量,表现层调用应用服务层的实现类(违反了面向接口编程)。表现层在CustomerController控制器中如下所示:

    //调用应用服务层ApplicationService
    private readonly CustomerAppService _customerAppService = null;

    //日志对象
    public readonly ILoggerEX _logger;

    public CustomerController(INotificationHandler notifications,
    ILoggerEX logger,
    CustomerAppService customerAppService
    )
    : base(notifications)
    {
    _customerAppService = customerAppService;
    _logger = logger;
    }

    (2)、依赖倒置

    这是一种编程风格,是面向接口编程的实现,依赖倒置的应用如:DDD\CQRS, 层依赖于层接口,而不是层依赖于另一个层的实现。依赖注入模式是依赖性反转的常见实现。依赖性反转方法提供了灵活性,可以帮助实现可插入设计,因为依赖性是通过配置而不是代码组成的。它还可以最大化可测试性,因为您可以轻松地将具体测试类注入到设计的不同层中。

    依赖性是通过配置的,如下代码所示,由CommandRepository类来实现ICommandRepository接口:

    servicesCommandRepository<QueryRepository<

    (3)、基于消息

    可以使用基于消息的通信来实现接口并提供层之间的交互,.NET技术如:wcf, web services, msmq它们支持跨物理和进程边界的交互(以xml的soap格式传输),但这是对于09年流行的web架构。现在基于消息多数用web api技术,是面向微服务开发(以json的rest api)。

    推荐阅读

    (点击标题可跳转阅读)

    C#高级语法之泛型、泛型约束,类型安全、逆变和协变

    .NET Core WebAPI文件分片上传与跨域请求处理

    .NET Core最优MD5打开方式

    看完本文有收获?请转发分享给更多人

    关注「DotNet」加星标,提升.Net技能 

    b333a9803857e4563310cedddb38f37a.png

    好文章,我在看❤️

    展开全文
  • 这里所说的三层体系,不是指物理上的三层,不是简单地放置三台机器就是三层体系结构,也不仅仅有B/S应用才是三层体系结构,三层是指逻辑上的三层,即使这三个层放置到一台机器上。 三层体系的应用程序将业务规则、...
  • 目录 编写Kurento应用程序 全局架构 通讯客户端,服务器和Kurento 媒体协商阶段(信令) ...在最高抽象级别上,Web应用程序的体系结构个不同的层组成: 表示(客户端):在这里,我们可以找到...
  • 第一章 数据库应用程序WebWeb三层体系结构第二章 PHP脚本编程语言PHP简介条件与分支循环函数类型用户自定义函数PHP运作实例第三章 PHP高级数据控制功能数组字符串正则表达式日期与时间整数与浮点数第四章PHP 5...
  • 书名: 使用Visual Basic和UML开发应用程序 <br>出版社:清华大学出版社 <br>页数:345 <br>内容提要: <br> 本书结合作者在面向对象客户/服务器系统开发方面丰富经验,介绍了如何在Visual Basic中用 ...
  • 文章目录一、应用协议的原理网络应用程序体系结构进程通信可供应用程序使用的运输服务因特网提供的运输服务二、应用协议的实现过程Web应用和HTTP协议HTTP概况非持续连接和持续连接非持续持续HTTP的报文格式...
  • 模型 – 视图 – 控制器或MVC,MVC是普遍叫法,是一种软件设计模式,用于开发Web应用程序。模型- 视图 – 控制器模式是由以下部分组成: 模型/Model – 一个负责维护数据模式最低水平。 视图/View – 负责显示...
  •  随着Web应用的商业逻辑包含逐渐复杂公式分析计算、决策支持等,使客户机越 来越不堪重负,因此将系统商业分离出来。单独形成一部分,这样三层结构产生了。 其中‘层’是逻辑上划分。[1-3]   体系结构 ...
  • 使用React进行全栈Web开发 使用React和Coursera专业化进行全栈Web开发。 本专业包含4门课程:前端Web UI框架和工具:引导4,使用React进行前端... Web开发的三层体系结构: 表示层与向用户交付有关,因此,这通常是与
  • 一、基于Java技术的Web应用体系结构整个网站系统采用Browser/Web/DataBase的三层体系结构。Web服务器接受请求,通过应用程序服务器执行一个Java服务器端小程序Servlet并返回其输出,从而实现与客户机进行信息资源的...
  • * * 第 25 章 基于Web的综合档案管理系统 本章综合第192021和22章中的模块以一个真实的Web项目综合档案管理系统的开发实战详细介绍了使用实体类的三层体系结构应用程序开发过程介绍了需求及功能分析数据库和实体类...
  • 第二个内容是你要搞清楚Oracle应用程序背后架构体系,也就是说你要明白诸如以下产品的结构体系:Oracle电子商务套件、Oracle 11i数据库、Siebel产品等。 本文首先讲述如何从一个普通Oracle DBA转变为一个Oracle...
  • 程序员在这一步需要考虑应用程序使用什么样的体系结构,是使用传统的Client/Server两 层体系结构,还是使用Browser/Web/Database的三层体系结构。不同的应用程序体系结构要求的数据库资源是不同的。 3、调整数据库...
  • SQL性能优化应该考虑哪些?

    千次阅读 2015-03-05 22:19:47
    1、调整数据结构的设计。...这一部分也是在开发信息系统之前完成,程序员在这一步需要考虑应用程序使用什么样的体系结构,是使用传统的Client/Server两层体系结构,还是使用Browser/Web/Database的三层体系结构
  • 关联知识MatplotlibPython介绍Matplotlib是一个跨平台Python库,用于绘制二维图形(也称为图)。 它可以在各种用户界面中使用,例如Python脚本,IPython Shell,Jupyter Notebook...Matplotlib具有三层体系结构:...
  • 关联知识 Matplotlib Python 介绍 Matplotlib是一个跨平台Python库,用于绘制二...Matplotlib具有三层体系结构:后端,美工和脚本,以逻辑方式组织为堆栈。 脚本是开发人员用来创建图形API。 美工完成了内部创建
  • 在表示集成.NET

    2021-03-02 20:17:04
    本页内容简介使用Web服务来达到互用性使用JNBridgePro来实现互用性使用Ja.NET来实现互用性小结参考资料 简介您现在应该了解XBikes应用程序体系结构和XBikes使用的互用性技术。在本章中,您可以查看允许XBikes开发...
  •  4.3 MVC体系结构  4.3.1 MVC理论  4.3.2 MVCRails方式  4.3.3 ActiveRecord(模型)  4.3.4 ActionPck模块  4.4 代码生成  4.5 ActionMailer  4.6 测试和调试  4.6.1 测试  4.6.2 调试  4.7...
  • 1、1、调整数据结构的...这一部分也是在开发信息系统之前完成,程序员在这一步需要考虑应用程序使用什么样的体系结构,是使用传统的Client/Server两层体系结构,还是使用Browser/Web/Database的三层体系结构。不同的
  • 实验2.4 进程相关的应用程序设计28 综合实验一嵌入式平台进程管理模拟实验33 第3章 嵌入式Linux内核、引导系统和文件系统36 3.1 Linux内核定制、裁剪和添加36 3.1.1 概述36 3.1.2 内核目录简介37 3.1.3 配置...

空空如也

空空如也

1 2 3 4 5 ... 15
收藏数 293
精华内容 117
关键字:

web应用程序使用的三层体系结构