有时,开发人员必须完全控制从服务操作返回数据的方式。 当服务操作必须以 WCF 不支持的格式返回数据时,就需要这样做。 本主题讨论如何使用 WCF REST 编程模型创建接收任意数据的服务。
实现服务协定
-
定义服务协定。 接收任意数据的操作必须具有一个类型为 Stream 的参数。 此外,此参数必须为传入请求正文的唯一参数。 本示例中介绍的操作还采用一个文件名参数。 此参数在请求的 URL 中传递。 通过在 WebInvokeAttribute 中指定 UriTemplate 可以指定在 URL 中传递参数。 在这种情况下,用于调用此方法的 URI 以“UploadFile/Some-Filename”结尾。 该 URI 模板的“{filename}”部分指定操作的文件名参数在用于调用该操作的 URI 中进行传递。
[ServiceContract] public interface IReceiveData { [WebInvoke(UriTemplate = "UploadFile/{fileName}")] void UploadFile(string fileName, Stream fileContents); }
-
实现服务协定。 该协定只有一个方法 UploadFile,该方法以流的形式接收包含任意数据的文件。 操作读取该数据流,同时对所读字节数进行计数,然后显示文件名和所读字节数。
public class RawDataService : IReceiveData { public void UploadFile(string fileName, Stream fileContents) { byte[] buffer = new byte[10000]; int bytesRead, totalBytesRead = 0; do { bytesRead = fileContents.Read(buffer, 0, buffer.Length); totalBytesRead += bytesRead; } while (bytesRead > 0); Console.WriteLine("Service: Received file {0} with {1} bytes", fileName, totalBytesRead); } }
承载服务
-
创建用于承载服务的控制台应用程序。
class Program { static void Main(string[] args) { } }
-
在 Main 方法中创建一个变量以保存服务的基址。
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
-
创建服务的一个 ServiceHost 实例,该实例指定服务类和基址。
ServiceHost host = new ServiceHost(typeof(RawDataService), new Uri(baseAddress));
-
添加一个指定协定、WebHttpBinding 和 WebHttpBehavior 的终结点。
host.AddServiceEndpoint(typeof(IReceiveData), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
-
打开服务主机。 现在服务已准备好接收请求。
host.Open(); Console.WriteLine("Host opened");
以编程方式调用服务
-
使用用于调用服务的 URI 创建一个 HttpWebRequest。 在此代码中,基址与 “/UploadFile/Text” 组合在一起。 URI 的 “UploadFile” 部分指定要调用的操作。 URI 的 “Test.txt” 部分指定要传递给 UploadFile 操作的文件名参数。 这两项都映射到应用于操作协定的 UriTemplate。
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(baseAddress + "/UploadFile/Test.txt");
-
将 HttpWebRequest 的 Method 属性设置为 POST,并将 ContentType 属性设置为 “text/plain”。 这会告知服务代码正在发送数据,且数据的格式为纯文本。
req.Method = "POST"; req.ContentType = "text/plain";
-
调用 GetRequestStream() 以获取请求流,创建要发送的数据,将该数据写入请求流,然后关闭请求流。
Stream reqStream = req.GetRequestStream(); byte[] fileToSend = new byte[12345]; for (int i = 0; i < fileToSend.Length; i++) { fileToSend[i] = (byte)('a' + (i % 26)); } reqStream.Write(fileToSend, 0, fileToSend.Length); reqStream.Close();
-
通过调用 GetResponse() 从服务获取响应,然后向控制台显示响应数据。
HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); Console.WriteLine("Client: Receive Response HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
-
关闭服务主机。
host.Close();
下面列出了此示例的完整代码。
using System; using System.Collections.Generic; using System.Text; using System.ServiceModel; using System.ServiceModel.Web; using System.ServiceModel.Description; using System.IO; using System.Net; namespace ReceiveRawData { [ServiceContract] public interface IReceiveData { [WebInvoke(UriTemplate = "UploadFile/{fileName}")] void UploadFile(string fileName, Stream fileContents); } public class RawDataService : IReceiveData { public void UploadFile(string fileName, Stream fileContents) { byte[] buffer = new byte[10000]; int bytesRead, totalBytesRead = 0; do { bytesRead = fileContents.Read(buffer, 0, buffer.Length); totalBytesRead += bytesRead; } while (bytesRead > 0); Console.WriteLine("Service: Received file {0} with {1} bytes", fileName, totalBytesRead); } } class Program { static void Main(string[] args) { string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; ServiceHost host = new ServiceHost(typeof(RawDataService), new Uri(baseAddress)); host.AddServiceEndpoint(typeof(IReceiveData), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior()); host.Open(); Console.WriteLine("Host opened"); HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(baseAddress + "/UploadFile/Test.txt"); req.Method = "POST"; req.ContentType = "text/plain"; Stream reqStream = req.GetRequestStream(); byte[] fileToSend = new byte[12345]; for (int i = 0; i < fileToSend.Length; i++) { fileToSend[i] = (byte)('a' + (i % 26)); } reqStream.Write(fileToSend, 0, fileToSend.Length); reqStream.Close(); HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); Console.WriteLine("Client: Receive Response HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription); host.Close(); } } }