Как я научился выдавать файл clientaccesspolicy.xml без IIS
Недавно один разработчик обратился ко мне и Мише с вопросом.
Суть вопроса такова.
Есть silverlight клиент, есть WCF сервис. Сервис доступен, скажем, по адресу http://localhost:8731/classic с wsHttpBinding или даже basicHttpBinding.
Сервис self hosted -ну то есть без всякого IIS в консольном приложении или сервисе есть код, который запускает сервис. Как то так.
Code Snippet
- ServiceHost sh = new ServiceHost(typeof(Service1));
- sh.Open();
- Console.WriteLine("up and running");
- Console.ReadLine();
- sh.Close();
Ну и есть собственно сервис, в моем примере это IService2 и класс Service1.
Code Snippet
- // Classical wsHttpBinding or basicHttpBinding
- [ServiceContract]
- public interface IService2
- {
- [OperationContract]
- int DoAdd(int a, int b);
- }
- public class Service1 : IService2
- {
- public int DoAdd(int a, int b)
- {
- return a + b;
- }
- \
Конфиг выглядит примерно так
Code Snippet
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <system.web>
- <compilation debug="true" />
- </system.web>
- <!-- When deploying the service library project, the content of the config file must be added to the host's
- app.config file. System.Configuration does not support config files for libraries. -->
- <system.serviceModel>
- <services>
- <service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
- name="WcfServiceLibrary1.Service1">
- <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
- <endpoint address="classic" binding="basicHttpBinding" contract="WcfServiceLibrary1.IService2" />
- <host>
- <baseAddresses>
- <add baseAddress="http://localhost:8731/" />
- </baseAddresses>
- </host>
- </service>
- </services>
- <behaviors>
- <serviceBehaviors>
- <behavior name="WcfServiceLibrary1.Service1Behavior">
- <serviceMetadata httpGetEnabled="true" />
- <serviceDebug includeExceptionDetailInFaults="true" />
- </behavior>
- </serviceBehaviors>
- </behaviors>
- </system.serviceModel>
- </configuration>
Задача – сделать так чтобы по адресу http://localhost:8731/clientacccesspolicy.xml выдавался нужный нам файл.
Решение
Воспользуемся возможностями .NET 3.5 по работе с REST.
1) Добавим контракт, который будет реализовывать выдачу файла.
Code Snippet
- // This service exposes operations via REST
- [ServiceContract]
- public interface IService1
- {
- [OperationContract]
- [WebGet(UriTemplate = "/clientacccesspolicy.xml")]
- Stream GetClientPolicy();
- }
Обратим внимание на параметр URITemplate – этот параметр описывает, как должен выглядеть WEB запрос.
2) Добавим настройки в конфиг файл. Нам нужно по адресу http://localhost:8731 добавить endpoint с webBinding, а также прописать endpointBehavior. Можно сделать из утилиты WCF Service Configuraiton Editor, можно руками.
Итак, что пришлось добавить.
Code Snippet
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <system.serviceModel>
- <services>
- <service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
- name="WcfServiceLibrary1.Service1">
- <endpoint address="" behaviorConfiguration="webHttp" binding="webHttpBinding"
- contract="WcfServiceLibrary1.IService1">
- </endpoint>
- .....
- </service>
- </services>
- <behaviors>
- <endpointBehaviors>
- <behavior name="webHttp">
- <webHttp />
- </behavior>
- </endpointBehaviors>
- ....
- </behaviors>
- </system.serviceModel>
- </configuration>
Итоговый конфиг
Code Snippet
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <system.web>
- <compilation debug="true" />
- </system.web>
- <!-- When deploying the service library project, the content of the config file must be added to the host's
- app.config file. System.Configuration does not support config files for libraries. -->
- <system.serviceModel>
- <services>
- <service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
- name="WcfServiceLibrary1.Service1">
- <endpoint address="" behaviorConfiguration="webHttp" binding="webHttpBinding"
- contract="WcfServiceLibrary1.IService1">
- </endpoint>
- <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
- <endpoint address="classic" binding="basicHttpBinding" contract="WcfServiceLibrary1.IService2" />
- <host>
- <baseAddresses>
- <add baseAddress="http://localhost:8731/" />
- </baseAddresses>
- </host>
- </service>
- </services>
- <behaviors>
- <endpointBehaviors>
- <behavior name="webHttp">
- <webHttp />
- </behavior>
- </endpointBehaviors>
- <serviceBehaviors>
- <behavior name="WcfServiceLibrary1.Service1Behavior">
- <serviceMetadata httpGetEnabled="true" />
- <serviceDebug includeExceptionDetailInFaults="true" />
- </behavior>
- </serviceBehaviors>
- </behaviors>
- </system.serviceModel>
- </configuration>
То есть мы сказали WCF, что запросы по адресу http://localhost:8731 обрабатывает webHttpBinding, а по адресу http://localhost:8731/classic – классический binding. (я взял wsHttpBinging, вроде говорят что для silverlight нужен другой).
3) Осталось написать реализацию. Я положи нужный мне XML в ресурсы, чтобы код не загромождать.
Code Snippet
- public Stream GetClientPolicy()
- {
- byte[] buffer = null;
- using (MemoryStream ms = new MemoryStream())
- {
- ms.Position = 0;
- using (StreamWriter sw = new StreamWriter(ms))
- {
- sw.WriteLine(Resource1.crossdomainpolicy1);
- }
- buffer = ms.GetBuffer();
- }
- WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml";
- return new MemoryStream(buffer);
- }
Обратим внимание на следующие моменты.
С помощью специального класса WebOperationContext, содержащего настройки специфичные для web запроса и ответа, я настроил правильный тип содержимого.
Второе – я зачем то занимаюсь шаманством с закрытием и открытием потока. Объясняю.
Метод требует возврата ОТКРЫТОГО потока – поток закрывается сам дальше. Но у меня же есть еще и StreamWriter, который я честно пытаюсь закрыть! А он берет и закрывает нижележащий поток (MemoryStream). Подумал я , да и решил закрыть StreamWriter, MemoryStream , а потом открыть заново.
ВСЁ! Не так страшно и сложно.
Итог – у меня есть сервис, который замечательно выдает clientaccesspolicy.xml, а также работает как обычный сервис.
пример кода приложен.
Ссылки
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