本文主要來自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 服務結構如下:
- 服務層是您定義您的Web 服務接口的地方。 這也是,客戶端和你的 Web 服務進行交互的一層。
- 業務層通常是業務邏輯
- 數據層是為了封裝數據訪問和操縱在業務層提供抽象的數據模型。
- Web服務通常有遠程過程調用(RPC)和RESTful (HTTP)兩類,現在占據主導地位的Web服務是RESTful (HTTP),具體內容可以參看文章《REST在企業中獲得成功了么?》,貼一張文章里的圖片:
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是一系列事物的綜合體:
- 包含高性能Razor Engine的Web應用框架
- 支持例如HTML,XML,JSON,SOAP等多種格式的基于消息的Web服務框架
- 包含內建IOC的容器
- 若干內建庫文件,諸如:Text serializer,Redis Client,ORM以及caching providers
- 除了ASP.NET Hosting和Mono Hosting之外,還包含self-hosting選項
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中所示
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 應用程序啟動時,您的服務合同列出作為元數據操作,如圖所示:
相關文章:
SignalR, Filters and ServiceStack
文章列表