Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Недавно один разработчик обратился ко мне и Мише с вопросом.
Суть вопроса такова.
Есть silverlight клиент, есть WCF сервис. Сервис доступен, скажем, по адресу https://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="https://localhost:8731/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WcfServiceLibrary1.Service1Behavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Задача – сделать так чтобы по адресу https://localhost:8731/clientaccesspolicy.xml выдавался нужный нам файл.
Решение
Воспользуемся возможностями .NET 3.5 по работе с REST.
1) Добавим контракт, который будет реализовывать выдачу файла.
Code Snippet
// This service exposes operations via REST
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(UriTemplate = "/clientaccesspolicy.xml")]
Stream GetClientPolicy();
}
Обратим внимание на параметр URITemplate – этот параметр описывает, как должен выглядеть WEB запрос.
2) Добавим настройки в конфиг файл. Нам нужно по адресу https://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="https://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, что запросы по адресу https://localhost:8731 обрабатывает webHttpBinding, а по адресу https://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
https://msdn.microsoft.com/en-us/library/cc681221(VS.100).aspx
Comments
- Anonymous
May 02, 2012
Опечатка в [WebGet(UriTemplate = "/clientacccesspolicy.xml")] - лишняя буква c Если не заметить, можно долго думать почему не работает.