С выходом IE8 мы получили одну из самых удобнейших функций в браузере - Визуальные Поисковые Предложения.
Данная статья покажет, как реализовать поиск друзей в твиттере, через специальную Визуальный строку поиска в IE8. По мере набора имени, мы будем видеть аватарку, имя, которое заполнил друг и текущий статус.
Руководство основано на статье Search Provider Extensibility in Internet Explorer из MSDN.
Строим WCF REST сервис.
Создаем новый Empty Web Site, назовем его TwitterFriendsSearch
Добавим новый элемент в проект, WCF Service и назовем его SuggestService.svc.
Это создаст следующие файлы:
- ISuggestService.cs - контракт Service. Определяет сигнатуру действий сервиса.
- SuggestService.cs - реализация Service
- SuggestService.svc -местонахождение сервиса.
- Web.config -настройки WCF сервиса.
Изменяем определение контракта сервиса(ISuggestService.cs) следующим образом:
[ServiceContract]
public interface ISuggestService
{
[OperationContract]
string Search(string friend);
}
Открываем реализацию сервиса (SuggestService.cs). Для начала сделаем простую реализацию, чуть позже изменим это.
public class SuggestService : ISuggestService
{
public string Search(string friend)
{
return "Hello, " + friend;
}
}
Для обеспечения поддержки REST, добавим обязательный WebGet атрибут над действием сервиса.
[OperationContract]
[WebGet(UriTemplate = "/Search?q={friend}",
BodyStyle = WebMessageBodyStyle.Bare)]
string Search(string friend);
Меняем настройки WCF сервиса(web.config) для поддержки REST. Расположите узел behaviors и добавьте новую цель поведения, под секцией System.serviceModel:
<system.serviceModel>
...
<behaviors>
...
<endpointBehaviors>
<behavior name="REST">
<webHttp/>
</behavior>
</endpointBehaviors>
...
</behaviors>
...
</system.serviceModel>
В той же секции, располагаем объявление сервиса и местонахождения. Заменяем связывание с wsHttpBinding на webHttpBinding и добавляем поведение местонахождения:
<services>
<service behaviorConfiguration="SuggestServiceBehavior"
name="SuggestService">
<endpoint address=""
binding="webHttpBinding"
contract="ISuggestService"
behaviorConfiguration="REST" />
</service>
</services>
Запустите проект и перейдите на .svc файл. Далее добавим UriШаблон, который мы объявили в сервисе, с именем друга для поиска:
localhost:50434/TwitterFriendsSearch/SuggestService.svc/search?q=guy
Ответ должен выглядеть как "hello, guy".
Возвращаем результат поиска.
Согласно MSDN странице, ссылку на которую я привел ранее, финальный результат ответа для визуального поиска должен выглядеть так (этот ответ соответствует изображению, которое я прикрепил в начале статьи):
<?xml version="1.0" encoding="utf-8" ?>
<SearchSuggestion>
<Query>guy</Query>
<Section>
<Item>
<Text>guyhaim</Text>
<Description>@rohitbhargava tinyurl.com/6m9e4g</Description>
<Url>twitter.com/home</Url>
<Image source="http://...normal.jpg"
height="48" width="48" alt="guyhaim" />
</Item>
<Item>
<Text>Guy Malachi</Text>
<Description>Yahoo toolbar looks kinda weird.</Description>
<Url>twitter.com/home</Url>
<Image source="http://...normal.jpg"
height="48" width="48" alt="guym" />
</Item>
<Item>
<Text>guy zohar</Text>
<Description>switch screen</Description>
<Url>twitter.com/home</Url>
<Image source="http://...normal.jpg"
height="48" width="48" alt="guyzo" />
</Item>
<Item>
<Text>guyzarz</Text>
<Description>@ekampf May it rest in peace, in one piece.</Description>
<Url>twitter.com/home</Url>
<Image source="http://...normal.jpg"
height="48" width="48" alt="guyzarz" />
</Item>
</Section>
</SearchSuggestion>
Создадим следующие классы, для представления элементов ответа, воспользуемся атрибутами Xml сериализации, для дальнейшей сериализации оных в нужный результат.
// Image.cs
[XmlType]
public class Image
{
[XmlAttribute(AttributeName="source")]
public string Source { get; set; }
[XmlAttribute(AttributeName = "height")]
public int Height { get; set; }
[XmlAttribute(AttributeName = "width")]
public int Width { get; set; }
[XmlAttribute(AttributeName = "alt")]
public string Alt { get; set; }
}
// Item.cs
[XmlType]
public class Item
{
[XmlElement]
public string Text { get; set; }
[XmlElement(IsNullable=false)]
public string Description { get; set; }
[XmlElement(IsNullable = false)]
public string Url { get; set; }
[XmlElement(IsNullable = false)]
public Image Image { get; set; }
}
// SearchSuggestion.cs
[XmlRoot(Namespace="")]
public class SearchSuggestion
{
public SearchSuggestion()
{
this.Section = new List<Item>();
}
[XmlElement]
public string Query { get; set; }
[XmlArray]
public List<Item> Section { get; set; }
}
Изменим контракт сервиса для возвращения SearchSuggestion ответа вместо строки. Так же добавим атрибут XmlSerializerFormat для уверенности, что ответ будет сериализирован в XML.
[ServiceContract]
public interface ISuggestService
{
[OperationContract]
[WebGet(UriTemplate = "/Search?q={friend}",
BodyStyle = WebMessageBodyStyle.Bare)]
[XmlSerializerFormat]
SearchSuggestion Search(string friend);
}
Теперь возвращаем в методе Search SearchSuggestion со значениями по умолчанию вместо строки.
public SearchSuggestion Search(string friend)
{
SearchSuggestion suggestion = new SearchSuggestion
{
Query = friend
};
suggestion.Section.Add(new Item
{
Text = friend,
Description = "Hello, " + friend,
Url = "http://blogs.microsoft.co.il/blogs/bursteg",
Image = new Image
{
Source = "http://tinyurl.com/burstegprofileimage",
Alt = "Guy Burstein",
Width = 48,
Height = 48
}
});
return suggestion;
}
Если сейчас запустить проект и перейти снова на ссылку, которую мы использовали ранее, мы должны получить следующий результат:
<?xml version="1.0" encoding="utf-8" ?>
<SearchSuggestion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Query>omer</Query>
<Section>
<Item>
<Text>omer</Text>
<Description>Hello, omer</Description>
<Url>blogs.microsoft.co.il/blogs/bursteg</Url>
<Image source="http://tinyurl.com/burstegprofileimage"
height="48"
width="48"
alt="Guy Burstein" />
</Item>
</Section>
</SearchSuggestion>
Добавление визуального поиска в Internet Explorer
Добавляем новый Xml файл в проект и называем его FriendsSuggestion.xml. Этот файл содержит подробную информацию о поисковом плагине для браузера и должен выглядеть так:
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Friends Search</ShortName>
<Url type="text/html"
template="http://localhost:50434/TwitterFriendsSearch/
SuggestService.svc/search?q={searchTerms}" />
<Url type="application/x-suggestions+xml"
template="http://localhost:50434/TwitterFriendsSearch/
SuggestService.svc/search?q={searchTerms}" />
<Image height="16"
width="16"
type="image/icon">twitter.com/favicon.ico</Image>
</OpenSearchDescription>
Добавляем Default.aspx страницу. Создаем в ней Javascript функцию, которая регистрирует плагин в браузере:
<script type="text/javascript">
function Register() {
window.external.AddSearchProvider('FriendsSuggestion.xml');
}
</script>
Создайте HTML кнопку, которая будет регистрировать поисковый плагин.
<form id="form1" runat="server">
<div>
<button onclick='Register();'>Click Here</button>
</div>
</form>
Снова запускаем проект, переходим на Default.aspx. Нажимаем на кнопку.
Теперь идем в поле поиска, пробуем написать туда имя друга. На данном этапе мы увидим только значение по умолчанию, так как не реализовали поддержку поиска в твиттере.
Поиск друзей в твиттере
Наконец-то, мы добрались до реализации поиска друзей в твиттере, но данная часть кода может быть изменена под любые ваши нужды.
В реализации сервиса, удаляем значения по умолчанию и делаем HTTP запрос в твиттер для получения списка друзей (замените "bursteg" на свой логин)
string url = string.Format(
"http://twitter.com/statuses/friends/{0}.xml", "bursteg");
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
WebResponse response = request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string responseString = reader.ReadToEnd();
reader.Close();
Далее, используем Linq2Xml для разбора ответа и получения списка друзей, согласно критерию поиска:
XDocument document = XDocument.Parse(response, LoadOptions.None);
var query = from e in document.Root.Descendants("user")
where e.Element("name").Value.Contains(friend) //
e.Element("screen_name").Value.Contains(friend)
select new Item
{
Text = e.Element("name").Value,
Image = new Image
{
Source = e.Element("profile_image_url").Value,
Alt = e.Element("screen_name").Value,
Width = 48,
Height = 48
},
Description = (e.Element("status") == null ? "" :
HttpUtility.HtmlDecode(e.Element("status").Element("text").Value)),
Url = (String.IsNullOrEmpty(e.Element("url").Value) ?
"http://twitter.com/home" :
e.Element("url").Value)
};
В заключение, добавим эти элементы в подсказку поиска.
suggestion.Section.AddRange(query);
Полная версия метода Search должна выглядеть так:
public SearchSuggestion Search(string friend)
{
SearchSuggestion suggestion = new SearchSuggestion();
suggestion.Query = friend;
string url = string.Format("http://twitter.com/statuses/friends/{0}.xml", "bursteg");
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
WebResponse response = request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string responseString = reader.ReadToEnd();
reader.Close();
XDocument document = XDocument.Parse(responseString, LoadOptions.None);
var query = from e in document.Root.Descendants("user")
where e.Element("name").Value.Contains(friend) //
e.Element("screen_name").Value.Contains(friend)
select new Item
{
Text = e.Element("name").Value,
Image = new Image
{
Source = e.Element("profile_image_url").Value,
Alt = e.Element("screen_name").Value,
Width = 48,
Height = 48
},
Description = (e.Element("status") == null ? "" :
HttpUtility.HtmlDecode(e.Element("status").Element("text").Value)),
Url = (String.IsNullOrEmpty(e.Element("url").Value) ?
"http://twitter.com/home" :
e.Element("url").Value)
};
suggestion.Section.AddRange(query);
return suggestion;
}
Теперь, когда ищем друга, мы будем видеть его имя, аватар и текущий статус в предложениях поиска.