Веб-службынаосновеинтерфейса 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, хотя он и создавался для возможности чтения и машиной, и человеком).
Есть две основных версии RSS – RSS 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; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!