Веб-службынаосновеинтерфейса REST (Representational State Transfer)



История возникновения. Общие сведения

 

Развитие технологий HTTP и Web привело к тому, что сам способ адресации по HTTP стало возможно использовать как некоторый стандартный интерфейс для работы с объектами. Например, методы HTTP – это, фактически, методы получения значения некоторого объекта (GET, POST), его изменения (PUT), выполнения над ним действий, удаления (метод DELETE). Этот факт поспособствовал появлению принципа взаимодействия через HTTP с любыми объектами и интерпретации сервера как службы хранения и управления объектами. 

 

Отличия веб-служб на основе REST и SOAP

 

1) в REST есть некоторый ресурс, к которому можно применять стандартные операции. Путь в строке HTTP – путь к самому ресурсу и его идентификация. В веб-службах SOAP путь дает доступ к самой веб-службе, ее контракту;

2) В REST используется не SOAP (не XML), а HTTP;

3) SOAP полностью базируется на методе POST, который является небезопасным и не идемпотентным, HTTPREST основан на использовании всей инфраструктуры HTTP с возможностью кэширования и выполнения идемпотентных кэшируемых запросов, поэтому более безопасных GET. REST позволяет использовать заголовки управления HTTP – if-match, if-none-match, управление кэшированием.

Если HTTPREST служба возвращает данные, которые редко меняются, то они могут кэшироваться инфраструктурой HTTP и программисту о кэшировании заботиться не нужно. Веб-сервера с такими службами сами будут кэшировать страницы, и для следующих запросов клиентов в случае, если не было превышено время кэширования, будет быстро выдаваться страницы из кэша. Это то, чего нет при запросе POST в SOAP. Ради этой возможности HTTPREST, собственно, и создавался.

4) В REST стоит задача непосредственно указания значений передаваемых параметров в строке адреса. Следовательно, необходим механизм отображения параметров, которые идут в URL, в параметры, имеющиеся у подпрограммы. В случае с C# технология WCF имеет готовые средства, которые позволяют не разбирать вручную заголовки HTTP, а использовать механизм автоматического чтения URL, разбор параметров и передачу их в роли соответствующих параметров метода.

 

Самое главное отличиеREST и SOAP: если SOAP полностью базируется на методе POST, который является небезопасным и не идемпотентным, то HTTP REST основан на использовании всей инфраструктуры HTTP с возможностью кэширования и выполнения идемпотентных кэшируемых запросов, поэтому более безопасных GET.

Пример REST веб-службы

 

Для управления отображением строки URLв параметры методов есть два основных атрибута: WebGetи WebInvoke. WebInvokeуспешнозаменяет WebGet.

 

[ServiceContract(Name = "WeatherService",

Namespace = "http://www.mycompany.com/weather/2010/05/24")]

publicinterfaceIWeatherService

{

[WebGet(UriTemplate = "temperature/get?loc={location}")]

[OperationContract]

double GetTemperature(string location);

 

[WebInvoke(Method = "GET",

   UriTemplate = "weather/get?loc={location}",

   ResponseFormat = WebMessageFormat.Xml)]

[OperationContract]

WeatherInfo GetWeatherInfo(string location);

 

[WebInvoke(Method = "GET",

   UriTemplate = "weather2/get?loc={location}&type={resultType}")]

[OperationContract]

Stream GetWeatherInfo2(string location, string resultType);

 

[WebInvoke(Method = "GET",

   UriTemplate = "weather/feed")]

[OperationContract]

[ServiceKnownType(typeof(Atom10FeedFormatter))]

SyndicationFeedFormatter GetWeatherInfoFeed();

}

 

WebInvoke имеетпараметр Method – этоимяметода HTTP. Можно указать POST, PUT, READ, GET и другие методы. Это тот метод HTTP, который использует клиент.

 

WebGet отличается тем, что в нем сразу предопределен метод – GET. Для него указывается строка шаблона адреса – UriTemplate. Здесь указываются имена параметров. В шаблоне указывается имя (в примере это temperature/), имя метода – get, далее, после знака вопроса, идут параметры запроса.

 

UriTemplate = "temperature/get?loc={location}"В имени параметра используется loc, а не location, потому что в HTTP запросах строка ограничена по размеру, поэтому принято сокращать имена параметров. Сокращение позволяет помещать больше параметров.

В фигурных скобках указывается имя передаваемого параметра. Это имя должно совпадать с именем параметра метода, к которому применяется атрибут. В приведенном примере параметр в атрибуте WebGet {location} будет ассоциирован с соответствующим параметром метода GetTemperature, stringlocation.

Пусть был вызван GetTemperature:

http://localhost/WeatherService/temperature/get?loc=Minsk.

 

temperature будет использоваться для поиска нужного метода, а значения параметров после вопроса будут разобраны и переданы в метод.

 

publicclassWeatherService : IWeatherService

{

privatestring LastLocation;

 

publicdouble GetTemperature(string location)

{

Console.WriteLine("Last location: {0}", LastLocation);

   LastLocation = location;

if (string.Compare(location, "Minsk", true) == 0)

return +15;

elseif (string.Compare(location, "Vitebsk", true) == 0)

return +13;

else

returndouble.MinValue;

}

 

publicWeatherInfo GetWeatherInfo(string location)

{

Console.WriteLine("Last location: {0}", LastLocation);

LastLocation = location;

if (string.Compare(location, "Minsk", true) == 0)

{

var res = newWeatherInfo()

{

Temperature = 15,

Humidity = 50,

Pressure = 760,

};

return res;

}

elseif (string.Compare(location, "Vitebsk", true) == 0)

{

var res = newWeatherInfo()

{

Temperature = 13,

Humidity = 48,

Pressure = 750,

};

return res;

}

else

returnnull;

}

 

<...>

}

 

GetTemperature проверяет пришедшее значение и в зависимости от него возвращает температуру. Рассмотрим, в каком виде клиент получит ответ.

 

staticvoidMain()

{

var host = newWebServiceHost(

typeof(WeatherService),

newUri("http://localhost/WeatherService/"));

host.Open();

Console.WriteLine("Press ENTER to stop the service");

Console.ReadLine();

}

 

ДляREST-службнужноиспользоватьнеServiceHost, аWebServiceHost. WebServiceHost отличается тем, что обеспечивает возможность создания точки доступа для прослушивания конкретного HTTP адреса.

 

В параметрах конструктора указывается тип данных службы и URL, на котором разместится служба. Драйвер HTTP обеспечивает возможность прослушивания на одном и том же порту (80й в примере) запросов разными программами – несколько программ могут слушать запрос. Для этого внутри HTTP реализована концепция мультиплексирования запросов в зависимости от строки адреса. Эта же концепция использовалась в примере с ручным конфигурированием SOAP веб-службы.

Фактически здесь происходит регистрация на 80м порту указанного URL, указание которого в строке обеспечит дальнейшую маршрутизацию запросов HTTP программе, которая зарегистрировала свой метод прослушивания входящих запросов. В этом ключевое отличие HTTP от TCP. В TCP, который предоставляется ОС, такой возможности нет, т.к. в нем никак не оговорено содержимое запроса. Единственная точка подключения при доступе по TCP – IP адрес и номер порта. В HTTP всегда, кроме этих данных, после установки соединения происходит передача запроса с заголовками, где обязательно есть заголовок запроса, в котором указывается метод и URL. Он и используется для маршрутизации и мультиплексирования соединения.

 

Метод Mainобязательно должен быть запущен с правами администратора, иначе операционная система не даст зарегистрировать службу.

Результат выполнения запроса

 

После запуска метода Main можно открыть браузер и выполнить запрос к методу: http://localhost/WeatherService/temperature/get?loc=Minsk. Результат – XML:

 

<?xml version="1.0"?>

<doublexmlns="http://schemas.microsoft.com/2003/10/Serialization/">

15

</double>

 

В IWeatherService такжеестьметод GetWeatherInfo:

 

[WebInvoke(Method = "GET",

UriTemplate = "weather/get?loc={location}",

ResponseFormat = WebMessageFormat.Xml)]

[OperationContract]

WeatherInfo GetWeatherInfo(string location);

 

ЭтотметодвозвращаетобъектWeatherInfo. Объект WeatherInfo будет сериализован в XML.

 

[XmlRoot]

[DataContract]

publicclassWeatherInfo

{

public WeatherInfo()

{

}

 

[XmlAttribute]

[DataMember]

publicdouble Temperature { get; set; }

 

[XmlAttribute]

[DataMember]

publicdouble Humidity { get; set; }

 

[XmlAttribute]

[DataMember]

publicdouble Pressure { get; set; }

}

 

Результат вызова метода GetWeatherInfo:

 

http://localhost/WeatherService/weather/get?loc=Minsk.

 

<?xmlversion="1.0"?>

<WeatherInfoxmlns="http://schemas.datacontract.org/2004/07/WcfServiceLibrary1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

<Humidity>50</Humidity>

<Pressure>760</Pressure>

<Temperature>15</Temperature>

</WeatherInfo>

 

Стандартное преобразование предполагает, что свойства превращаются в XML элементы, несмотря на то, что они помечены как XmlAttribute.

Возникает вопрос: можно ли изменить ответ на свой XML? Это легко реализуемо – выводом можно точно и гибко управлять. Кроме метода GetWeatherInfo, в интерфейсе определен еще один метод – GetWeatherInfo2.

 

[WebInvoke(Method = "GET",

UriTemplate = "weather2/get?loc={location}&type={resultType}")]

[OperationContract]

Stream GetWeatherInfo2(string location, string resultType);

 

 

Параметры метода GetWeatherInfo2: location (loc), resultType (указывает, в каком формате нужно возвращать результат). Результат возвращается в виде типа данных Stream. Если результат имеет такой тип данных, содержимое этого потока будет возвращено клиенту как есть. Если в поток (HTTPResponse) был записан XML, клиент получит XML, если – HTML, то клиент получит HTML и так далее. 

 

[ServiceBehavior(

InstanceContextMode = InstanceContextMode.PerSession,

ConcurrencyMode = ConcurrencyMode.Multiple)]

publicclassWeatherService : IWeatherService

{

privatestring LastLocation;

 

<...>

 

publicStream GetWeatherInfo2(string location, string resultType)

{

switch (resultType)

   {

case"html":

return GetWeatherInfoAsHtml(location);

case"text":

return GetWeatherInfoAsText(location);

case"json":

return GetWeatherInfoAsJson(location);

case"xml":

default:

return GetWeatherInfoAsCustomXml(location);

}

}

}

 

 

privateStream GetWeatherInfoAsCustomXml(string location)

{

WeatherInfo res = GetWeatherInfo(location);

if (res != null)

{

var stream = newMemoryStream();

var t = newXmlSerializer(typeof(WeatherInfo));

   t.Serialize(stream, res);

   stream.Seek(0, SeekOrigin.Begin);

return stream;

}

else

returnnull;

}

 

Ни в коем случае нельзя использовать оператор using, т.к. нельзя вызывать метод Dispose для потока. Dispose освободит и закроет поток, а здесь необходимо, чтобы он оставался открытым. Его закрытие выполнит сама WCF после чтения и передачи содержимого.

Форматирование будет применяться уже с учетом XmlAttribute, а не по DataContract.

 

Результатвыполнениязапроса:

 

http://localhost/WeatherService /weather2/get?loc=Minsk&type=xml

 

<?xmlversion="1.0"?>

<WeatherInfoPressure="760" Humidity="50" Temperature="15" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>

Результаты работы других методов в виде таблицы:

 

Строка запроса Результат
http://localhost /WeatherService/weather2/get?loc=Minsk&type=text Temperature: 15 Humidity: 50 Pressure: 760  
http://localhost /WeatherService/weather2/get?loc=Minsk&type=html <html><body> Temperature: 15 Humidity: 50 Pressure: 760 </body></html>
http://localhost /WeatherService/weather2/get?loc=Minsk&type=json {"Humidity":50,"Pressure":760,"Temperature":15}   JSON – JavaScript Simple Object Notation. Результат приходит в виде файла “get” – JSON интерпретируется самим браузером. Код метода GetWeatherInfoAsJson приведен ниже.

 

privateStream GetWeatherInfoAsJson(string location)

{

WeatherInfo res = GetWeatherInfo(location);

if (res != null)

{

var stream = newMemoryStream();

DataContractJsonSerializer s =

newDataContractJsonSerializer(typeof(WeatherInfo));

   s.WriteObject(stream, res);

   stream.Seek(0, SeekOrigin.Begin);

WebOperationContext.Current.OutgoingResponse.ContentType =

"application/json; charset=utf-8";

return stream;

}

else

returnnull;

}

 

 

DataContractJsonSerializerиспользуется, чтобывывестиданныевформатеJSON. Этот объект создается на основе типа данных WeatherInfo и обеспечивает запись в поток объекта, точнее, его свойств. Для использования с DataContractJsonSerializer класс должен иметь атрибут DataContract и размеченные атрибутом DataMember поля.

      

WebOperationContext используется для хранения контекста операций. В нем есть статические методы и статические данные. Свойство Current – объект, который хранит сам контекст. Он нужен только на время работы потока по обработке запроса. Хранятся эти данные в памяти потока. OutgoingResponse – исходящий ответ, кроме него есть IncomingRequest (входящий запрос). Это объекты, где записаны HTTP заголовки. Некоторые из этих заголовков – стандартные, они присутствуют в OutgoingResponse или IncomingRequest как свойства. Примеры таких заголовков – Content-Length и Content-Type. Здесть такой стандартный заголовок, Content-Type, для OutgoingResponse устанавливается в "application/json; charset=utf-8". Это формат данных с указанием кодовой таблицы для их интерпретации, UTF-8. Заголовки IncomingRequest можно менять аналогичным способом.

 

КромеIncomingRequestиOutgoingResponseуCurrentестьсвойстваOutgoingRequestиIncomingResponse. IncomingRequest – свойство, доступное для чтения серверу. OutgoingResponse – тоже свойство сервера, помогающее ему формировать ответ. С OutgoingRequest и IncomingResponse, в свою очередь, работает клиент: в OutgoingRequest он записывает заголовки для своего исходящего запроса, а IncomingResponse – это пришедший емуответ сервера. Это сделано, чтобы служба не могла, получив входящий запрос, сделать для себя же исходящий запрос. Аналогичная ситуация предотвращена и для ответов.

RSS

Общие сведения

 

Современный подход к реализации REST интерфейсов в службах предполагает использование такого формата, как RSS, точнее его реализации Atom 1.0.

 

RSS – это специальный стандартизированный подход для получения заголовков новостей. RSS – это XML, который хранится серверами. Получая по HTTP содержимое этого XML и проверяя стандартные значения тегов, можно отслеживать, поменялось ли что-либо на некотором сайте.

 

Например, стоит задача: имеется сайт, содержащий новости. У сайта есть разделы: спорт, политика, культура, происшествия, финансы. Существуют автоматические агрегаторы новостей: если пользователя интересует раздел финансов, он следит за ним и мало интересуется другими, агрегатор позволяет пользователю видеть не весь список новостей, а только последние (изменившиеся и появившиеся с последнего посещения) новости по интересующим разделам. Кроме того, RSS позволяет не просто получать последние новости, а собирать их из разных источников и формировать тематическую подборку.

           Многие сайты имеют возможность публикации метаинформации о самих себе через RSSfeeds (фиды). Часто сайты имеют значок RSS – это ссылка на XML, которая периодически обновляется. В этом XML помечены все элементы, которые имеются на сайте. Этот подход можно расширить любые объекты – предоставлять их список типов и подтипов, операции над ними, интерфейсы и т.д. То есть метаинформацию RSS можно использовать для описания самой службы.

           Главный недостаток HTTPREST – отсутствие полной информации о возможностей REST службы (в отличие от SOAP, предоставляющего свою схему). RSS – это способ описания возможностей REST-службы.

 

RSS, по сути, в некоторой степени заменяет WSDL.RSS описывает типы данных, операции, ресурсы, объекты службы, и RSS можно применять при работе с самими объектами. RSS, в отличие от JSON, предоставляет не только данные, но и метаинформацию.

Пример реализации RSS

 

Пусть служба WeatherService (была рассмотрена ранее) несет информацию о погоде по некоторым городам, их количество конечно. Если информация о погоде какое-то время не изменялась, в RSS-документе никаких обновлений не будет, а если изменялась – будут помечены обновления. Каждый элемент в RSS помечается датой и временем изменения, поэтому, если нужно обеспечить контроль за информацией о погоде, удобно сделать это в RSS, и все клиенты будут читать RSSc информацией с пометкой, когда она поменялась. Это позволит, например, отследить тенденции, например, изменения температуры: можно собрать и визуализировать информацию, когда и насколько менялась температура.

Браузер тоже умеет интерпретировать RSS: он сразу в виде HTML показывает RSS – что поменялось на странице с предыдущего визита. Все отображается уже в удобном для человека виде (не XML. Человеку достаточно тяжело читать XML, хотя он и создавался для возможности чтения и машиной, и человеком).

Есть две основных версии RSSRSS 2.0 и Atom 1.0. Оба формата собирают обновленные новости и метаинформацию с сайтов, но за ними стояли две противоборствующие группы сторонников каждого формата. Поэтому осталось два основных стандарт: Atom 1.0, который считается более удобным для HTTP REST, и RSS 2.0 – для новостных сайтов. Отличия форматов проявляются в названиях тегов и в стандартном их наборе, но по возможностям оба формата приблизительно равны.

 

Стандартом для веб-служб REST считается возвращать данные в формате Atom 1.0. Библиотека WCF имеет средства, значительно облегчающие эту задачу.

 

publicSyndicationFeedFormatterGetWeatherInfoFeed()

{

string[] locations = { "Minsk", "Vitebsk" };

//WhetherInfo res = GetWhetherInfo(location);

var feedData = newSyndicationFeed("WeatherInfo",

"Weather information for the given location",

newUri("http://localhost/WeatherService/weather/feed"));

List<SyndicationItem> items = newList<SyndicationItem>();

foreach (string location in locations)

{

SyndicationItem item = newSyndicationItem(location,

string.Format("{0} temperature: {1}",

           location, GetTemperature(location)),

newUri("http://localhost/WeatherService/weather/get?loc="

           + location), "ItemID", DateTime.Now);

   items.Add(item);

}

feedData.Items = items;

Atom10FeedFormatter feed = newAtom10FeedFormatter(feedData);

return feed;

}

 

Для создания RSS существует тип данных, который называется SyndicationFeedFormatter. Это объект для форматирования данных в формате RSS. Если метод возвращает данные в этом формате, WCF умеет использовать этот объект, чтобы вернуть не его свойства, а его содержимое, то, что он возвращает в виде потока – SyndicationFeedFormatter умеет возвращать потока и WCF об этом знает. Ситуация аналогична с примерами, где методы службы возвращали объекты типа Stream: WCF также выводила не свойства самих объектов (открыт или закрыт, позиция и другие), а их содержимое.

           Как работает GetWeatherInfoFeed? Создается объект feedData класса SyndicationFeed. Для него создается список элементов класса SyndicationItem. feedData и есть источник данных для синдикации – для создания объекта RSS. В цикле создаются объекты класса SyndicationItem, накапливаются в массиве созданных объектов. Для них задаются различные свойства, одно из которых – URL для получения значений.

           Мы формируем список ссылок и присваиваем его свойству Items (feedData.Items = items) класса SyndicationFeed. После этого создается Atom10FeedFormatter на базе полученного объекта SyndicationFeed. feedData – собственно данные RSS, так же, как и XML, в разобраном виде, содержат просто данные определенного формата. Это еще не XML. А вот Atom10FeedFormatter – это форматированный XML в формате Atom 1.0. Объект Atom10FeedFormatter с вложенным внутрь SyndicationFeed обрабатывается WCF. WCF вызывает сериализацию объекта в поток. Это будет XML документ, соответствующий стандарту Atom 1.0.

           Чем хорош RSS? Он может содержать ссылки, которые может показывать браузер. Если этот RSS отобразить на экране, он будет отформатирован и покажет ссылки. Можно приспособить веб-службу для показа результата прямо в браузере. Рассмотрим результат вызова описанного метода (http://localhost/WeatherService/weather/feed).

 

 

Так выглядит результат при просмотре в браузере Internet Explorer. Эту страницу сформатировал сам браузер. Имена городов и зеленые стрелки работают как ссылки (пример ссылки – в нижней части рисунка). Это ссылки, указанные в SyndicationFeed. Код ответа службы представлен ниже.

 

<feedxmlns="http://www.w3.org/2005/Atom">

<titletype="text">

WeatherInfo

</title>

<subtitletype="text">

Weather information for the given location

</subtitle>

<id>

uuid:b71cda32-74cb-495f-bc38-21ed9f5bb22e;id=1

</id>

<updated>

2013-12-22T16:19:57Z

</updated>

<linkrel="alternate"href="http://localhost/WeatherService/weather/feed"/>

<entry>

<id>

ItemID

</id>

<titletype="text">

Minsk

</title>

<updated>

2013-12-22T19:19:57+03:00

</updated>

<linkrel="alternate"href="http://localhost/WeatherService/weather/get?loc=Minsk"/>

<contenttype="text">

Minsk temperature: 15

</content>

</entry>

<entry>

<id>

ItemID

</id>

<titletype="text">

Vitebsk

</title>

<updated>

2013-12-22T19:19:57+03:00

</updated>

<linkrel="alternate"href="http://localhost/WeatherService/weather/get?loc=Vitebsk"/>

<contenttype="text">

Vitebsk temperature: 13

</content>

</entry>

</feed>

     

Наиболее полное представление о той или иной версии RSS можно получить, изучив спецификацию.


 

Облачные вычисления


Дата добавления: 2018-08-06; просмотров: 253; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!