文章出處

本文主要來自MSDN雜志《Building Cross-Platform Web Services with ServiceStack》,Windows Communication Foundation (WCF) 是一個相當優秀的服務框架,當我們討論跨平臺的服務的時候,雖然WCF對WebService的支持還行,在面對一些高級應用的不太好,微軟重新發展了ASP.NET WebAPI框架,關于這兩個框架的討論可以看我另外一篇文章《WCF和ASP.NET Web API在應用上的選擇》 。在討論跨平臺的Web服務上,ASP.NET Web API是一個重要選項,在本文中,我將展示如何利用 ServiceStack (開放源代碼.NET 和Mono REST 服務框架) 來完成這一任務,不用離開 Visual Studio 或 Microsoft.NET/Mono,除了 ServiceStack 之外還有個Nancy的框架,具體可以看《.NET的微型Web框架 Nancy》。

一個典型的 Web 服務結構如下:

Print

  • 服務層是您定義您的Web 服務接口的地方。 這也是,客戶端和你的 Web 服務進行交互的一層。
  • 業務層通常是業務邏輯
  • 數據層是為了封裝數據訪問和操縱在業務層提供抽象的數據模型。
  • Web服務通常有遠程過程調用(RPC)和RESTful (HTTP)兩類,現在占據主導地位的Web服務是RESTful (HTTP),具體內容可以參看文章《REST在企業中獲得成功了么?》,貼一張文章里的圖片:

REST&SOAP1

2年前REST就已經成為Web API部署方式的主流了,而且一直保持這種發展勢頭,現在基本上都是REST服務,SOAP在企業內網還存在。

遠程過程調用 (RPC) ,每個請求旨在類似于函數調用:

public interface IService

{

      string DoSomething(int input);

}

RPC 方法對服務的修改非常不友好。 例如前面的代碼段,如果要求從客戶端來執行更高版本的 Web 服務的 DoSomething 方法的兩個輸入參數 — 或需要返回字符串值之外的另一個字段 —— 給老客戶重大更改是不可避免的。 當然,您始終可以創建平行的 DoSomething_v2 方法,要帶兩個輸入的參數,但久而久之會搞亂您的 Web 服務接口和消費者,服務變得越來越丑,用WCF實現的Web服務就是屬于這種情況,下面我們介紹ServiceStack。

ServiceStack是.Net和Mono的開源框架,相對WCF,MVC及Web API而言它是開發Web服務與Web應用的有力替代品,它越來越普及。 用 ServiceStack 生成的 web 服務可以運行在 Windows 環境中,.NET 代碼或Mono支持 Linux 環境中。 Mono支持的操作系統包括:

  • Linux
  • Mac OS X, iOS
  • Sun Solaris
  • BSD
  • Microsoft Windows
  • Nintendo Wii
  • Sony PlayStation 3

ServiceStack是一系列事物的綜合體:

ServiceStack 強制遠程 Web 服務最佳實踐、 基于公約 DTO 標準為其 Web 服務接口,ServiceStack 還提供預置的響應狀態對象,可用于撰寫 DTO,鼓勵更加直接和簡單的錯誤處理方案,顯然和WCF是明顯不同的路線。

本文假定您有一些熟悉 WCF 和.NET 框架。 為了更好地展示WCF 概念可以如何轉化為 ServiceStack 的概念,首先會在WCF中實現服務層。我會告訴你如何通過將WCF Web 服務移植到等效的使用 ServiceStack 轉換為跨平臺的 Web 服務。

WCF 使用數據合同建立的客戶端和服務器之間的通信手段。 ServiceStack和WCF相同。 WCF 需要何數據對象和數據成員打上標記; 否則,WCF 簡單地忽略它們。 這是 ServiceStack 和 WCF 與的不同的地方。 ServiceStack 支持所有POCO 的對象作為契約:

WCF的契約:

   [DataContract]
    public class Ticket
    {
        [DataMember]
        public int TicketId { get; set; }
        [DataMember]
        public int TableNumber { get; set; }
        [DataMember]
        public int ServerId { get; set; }
        [DataMember]
        public List<Order> Orders { get; set; }
        [DataMember]
        public DateTime Timestamp { get; set; }
    }
    [ServiceContract]
    public interface ITicketService
    {
        /// <summary>
        /// 檢索當前隊列中的所有門票的完整清單
        /// </summary>
        /// <returns></returns>
        [OperationContract]
        List<Ticket> GetAllTicketsInQueue();

        /// <summary>
        /// 新增新門票
        /// </summary>
        /// <param name="ticket"></param>
        [OperationContract]
        void QueueTicket(Ticket ticket);

        /// <summary>
        /// 從隊列拉出一張票
        /// </summary>
        /// <returns></returns>
        [OperationContract]
        Ticket PullTicket();
    }
}

把它轉換為ServiceStack的契約:

public class Ticket
{
       public int TicketId { get; set; }
        public int TableNumber { get; set; }
        public int ServerId { get; set; }
        public List<Order> Orders { get; set; }
        public DateTime Timestamp { get; set; }

}

public class GetAllTicketsInQueueRequest
{
}

public class QueueTicketRequest
{
    public Ticket Ticket { get; set; }
}

public class PullTicketRequest
{
}

public interface ISCTicketService
{
    List<Ticket> Any(GetAllTicketsInQueueRequest request);

    void Any(QueueTicketRequest request);

    Ticket Any(PullTicketRequest request);
}

ServiceStack 規定每個唯一的請求是對象所標識唯一的請求,這意味著你不能重用 DTO 跨多個服務實現與 ServiceStack 的請求。ServiceStack 支持不同的操作,如有 Get 和 Post。 您的選擇在這里僅影響的 HTTP 請求。 指定任何 Web 服務請求是指可以通過 HTTP GET 和 HTTP POST 調用操作。 這種強制措施,簡化了 rest 風格的 Web 服務實現。要將您的 ServiceStack Web 服務變成 rest 風格的 Web 服務,只需添加 URL [Route(...)]向您的 Web 服務請求聲明屬性。

    //Request DTO
    public class Hello
    {
        public string Name { get; set; }
    }

    //Response DTO
    public class HelloResponse
    {
        public string Result { get; set; }
        public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
    }

    //Can be called via any endpoint or format, see: http://servicestack.net/ServiceStack.Hello/
    public class HelloService : Service
    {
        public object Any(Hello request)
        {
            return new HelloResponse { Result = "Hello, " + request.Name };
        }
    }

    //REST Resource DTO
    [Route("/todos")]
    [Route("/todos/{Ids}")]
    public class Todos : IReturn<List<Todo>>
    {
        public long[] Ids { get; set; }
        public Todos(params long[] ids)
        {
            this.Ids = ids;
        }
    }

    [Route("/todos", "POST")]
    [Route("/todos/{Id}", "PUT")]
    public class Todo : IReturn<Todo>
    {
        public long Id { get; set; }
        public string Content { get; set; }
        public int Order { get; set; }
        public bool Done { get; set; }
    }

    public class TodosService : Service
    {
        public TodoRepository Repository { get; set; }  //Injected by IOC

        public object Get(Todos request)
        {
            return request.Ids.IsEmpty()
                ? Repository.GetAll()
                : Repository.GetByIds(request.Ids);
        }

        public object Post(Todo todo)
        {
            return Repository.Store(todo);
        }

        public object Put(Todo todo)
        {
            return Repository.Store(todo);
        }

        public void Delete(Todos request)
        {
            Repository.DeleteByIds(request.Ids);
        }
    }   

以ASP.NET Hosting承載ServiceStack,創建一個空的ASP.NET應用,使用 NuGet 包管理器控制臺將 ServiceStack 引用添加到 ServiceStack.Host.AspNet中所示

image

Web.config 會增加下面的配置

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
    <httpHandlers>
      <add path="*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" />
    </httpHandlers>
  </system.web>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <add path="*" name="ServiceStack.Factory" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true" />
    </handlers>

  </system.webServer>
</configuration>

你需要從 ServiceStack.WebHost.End 繼承­實現端點。

public class AppHost  : AppHostBase
    {       
        public AppHost() //Tell ServiceStack the name and where to find your web services
            : base("StarterTemplate ASP.NET Host", typeof(HelloService).Assembly) { }

        public override void Configure(Funq.Container container)
        {
            //Set JSON web services to return idiomatic JSON camelCase properties
            ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
       
            //Configure User Defined REST Paths
            Routes
              .Add<Hello>("/hello")
              .Add<Hello>("/hello/{Name*}");

            //Uncomment to change the default ServiceStack configuration
            //SetConfig(new EndpointHostConfig {
            //});

            //Enable Authentication
            //ConfigureAuth(container);

            //Register all your dependencies
            container.Register(new TodoRepository());           
        }

        /* Uncomment to enable ServiceStack Authentication and CustomUserSession
        private void ConfigureAuth(Funq.Container container)
        {
            var appSettings = new AppSettings();

            //Default route: /auth/{provider}
            Plugins.Add(new AuthFeature(() => new CustomUserSession(),
                new IAuthProvider[] {
                    new CredentialsAuthProvider(appSettings),
                    new FacebookAuthProvider(appSettings),
                    new TwitterAuthProvider(appSettings),
                    new BasicAuthProvider(appSettings),
                }));

            //Default route: /register
            Plugins.Add(new RegistrationFeature());

            //Requires ConnectionString configured in Web.Config
            var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
            container.Register<IDbConnectionFactory>(c =>
                new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider));

            container.Register<IUserAuthRepository>(c =>
                new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>()));

            var authRepo = (OrmLiteAuthRepository)container.Resolve<IUserAuthRepository>();
            authRepo.CreateMissingTables();
        }
        */

        public static void Start()
        {
            new AppHost().Init();
        }
    }

ServiceStack Web 應用程序啟動時,您的服務合同列出作為元數據操作,如圖所示:

image

相關文章:

SignalR, Filters and ServiceStack

采訪ServiceStack的項目領導Demis Bellot——第1部分

采訪ServiceStack的項目領導Demis Bellot——第2部分            


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()