Как сделать простейший Web-handler - формирующий XML или JSON.
![web performance](/WebHandler/WebHandler.gif)
Помимо легковесности и простоты - XML, формируемые хандлерами, достаточно легко шифруются. Я постоянно использую шированные на прикладном уровне XML, когда в этом возникает необходимость (например для защиты от копирования платных программ). У меня даже поднята своя собственная инфраструктура выдачи ключей шифровния и их энролмента (http://activator.vb-net.com/activate.ashx. Причем при таком механизме шифрования, полностью обходится стандартная платная инфраструктура сертификатов, прикрученная к интернету непонятно кем и непонятно для чего (но позволяющая выкачивать из пользователей интернета миллионы долларов). То есть компании покупают у Thawte ключи шифрования, которыми заверяют подлинность своих собственных открытых клоючей шифрования - а потом (в конечном итоге) перекладываются свои расходы на рядовых пользователей интернета. При шифровании XML, формируемыми в хандлерах эта вся инфраструктура платных заверений открытых ключей не нужна совершенно, а защищенность трафика получается ровно такая же, как в самых-самых крученых решениях.
Впрочем, еще более адская разводкой в области безопасности интернета, чем даже стандартная инфраструктура сертификатов - является Криптография по ГОСТ.
Не касаясь преимуществ шифрования трафика - подобными простейшими XML, сформированными хандлерами, реализуются в интернете десятки стандартных задач - начиная от построения SiteMap для целей SEO и обменом данных с Flex-приложениями, кончая формированием RSS-каналов сайтов и Шлюзов к платежным системам интернет-денег.
Хандлеры, формирующий XML используются при работе шлюзов с 1С, а полученный XML можно напрямую укладывать в базу, корректировать его там встроенными XML-функциями SQL, парсить на уровне SQL, парсить на уровне кода, а также собирать не просто строками (как будет показано ниже), но и более сложными специализированными классами. Подобный хандлер можно сделать на классах XLINQ, а не на простых строках.
Но я покажу здесь самый найпростейший из возможных вариантов - вариант в сто волшебных кликов мышкой.
Я начну с создания WebApplication на NET 4.0 - чтобы показать все варианты, потом удалю Web-приложение и сделаю просто сайт с хандлером. Просто сайт в беспроектной архитерктуре имеет ряд преимуществ перед веб-приложением. Основное преимущество - код можно править в Visual Studio во время просмотра странички и просто нажимать рефреш в браузере - и откорректированный код сразу же оказывается в браузере. В отличие от этого - изменение в код веб-приложения невозможны и для ничтожнейшей поправки надо делать стоп в Visual Studio и запускать приложение на выполнение снова и снова. Зато в web-приложении возможны какие-то фишки, которые невозможны в сайте.
Но общая рекомендация такова - можете обходится беспроектной архитектурой - обходитесь. Ну а мы для начала начнем с хандлера в составе проекта.
![](/WebHandler/1_1.gif)
![](/WebHandler/2_1.gif)
![](/WebHandler/3_1.gif)
Здесь первый ключевой момент - в проект wevApllication надо обеспечить, чтобы парсер URL не преобразовывал URL при реквестах к хандлеру, это делается внесением в global.asax правила - {resource}.ashx/{patchinfo} - подробнее о преобраованиях URL читайте тут ASP.NET URL rewriting.
![](/WebHandler/4_1.gif)
Теперь собственно добавляем шаблон хандлера.
![](/WebHandler/5_1.gif)
![](/WebHandler/6_1.gif)
![](/WebHandler/3_1.gif)
Назначение этого хандлера - сформировать XML со списком файлов отображения для моего фотослайдера на FLEX. Пока создадим папочку для изображений (которую потом на хостинге смапируем с помощью виртуальных директорий IIS на реальную папку с изображениями).
Забросим в эту папку несколько изображений для отладки и поставим сразу же безопасность на эту папку - разрешено читать всем.
![](/WebHandler/8_1.gif)
![](/WebHandler/9_1.gif)
![](/WebHandler/10_1.gif)
![](/WebHandler/11_1.gif)
![](/WebHandler/12_1.gif)
![](/WebHandler/13_1.gif)
![](/WebHandler/14_1.gif)
![](/WebHandler/15_1.gif)
![](/WebHandler/16_1.gif)
![](/WebHandler/17_1.gif)
Теперь пора проверится - все ли работает в создаваемом нами проекте. Введем пару строк кода и проверимся:
![](/WebHandler/18_1.gif)
![](/WebHandler/19_1.gif)
![](/WebHandler/20_1.gif)
Да все отлично. Теперь можно создавать реальный код. Но для начала я покажу структуру рисунков, которые я собираюсь показывать этим хандлером (и как я эти рисунки обычно создаю).
Все рисунки на этом моем сайте я создаю с помощью XNVIEW - а эта пакетный формирователь аватарок в этой проге создает уменьшенный рисунок-аватарку с суффиксом "_1":
![](/WebHandler/Xnview-1_1.gif)
![](/WebHandler/Xnview-2_1.gif)
![](/WebHandler/Xnview-3_1.gif)
![](/WebHandler/Xnview-4_1.gif)
![](/WebHandler/Xnview-5_1.gif)
![](/WebHandler/Xnview-6_1.gif)
![](/WebHandler/Xnview-7_1.gif)
![](/WebHandler/Xnview-8_1.gif)
Следовательно, нам потребуется сортировка файлов. А реализция сортировки требует реализации интерфейса IComparer, иначе непонятно по какому критерию мортировать файлы - по дате, размеру, времени создания, расширению или иначе. А нам нужна сортировака по имени.
![](/WebHandler/21_1.gif)
Поэтому сразу создадим такой метод, который будем вызывать в сортировке файлов. Обратите внимание, что весь шаблон этого кода создается автоматически - для этого надо поставить мышку на второй строчке и нажать ввод.
1: Public Class CompareFileByName
2: Implements IComparer
3:
4: Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
5: If x.Name = y.Name Then
6: Return 0
7: ElseIf x.Name > y.Name Then
8: Return 1
9: ElseIf x.Name < y.Name Then
10: Return -1
11: End If
12:
13: End Function
14: End Class
Теперь вводим первый простейший вариант кода (который вы видите на скрине), и проверяем его:
![](/WebHandler/22_1.gif)
Чудесно, все работает. Почистим проект от ненужных папок и скопируем его на хостинг. Я копирую его на один из своих собственных серверов.
Теперь пропишем доменное имя (хост-хеадер) для нашего будущего хандлера.
![](/WebHandler/27_1.gif)
![](/WebHandler/28_1.gif)
Теперь создадим новый web-сайт в IIS и внимание (!) сделаем в нем виртуальную директорию с нашим именем папки ImageSlider, использованным при отладке, но которая будет смотреть уже на реальные рисунки.
![](/WebHandler/IIS-1_1.gif)
![](/WebHandler/IIS-2_1.gif)
![](/WebHandler/IIS-3_1.gif)
![](/WebHandler/IIS-4_1.gif)
![](/WebHandler/IIS-5_1.gif)
![](/WebHandler/IIS-6_1.gif)
![](/WebHandler/IIS-7_1.gif)
![](/WebHandler/IIS-1_1.gif)
Теперь укажем, что это сайт на NET 4.0, а не на NET 2.0 и дефолтный документ этого узла.
![](/WebHandler/IIS-9_1.gif)
![](/WebHandler/IIS-10_1.gif)
![](/WebHandler/IIS-11_1.gif)
![](/WebHandler/IIS-12_1.gif)
![](/WebHandler/IIS-13_1.gif)
![](/WebHandler/IIS-14_1.gif)
Все, настройка IIS завершена, если есть возможность подождать некоторое время, то можно проверить сайт в боевой работе, но всене так просто - дело в том, что я обычно выкладываю фотки не все подряд в одну директорию, а каждую выкладку за какой-то день делаю в отдельную директорию. Поэтому нам придется добавить в код еще и обход директорий.
В принципе, это можно было бы сделать отдельной функций бейсика, которая позволяет обходить все директории, да еще и позволяет указать несколько шаблонов файлов сразу (jpg и gif) - My.Computer.FileSystem.GetFiles(FolderName, Microsoft.VisualBasic.FileIO.SearchOption.SearchAllSubDirectories, {"*.gif", "*.jpg"}), но я для большей гибкости реализую эту функцию сам своим кодом - в той канве в которой я уже начал.
Для начала я создам на девелоперской машине такую же структуру каталогов, как в боевой обстановке хостинга.
![](/WebHandler/29_1.gif)
![](/WebHandler/30_1.gif)
![](/WebHandler/31_1.gif)
![](/WebHandler/32_1.gif)
Теперь я просто перенесу существуюший код в обход одной директории, и добавлю в хандлер параметр и проверю, при котором алгоритм будет обходить все директории.
![](/WebHandler/33_1.gif)
![](/WebHandler/34_1.gif)
![](/WebHandler/35_1.gif)
Опубликуем код и проверим его в реальной обстановке. Но перед этим я решил добавить код в Subversion. О системе SVN вы можете почитать на отдельной страничке моего сайта - Избавляемся от Team Foundation Server - ставим Subversion.
![](/WebHandler/SVN-1_1.gif)
![](/WebHandler/SVN-2_1.gif)
![](/WebHandler/SVN-3_1.gif)
![](/WebHandler/SVN-4_1.gif)
![](/WebHandler/SVN-5_1.gif)
![](/WebHandler/SVN-6_1.gif)
И еще я одну опарацию попутно произведу - перейду из проектной в беспроетную архитектуру. Если в проектной архитектуре у меня было свой свойства проекта (левый скрин), мастер публикации и Global.asax, то сейчас я выброшу все файлы проектной архитектуры и global.asax - свойства сайта на правом скрине будут теперь выглядеть гораздо скромнее, но зато теперь код можно будет менять без перезапуска проекта.
![](/WebHandler/41_1.gif)
![](/WebHandler/42_1.gif)
Собственно этот вариант мы бы получили, если бы изначально создавали сайт вот таким путем.
![](/WebHandler/43_1.gif)
![](/WebHandler/44_1.gif)
Итак, публикуем сайт.
![](/WebHandler/45_1.gif)
![](/WebHandler/46_1.gif)
![](/WebHandler/47_1.gif)
Проверяем и, увы, обнаруживаем с удивлением, что код упал. Ведь в этих папочках еще полно каких-то заметок - например где и когда сняты данные фотки. А никакой фильтрации-то файлов у нас пока нет.
![](/WebHandler/39_1.gif)
![](/WebHandler/40_1.gif)
Но и добавить фильтрацию только графических файлов несложно - это пару строк кода, которые будут отбирать только интересующие нас типы файлов.
Итак, остановимся на этом варианте кода - это и будет простейший вариант необходимого фотосладеру кода.
1: <%@ WebHandler Language="VB" CodeBehind="GetImageName.ashx.vb" Class="GetImageName" %>
2:
3: Imports System.Web
4: Imports System.Web.Services
5:
6: Public Class GetImageName
7: Implements System.Web.IHttpHandler
8:
9: Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
10: Try
11: Dim SliderFotos As String = HttpContext.Current.Server.MapPath("ImageSlider")
12: '
13: context.Response.ContentType = "text/xml"
14: context.Response.Write("<?xml version='1.0' encoding='UTF-8'?>")
15: context.Response.Write("<BestFotoNumbers>" & vbCrLf)
16: '
17: If context.Request.Params("all") Is Nothing Then
18: ProcessOneFolder(context, SliderFotos)
19: Else
20: Dim Folders As New IO.DirectoryInfo(SliderFotos)
21: For Each OneFolder As IO.DirectoryInfo In Folders.GetDirectories()
22: ProcessOneFolder(context, OneFolder.FullName)
23: Next
24: End If
25: '
26: context.Response.Write("</BestFotoNumbers>" & vbCrLf)
27: Catch ex As Exception
28: context.Response.ContentType = "text/plain"
29: context.Response.Write(ex.Message)
30: End Try
31:
32: End Sub
33:
34: Sub ProcessOneFolder(ByVal context As HttpContext, ByVal FolderName As String)
35: Dim FileList As New IO.DirectoryInfo(FolderName)
36: Dim Files1() As IO.FileInfo = FileList.GetFiles("*.gif")
37: Dim Files2() As IO.FileInfo = FileList.GetFiles("*.jpg")
38: Dim Files3() As IO.FileInfo = FileList.GetFiles("*.png")
39: Dim Files(Files1.Length + Files2.Length + Files3.Length-1) As IO.FileInfo
40: Array.Copy(Files1, Files, Files1.Length)
41: Array.Copy(Files2, Files, Files2.Length)
42: Array.Copy(Files3, Files, Files3.Length)
43: Array.Sort(Files, New CompareFileByName)
44: For i As Integer = 0 To Files.Count / 2 - 1
45: If Files.Count >= i * 2 + 1 Then
46: If IO.Path.GetFileNameWithoutExtension(Files(i * 2 + 1).Name).Contains(IO.Path.GetFileNameWithoutExtension(Files(i * 2).Name)) Then
47: context.Response.Write("<BestFoto>")
48: context.Response.Write("<Big>" & FileList.Name & "/" & Files(i * 2).Name & "</Big>" & vbCrLf)
49: context.Response.Write("<Small>" & FileList.Name & "/" & Files(i * 2 + 1).Name & "</Small>" & vbCrLf)
50: context.Response.Write("</BestFoto>" & vbCrLf)
51: End If
52: End If
53: Next
54: End Sub
55:
56: ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
57: Get
58: Return False
59: End Get
60: End Property
61: End Class
62:
63: Public Class CompareFileByName
64: Implements IComparer
65:
66: Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
67: If x.Name = y.Name Then
68: Return 0
69: ElseIf x.Name > y.Name Then
70: Return 1
71: ElseIf x.Name < y.Name Then
72: Return -1
73: End If
74:
75: End Function
76: End Class
Итак, если вы вместе со мной проделали весь путь этой странички от начала до конца - вы не только получили хандлер для моего OpenSource фото-слайдера на Flex за сто кликов мышкой, но и научились настраивать IIS, мапить виртуальные директории IIS на реальные папки с данными, получили начальные понятия о сортировке, работе с массивами, работе с папками и файлами, проектной и беспроектной архитектурой Visual Studio 2010, о контроле версий с помощью Subversion, о методологии пошаговой реализации кода и поняли насколько принципиально важным компонентом в web является понятие handler.
Теперь я покажу step-by-step чуток другой мой хандлер, тоже являющийся важным компонентом моего Simple Flash Banner Rotator with BLUR Effect. Надеюсь, что такое JSON - знают все. Это тот же самый XML - только количество символов и тегов-паразитов сведено к минимуму. Ибо строковые обработки - циклически проходы по памяти и сравнение байтов памяти с шаблонами - это самые тормозные операции из всех, которые можно придумать. Поэтому и надо максимально убрать ненужные скобочки и теги - если смысл данных ясен из контекста их расположения.
Для построение аналогичного хандлера, формирующего JSON я применю практически тот же способ, что и в предыдущем примере. Все будет тупо, насколько это возможно - никаких крученных микрософтовских классов, только строки и неслько кликов мышкой в нужных местах экрана. В этом примере мышиных кликов будет тоже около ста.
Поскольку JSON (в отличие от XML) браузером напрямую не отображается - я также сделаю страничку http://pencil.vb-net.com/ для отображения JSON. Эту страничку я сделаю на jQuery и плагине к нему http://www.datatables.net/. Для разнообразия, я выведу в этом примере данные не оберткой вокруг SQL-процедуры, а чистым LINQ. Им же и пересортирую данные в случайном порядке.
Итак, создаем чистый web-проект и добавляем в него хандлер:
![](/WebHandler/100_1.gif)
![](/WebHandler/101_1.gif)
![](/WebHandler/102_1.gif)
![](/WebHandler/103_1.gif)
Теперь создадим буфер в памяти компьютера для чтения данных из SQL-сервера и разметим его путем перетаскивания таблички из SQL-сервера:
![](/WebHandler/104_1.gif)
![](/WebHandler/105_1.gif)
![](/WebHandler/106_1.gif)
![](/WebHandler/107_1.gif)
![](/WebHandler/108_1.gif)
Теперь вводим код хандлера:
![](/WebHandler/109_1.gif)
Не уверен, правда, что LINQ-способ сортировки быстрее, чем сортировка прямо внутри SQL - но пусть для этого примера будет так:
1: <%@ WebHandler Language="VB" Class="GetSiteTopic" %>
2:
3: Imports System
4: Imports System.Web
5:
6: Public Class GetSiteTopic : Implements IHttpHandler
7:
8: Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
9: Try
10: Dim DB1 As New TopicDataContext
11: Dim AllRecord As System.Linq.IQueryable(Of Entrance) = From X In DB1.Entrance Select X
12: Dim AllRecords = AllRecord.ToList
13: Dim RND As New System.Random(Now.Millisecond + Now.Second + Now.Minute + Now.Hour * Now.Day)
14: Dim RandomRecords = AllRecords.OrderBy(Function(r) RND.Next()).ToList
15: Dim Result As New StringBuilder("[")
16: For i As Integer = RandomRecords.Count - 1 To 0 Step -1
17: Result.Append("{""")
18: Result.Append("TYPE"":""")
19: Result.Append(RandomRecords(i).Type)
20: Result.Append(""",""")
21: Result.Append("URL"":""")
22: Result.Append(RandomRecords(i).URL)
23: Result.Append(""",""")
24: Result.Append("TXT"":""")
25: Result.Append(RandomRecords(i).TXT.Replace("""", ""))
26: Result.Append("""},")
27: Result.AppendLine()
28: Next
29: Result.Remove(Result.Length - 3, 3)
30: Result.Append("]")
31: context.Response.ContentType = "application/json"
32: context.Response.Charset = "utf-8"
33: 'context.Response.Write("[""a"",""b"",""c""]")
34: context.Response.Write(Result.ToString)
35: Catch ex As Exception
36: context.Response.ContentType = "text/plain"
37: context.Response.Write(ex.Message)
38: End Try
39:
40: End Sub
41:
42: Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
43: Get
44: Return False
45: End Get
46: End Property
Проверить корректность сформированного хандлером JSON можно на этом сайте http://www.jsonlint.com/, а увидеть JSON глазками можно в отладчике FireFox:
![](/WebHandler/112_1.gif)
![](/WebHandler/110_1.gif)
![](/WebHandler/111_1.gif)
Но хотелось бы увидеть ответ хандлера непосредственно на HTML-страничке. Для этого сделаем небольшую тестовую страничку на JQUERY. Результат сформированный этой тестовой страничкой вы можете увидеть на http://pencil.vb-net.com/.
1: <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>
2:
3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4: <html xmlns="http://www.w3.org/1999/xhtml">
5: <head runat="server">
6:
7: <script src="jquery-1.4.4.min.js" type="text/javascript" language="javascript"></script>
8: <script src="jquery.dataTables.min.js" type="text/javascript" language="javascript"></script>
9: <script type="text/javascript" language="javascript">
10:
11: $(document).ready(function () {
12: $.getJSON("GetSiteTopic.ashx", {}, ret1);
13:
14: });
15:
16: function ret1(json, success) {
17: $('#waiting').hide();
18:
19: $('#resultcontent').dataTable({
20: "aLengthMenu": [[10, -1, ], [10, "All"]]
21: });
22:
23: for (var i = 0; i < json.length - 1; i++) {
24: $('#resultcontent').dataTable().fnAddData
25: ([
26: json[i].TYPE,
27: json[i].URL,
28: json[i].TXT
29: ]);
30: }
31: }
32: </script>
33: </head>
34: <body>
35: <img id="waiting" src="wait.gif" />
36: <table id="resultcontent" style="width: 100%">
37: <thead>
38: <tr>
39: <th>
40: </th>
41: <th>
42: </th>
43: <th>
44: </th>
45: </tr>
46: </thead>
47: <tbody>
48: <tr>
49: <td>
50: </td>
51: <td>
52: </td>
53: <td>
54: </td>
55: </tr>
56: </tbody>
57: </table>
58: </body>
59: </html>
Вы можете построить свой аналогичный хандлер по этому же принципу. Этот хандлер является серверной основой моего Simple Flash Banner Rotator with BLUR Effect . Так же как предыдущий хандлер был серверной основой моего OpenSource фото-слайдера на Flex.
![](http://forum.vb-net.com/GetTopicCount.png?id=B0C08815-4FF8-4DF3-9F9D-427B3E72251A)
<SITEMAP> <MVC> <ASP> <NET> <DATA> <KIOSK> <FLEX> <SQL> <NOTES> <LINUX> <MONO> <FREEWARE> <DOCS> <ENG> <CHAT ME> <ABOUT ME> < THANKS ME> |