Welcome to MSDN Blogs Sign in | Join | Help

Как я научился выдавать файл clientaccesspolicy.xml без IIS

Недавно один разработчик обратился ко мне и Мише с вопросом.

Суть вопроса такова.

Есть silverlight клиент, есть WCF сервис. Сервис доступен, скажем, по адресу http://localhost:8731/classic с wsHttpBinding или даже basicHttpBinding.

Сервис self hosted -ну то есть без всякого IIS в консольном приложении или сервисе есть код, который запускает сервис. Как то так.

Code Snippet
  1.             ServiceHost sh = new ServiceHost(typeof(Service1));
  2.             sh.Open();
  3.             Console.WriteLine("up and running");
  4.             Console.ReadLine();
  5.             sh.Close();

Ну и есть собственно сервис, в моем примере это IService2 и класс Service1.

Code Snippet
  1. // Classical wsHttpBinding or basicHttpBinding
  2. [ServiceContract]
  3. public interface IService2
  4. {
  5.     [OperationContract]
  6.     int DoAdd(int a, int b);
  7. }
  8. public class Service1 : IService2
  9. {
  10.     public int DoAdd(int a, int b)
  11.     {
  12.         return a + b;
  13.     }
  14. \

Конфиг выглядит примерно так

Code Snippet
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.   <system.web>
  4.     <compilation debug="true" />
  5.   </system.web>
  6.   <!-- When deploying the service library project, the content of the config file must be added to the host's
  7.   app.config file. System.Configuration does not support config files for libraries. -->
  8.   <system.serviceModel>
  9.     <services>
  10.       <service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
  11.         name="WcfServiceLibrary1.Service1">
  12.         <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  13.         <endpoint address="classic" binding="basicHttpBinding" contract="WcfServiceLibrary1.IService2" />
  14.         <host>
  15.           <baseAddresses>
  16.             <add baseAddress="http://localhost:8731/" />
  17.           </baseAddresses>
  18.         </host>
  19.       </service>
  20.     </services>
  21.     <behaviors>
  22.       <serviceBehaviors>
  23.         <behavior name="WcfServiceLibrary1.Service1Behavior">
  24.           <serviceMetadata httpGetEnabled="true" />
  25.           <serviceDebug includeExceptionDetailInFaults="true" />
  26.         </behavior>
  27.       </serviceBehaviors>
  28.     </behaviors>
  29.   </system.serviceModel>
  30. </configuration>

Задача – сделать так чтобы по адресу http://localhost:8731/clientacccesspolicy.xml выдавался нужный нам файл.

Решение

Воспользуемся возможностями .NET 3.5 по работе с REST.

1) Добавим контракт, который будет реализовывать выдачу файла.

Code Snippet
  1.     // This service exposes operations via REST
  2.     [ServiceContract]
  3.     public interface IService1
  4.     {
  5.         [OperationContract]
  6.         [WebGet(UriTemplate = "/clientacccesspolicy.xml")]
  7.         Stream GetClientPolicy();
  8.     }

Обратим внимание на параметр URITemplate – этот параметр описывает, как должен выглядеть WEB запрос.

2) Добавим настройки в конфиг файл. Нам нужно по адресу http://localhost:8731 добавить endpoint с webBinding, а также прописать endpointBehavior. Можно сделать из утилиты WCF Service Configuraiton Editor, можно руками.

Итак, что пришлось добавить.

 

Code Snippet
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.   <system.serviceModel>
  4.     <services>
  5.       <service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
  6.         name="WcfServiceLibrary1.Service1">
  7.         <endpoint address="" behaviorConfiguration="webHttp" binding="webHttpBinding"
  8.           contract="WcfServiceLibrary1.IService1">
  9.         </endpoint>
  10.          .....
  11.       </service>
  12.     </services>
  13.     <behaviors>
  14.       <endpointBehaviors>
  15.         <behavior name="webHttp">
  16.           <webHttp />
  17.         </behavior>
  18.       </endpointBehaviors>
  19.        ....
  20.       </behaviors>
  21.   </system.serviceModel>
  22. </configuration>

 

Итоговый конфиг

 

Code Snippet
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.   <system.web>
  4.     <compilation debug="true" />
  5.   </system.web>
  6.   <!-- When deploying the service library project, the content of the config file must be added to the host's
  7.   app.config file. System.Configuration does not support config files for libraries. -->
  8.   <system.serviceModel>
  9.     <services>
  10.       <service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
  11.         name="WcfServiceLibrary1.Service1">
  12.         <endpoint address="" behaviorConfiguration="webHttp" binding="webHttpBinding"
  13.           contract="WcfServiceLibrary1.IService1">
  14.         </endpoint>
  15.         <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  16.         <endpoint address="classic" binding="basicHttpBinding" contract="WcfServiceLibrary1.IService2" />
  17.         <host>
  18.           <baseAddresses>
  19.             <add baseAddress="http://localhost:8731/" />
  20.           </baseAddresses>
  21.         </host>
  22.       </service>
  23.     </services>
  24.     <behaviors>
  25.       <endpointBehaviors>
  26.         <behavior name="webHttp">
  27.           <webHttp />
  28.         </behavior>
  29.       </endpointBehaviors>
  30.       <serviceBehaviors>
  31.         <behavior name="WcfServiceLibrary1.Service1Behavior">
  32.           <serviceMetadata httpGetEnabled="true" />
  33.           <serviceDebug includeExceptionDetailInFaults="true" />
  34.         </behavior>
  35.       </serviceBehaviors>
  36.     </behaviors>
  37.   </system.serviceModel>
  38. </configuration>

 

То есть мы сказали WCF, что запросы по адресу http://localhost:8731 обрабатывает webHttpBinding, а по адресу http://localhost:8731/classic – классический binding. (я взял wsHttpBinging, вроде говорят что для silverlight  нужен другой).

3) Осталось написать реализацию. Я положи нужный мне XML в ресурсы, чтобы код не загромождать.

Code Snippet
  1.         public Stream GetClientPolicy()
  2.         {
  3.             byte[] buffer = null;
  4.             using (MemoryStream ms = new MemoryStream())
  5.             {
  6.                 ms.Position = 0;
  7.                 using (StreamWriter sw = new StreamWriter(ms))
  8.                 {
  9.                     sw.WriteLine(Resource1.crossdomainpolicy1);
  10.                 }
  11.                 buffer = ms.GetBuffer();
  12.             }
  13.             WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml";
  14.             return new MemoryStream(buffer);
  15.         }

Обратим внимание на следующие моменты.

С помощью специального класса WebOperationContext, содержащего настройки специфичные для web запроса и ответа, я настроил правильный тип содержимого.

Второе – я зачем то занимаюсь шаманством с закрытием и открытием потока. Объясняю.

Метод требует возврата ОТКРЫТОГО потока – поток закрывается сам дальше. Но у меня же есть еще и StreamWriter, который я честно пытаюсь закрыть! А он берет и закрывает нижележащий поток (MemoryStream). Подумал я , да и решил закрыть StreamWriter, MemoryStream , а потом открыть заново.

ВСЁ! Не так страшно и сложно.

Итог – у меня есть сервис, который замечательно выдает clientaccesspolicy.xml,  а также работает как обычный сервис.

image

пример кода приложен. 

 Ссылки

ms-help://MS.MSDNQTR.v90.en/wcf_con/html/0283955a-b4ae-458d-ad9e-6fbb6f529e3d.htm

http://msdn.microsoft.com/en-us/library/cc681221(VS.100).aspx

Published Thursday, September 03, 2009 3:55 AM by Marat Bakirov [MSFT]
Filed under: , ,

Attachment(s): ClientAccessViaWCF.zip

Comments

No Comments

Anonymous comments are disabled
 
Page view tracker