Шлюз к 1С по протоколу Битрикс

Это самый нижний уровень шлюза, выполняющий сетевое взаимодействие с 1С по протоколу Bitrix. Описанный на этой страничке уровень, выполняя сетевое взаимодействие с 1С, поднимает на вышележащий уровень программного обеспечения XML-данные, полученные из 1С и, наоборот, затребует от вышележащего уровня строку с XML, которые необходимо отправить для 1С.
Мой шлюз выполняет все необходимые операции для сетевого взаимодествия с 1С:
- поддержание протокольного взаимодействия с 1С
- буферизацию данных при приеме и отправке
- зипование и раззиповывание при приеме и отправке данных
- фрагментацию и сборку пакета при приеме
- журналирование всех операций
В сущности, вышележащему уровню софта остается только манипуляции с итоговым XML, который принят из 1С. И, в обратном направлении, подготовка строки с XML для отправки в 1С. Все остальные черновые операции берет на себя мой шлюз, код которого я публикую ниже.
Хотя сама по себе подготовка XML и его парсинг (функционал вышележащего слоя программного обеспечения) - это тоже весьма непростая задача (в моем случае это десятки тысяч строк кода). И именно этот слой обработки XML составляет специфику любой коммерческой системы (в том числе той, что я разработал с использованием данного шлюза). конечно, саму специфику моей коммерческой системы я публиковать не буду - я покажу вместо кода в десятки тысяч строк (размещенного в ImportXML и ExportXML) лишь две пустые заглушки. Для вас использование моего шлюза - дает возможность реализовать всего две функции ImportXML и ExportXML, в которых вы вставите свой собственный код для работы с XML-данными своей собственной коммерческой системы.
Прежде всего моя коммерческая система была разработана на PostgreSQL (ибо это быстрая, весьма функционально продвинутая и совершенно бесплатная СУБД). Работа с которой отличается тем, что она требует ОДНОГО коннекта со всего сайта (и всех реквестов) - так же как надо работать с Ораклом. Как вообще работать с PostgreSQL из ASP.NET я описал для широкой публики здесь - Этюды на ASP.NET. Пример сайта на СУБД PostgreSQL. Соответвенно, если вы используете какие-то другие СУБД - вам придется заменить этот механизм. Ну например в случае mySQL или MS SQL - просто выбросить PG_Safe_Connection и выполнять каждую SQL-операцию в отдельном коннекте.
Итак, весь этот код должен быть доступен для 1С через инет. Для этого создайте на своем сайте хандлер (который будет дергать 1С по протоколу взаимодействия с Битриксом).
<%@ WebHandler Language="VB" Class="_1c_XML" %>
И поместите в область App_Code своего сайта класс _1c_XML, который будет вызван из 1С:
1: Imports Microsoft.VisualBasic
2:
3: Public Class _1c_XML
4: Inherits _1c_Gateway
5:
6: Public Sub ImportXML(ByVal FullOutXmlFileName As String, ByRef LoadResult As ProcessReceiveXMLResult) Handles MyBase.XMLReceive
7: ....
8: LoadResult = ProcessReceiveXMLResult.Success
9: End Sub
10:
11: Public Sub ExportXML(ByRef XMLString As String) Handles MyBase.SendXMLRequest
12: ....
13: XMLString = "<?xml version='1.0' encoding='UTF-8' ?> <КоммерческаяИнформация ВерсияСхемы='2.04' ДатаФормирования='2010-07-24T11:29:20'>.....</xml>"
14: End Sub
15: End Class
Теперь рассмотрим собственно код моего шлюза - класс _1c_Gateway, от которого унаследован класс _1c_XML, непосредственно вызываемый хандлером.
1: 'Этот хандлер реализует протокол шлюза с 1С по протоколу http://v8.1c.ru/edi/edi_stnd/131/ на самом нижнем уровне
2: '1С-шлюз выполняет передачу данных в 1С одним фрагментом, а прием многими фрагментами (в обоих направлениях в ZIP-формате)
3: 'и вызывает код обработки принятых или отправляемых в 1С XML-файлов
4: '
5: 'Хандлер требует три параметра в конфиге:
6: '<add key="Cache" value="Temp" /> - имя рабочего каталога-буфера для обработки файлов
7: '<add key="ZipPath" value="C:\Program Files (x86)\7-Zip\7z.exe" /> - местоположение зиипера
8: '<add key="1С_FullLog" value="True" /> - необходимость ведения полного журнала
9: '
10: 'хандлеру требуется постоянный коннект к PostgreSQL (класс PG1.SQL_Postgres его поддерживается через интерфейс IRequiresSessionState)
11: 'в базе хандлер использует таблу 1C_Exchange для учета принятых фрагментов и таблу TerminalError для записи ошибок
12: '
13: 'при приеме файла вызывается событие XMLReceive, которое обрабатывается во внешнем коде (коде более высокого уровня)
14: 'этому событию передаются имя принятого файла, обработчик события возвращает результат обработки
15: '
16: 'при передаче в 1С данных, шлюз вызывает событие SendXMLRequest, которому внешний код более высокого уровня передает сформированную строку XML для передачи и учетный номер (номер заказа)
17:
18: Imports System
19: Imports System.Web
20: Imports Microsoft.VisualBasic
21:
22: 'это интерфейс события, обрабатываемого во внешнем коде - ему передаются имя принятого файла и надо возвратить результат обработки полученных от 1С данных
23: Public Delegate Sub XMLReceived(ByVal FileName As String, ByRef ProcessReceiveResult As ProcessReceiveXMLResult)
24: Public Enum ProcessReceiveXMLResult
25: Success = 1
26: Failure = 2
27: Progress = 3
28: End Enum
29:
30: 'это интерфейс события для внешнего кода, который формирует XML для передачи в 1С
31: Public Delegate Sub SendingXMLRequest(ByRef XMLstring As String)
32:
33:
34: Public Class _1c_Gateway : Implements IHttpHandler, IRequiresSessionState
35:
36: Public Event XMLReceive As XMLReceived
37: Public Event SendXMLRequest As SendingXMLRequest
38:
39: Dim DataSegmentSize As Integer = 10000 'размер сегмента данных для передачи из битрикса
40:
41: Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
42: '
43: PG_Safe_Connection(context)
44: '
45: If context.Request.QueryString("type") IsNot Nothing And context.Request.QueryString("mode") IsNot Nothing Then
46: If context.Request.QueryString("type") = "catalog" And context.Request.QueryString("mode") = "checkauth" Then
47: Receive_Step1(context)
48: ElseIf context.Request.QueryString("type") = "catalog" And context.Request.QueryString("mode") = "init" And context.Request.Cookies("SessionID") IsNot Nothing Then
49: Receive_Step2(context)
50: ElseIf context.Request.QueryString("type") = "catalog" And context.Request.QueryString("mode") = "file" And context.Request.QueryString("filename") IsNot Nothing And context.Request.Cookies("SessionID") IsNot Nothing Then
51: Receive_Step3(context)
52: ElseIf context.Request.QueryString("type") = "catalog" And context.Request.QueryString("mode") = "import" And context.Request.QueryString("filename") IsNot Nothing And context.Request.Cookies("SessionID") IsNot Nothing Then
53: Receive_Step4(context)
54: ElseIf context.Request.QueryString("type") = "sale" And context.Request.QueryString("mode") = "checkauth" Then
55: Send_Step1(context)
56: ElseIf context.Request.QueryString("type") = "sale" And context.Request.QueryString("mode") = "init" And context.Request.Cookies("SessionID") IsNot Nothing Then
57: Send_Step2(context)
58: ElseIf context.Request.QueryString("type") = "sale" And context.Request.QueryString("mode") = "query" And context.Request.Cookies("SessionID") IsNot Nothing Then
59: Send_Step3(context)
60: ElseIf context.Request.QueryString("type") = "sale" And (context.Request.QueryString("mode") = "success" Or context.Request.QueryString("mode") = "failure") And context.Request.Cookies("SessionID") IsNot Nothing Then
61: Send_Step4(context)
62: End If
63: End If
64: End Sub
65:
66: #Region "Прием данных из 1С"
67:
68: Sub Receive_Step1(ByVal context As HttpContext)
69: Dim GUID1 As Guid = Guid.NewGuid
70: context.Session("SessionID") = GUID1.ToString
71: context.Response.ContentType = "text/plain"
72: context.Response.Write("success" & vbCrLf & "SessionID" & vbCrLf & GUID1.ToString & vbCrLf & vbCrLf)
73: '
74: TraceLog(context, "Receive_Step1: " & "type=" & context.Request.QueryString("type") & ",mode=" & context.Request.QueryString("mode") & ",SessionID=" & context.Session("SessionID") & ",Cookies=" & GetAllCookies(context))
75: End Sub
76:
77: Sub Receive_Step2(ByVal context As HttpContext)
78: context.Response.ContentType = "text/plain"
79: context.Response.Write("zip=yes" & vbCrLf & "file_limit=" & DataSegmentSize.ToString & vbCrLf)
80: '
81: TraceLog(context, "Receive_Step2: " & "type=" & context.Request.QueryString("type") & ",mode=" & context.Request.QueryString("mode") & ",Cookies=" & GetAllCookies(context))
82: End Sub
83:
84: Sub Receive_Step3(ByVal context As HttpContext)
85: 'собственно прием и запись сегмента данных в файловую систему
86: Dim Buf1(DataSegmentSize) As Byte
87: Try
88: TraceLog(context, "Receive_Step3: (Start) " & ",type=" & context.Request.QueryString("type") & ",mode=" & context.Request.QueryString("mode") & ",filename=" & context.Request.QueryString("filename").Replace("\", "\\") & ",Cookies=" & GetAllCookies(context))
89: '
90: Dim DataSegmentNum As Integer = 0
91: DataSegmentNum = GetLastDataSegmentNum(context, context.Request.Cookies("SessionID").Value)
92: DataSegmentNum += 1
93: '
94: Dim WorkingDir As String = context.Server.MapPath(ConfigurationManager.AppSettings("Cache").ToString)
95: '
96: Dim SegmentFileName As String = context.Request.Cookies("SessionID").Value & "_" & DataSegmentNum
97: '
98: Dim FullZipFileName As String = IO.Path.Combine(WorkingDir, SegmentFileName & "_" & context.Request.QueryString("filename"))
99: If Not My.Computer.FileSystem.FileExists(FullZipFileName) Then
100: If HttpContext.Current.Request.InputStream.Length <= Buf1.Length Then
101: While HttpContext.Current.Request.InputStream.CanRead
102: Dim CountReadingBytes As Integer = HttpContext.Current.Request.InputStream.Read(Buf1, 0, HttpContext.Current.Request.InputStream.Length)
103: If CountReadingBytes = 0 Then Exit While
104: ReDim Preserve Buf1(HttpContext.Current.Request.InputStream.Length - 1)
105: My.Computer.FileSystem.WriteAllBytes(FullZipFileName, Buf1, False)
106: ReDim Buf1(DataSegmentSize)
107: End While
108: '
109: SaveReceiveFileName(context, context.Request.Cookies("SessionID").Value, DataSegmentNum, context.Request.QueryString("filename"), context.Request.InputStream.Length)
110: '
111: context.Response.ContentType = "text/plain"
112: context.Response.Write("success" & vbCrLf) 'сегмент принят
113: '
114: TraceLog(context, "Receive_Step3 (success) : " & FullZipFileName.Replace("\", "\\"))
115: Else
116: context.Response.ContentType = "text/plain"
117: context.Response.Write("failure" & vbCrLf & "Buffer overflow" & vbCrLf & vbCrLf) 'ошибка
118: TraceLog(context, "Receive_Step3 (failure) : " & FullZipFileName.Replace("\", "\\") & " Buffer overflow")
119: End If
120: Else
121: context.Response.ContentType = "text/plain"
122: context.Response.Write("failure" & vbCrLf & FullZipFileName & " Is present" & vbCrLf & vbCrLf) 'ошибка
123: TraceLog(context, "Receive_Step3 (failure): " & FullZipFileName.Replace("\", "\\") & " Is present")
124: End If
125: Catch ex As Exception
126: TraceLog(context, "Receive_Step3 (failure): " & ex.Message)
127: context.Response.ContentType = "text/plain"
128: context.Response.Write("failure" & vbCrLf & ex.Message & vbCrLf & vbCrLf) 'ошибка
129: End Try
130: End Sub
131:
132:
133: 'окончательная обработка принятых сегментов
134: Sub Receive_Step4(ByVal context As HttpContext)
135: Dim ResultCode As String = "failure"
136: Dim FullZipFileName As String = "" 'полное имя ZIP-файла после конкатенации фрагментов (например G:\Projects\DelmarDisk\www\Temp\f160a80d-183d-46a3-b0a7-e3ad2d008a0e.zip)
137: Dim XmlFileName As String = "" 'имя раззипованного XML-файла, переданного 1С (например offers.xml)
138: Dim FullOutXmlFileName As String = "" 'полное (например G:\Projects\DelmarDisk\www\Temp\f160a80d-183d-46a3-b0a7-e3ad2d008a0e\offers.xml)
139: '
140: Try
141: '(1) - сначала отберем имена всех принятых из 1С фрагментов и сольем принятые сегменты в один (context.Request.Cookies("SessionID").Value=f160a80d-183d-46a3-b0a7-e3ad2d008a0e")
142: If Not ConcatAllSegments(context, context.Request.Cookies("SessionID").Value, FullZipFileName) Then
143: GoTo EndReceive
144: End If
145: '
146: '(2) - определяем имя принятого из 1С файла
147: If Not GetZippedFileName(context, FullZipFileName, XmlFileName) Then
148: GoTo EndReceive
149: End If
150: '
151: '(3) - раззиповываем каталог с упакованным в нем XML-файлом
152: If Not UnZip(context, FullZipFileName, XmlFileName, FullOutXmlFileName) Then
153: GoTo EndReceive
154: End If
155: '
156: '(4) - вызов внешнего кода, который распарсит принятый XML и уложит его в базу
157: Dim LoadResult As ProcessReceiveXMLResult = ProcessReceiveXMLResult.Progress
158: RaiseEvent XMLReceive(FullOutXmlFileName, LoadResult)
159: '
160: Select Case LoadResult
161: Case ProcessReceiveXMLResult.Success : ResultCode = "success"
162: Case ProcessReceiveXMLResult.Failure : ResultCode = "failure"
163: Case ProcessReceiveXMLResult.Progress : ResultCode = "progress"
164: End Select
165: '
166: '(5) - отметились у себя в базе о результатах згрузки данных из 1С
167: SaveXMLLoadCode(context, context.Request.Cookies("SessionID").Value, ResultCode)
168: Catch ex As Exception
169: TraceLog(context, "Receive_Step4: (error)" & ex.Message)
170: GoTo EndReceive
171: End Try
172: '
173: EndReceive:
174: context.Response.ContentType = "text/plain"
175: context.Response.Write(ResultCode & vbCrLf)
176: '
177: TraceLog(context, "Receive_Step4: (" & ResultCode & ") " & ",type=" & context.Request.QueryString("type") & ",mode=" & context.Request.QueryString("mode") & ",filename=" & context.Request.QueryString("filename").Replace("\", "\\") & ",Cookies=" & GetAllCookies(context) & ", FullZipFileName=" & FullZipFileName & ", XmlFileName=" & XmlFileName & ", FullOutXmlFileName=" & FullOutXmlFileName)
178: End Sub
179: #End Region
180:
181: #Region "Файловые операции при приеме данных из 1С"
182:
183: 'функция сливает в один фрагмент все принятые из 1С фрагменты файла и возвращает полное имя собранного файла (и ошибку - если не получилось)
184: Function ConcatAllSegments(ByVal context As HttpContext, ByVal SessionID As String, ByRef FullZipFileName As String) As Boolean
185: Dim WorkingDir1 As String = context.Server.MapPath(ConfigurationManager.AppSettings("Cache").ToString) 'C:\WWW\LocalUser\delmar\WWW\Temp1
186: 'сначала отберем имена всех принятых из 1С фрагментов
187: Dim ConcatCommand As New StringBuilder
188: Try
189: Dim DT1 As Data.DataTable
190: DT1 = GetAllDataSegment(context, SessionID)
191: If DT1 Is Nothing Then
192: Return False
193: Else
194: For i As Integer = 0 To DT1.Rows.Count - 1
195: Dim OneSegmentFileName As String = DT1.Rows(i)("SessionID") & "_" & DT1.Rows(i)("DataSegmentNum") & "_" & DT1.Rows(i)("FileName")
196: Dim FullSegmentFileName As String = IO.Path.Combine(WorkingDir1, OneSegmentFileName)
197: ConcatCommand.Append(FullSegmentFileName & " /B + ")
198: Next
199: ConcatCommand.Remove(ConcatCommand.Length - 2, 2)
200: FullZipFileName = IO.Path.Combine(WorkingDir1, SessionID & ".zip")
201: ConcatCommand.Append(FullZipFileName & " /Y")
202: End If
203: Catch ex As Exception
204: TraceLog(context, "Receive_Step4: (ConcatError) " & ex.Message)
205: Return False
206: End Try
207: '
208: 'сливаем принятые сегменты в один
209: Dim Concat As New System.Diagnostics.Process
210: Concat.StartInfo.RedirectStandardOutput = True
211: Concat.StartInfo.UseShellExecute = False
212: Concat.StartInfo.CreateNoWindow = True
213: Dim CopyLog As String
214: Try
215: Concat.StartInfo.FileName = "cmd.exe"
216: Concat.StartInfo.Arguments = "/C copy " & ConcatCommand.ToString
217: '"G:\Projects\DelmarDisk\www\Temp\f160a80d-183d-46a3-b0a7-e3ad2d008a0e_1_v8_C26_25.zip /B + G:\Projects\DelmarDisk\www\Temp\f160a80d-183d-46a3-b0a7-e3ad2d008a0e_2_v8_C26_25.zip /B + G:\Projects\DelmarDisk\www\Temp\f160a80d-183d-46a3-b0a7-e3ad2d008a0e_3_v8_C26_25.zip /B G:\Projects\DelmarDisk\www\Temp\f160a80d-183d-46a3-b0a7-e3ad2d008a0e.zip /Y"
218: Concat.StartInfo.WorkingDirectory = WorkingDir1
219: Concat.StartInfo.WindowStyle = Diagnostics.ProcessWindowStyle.Hidden
220: Concat.Start()
221: Concat.WaitForExit()
222: CopyLog = Concat.StandardOutput.ReadToEnd() '.... 1 file(s) copied.
223: Concat.Close()
224: Catch ex As Exception
225: TraceLog(context, "Receive_Step4: (CopyError) " & ex.Message)
226: Return False
227: End Try
228: '
229: If My.Computer.FileSystem.FileExists(FullZipFileName) Then
230: Return True
231: Else
232: TraceLog(context, "Receive_Step4: (CopyError) " & FullZipFileName & " not found")
233: Return False
234: End If
235: End Function
236:
237: 'функция определяет имя содержащегося в зипе 1C XML-файла (например offers.xml)
238: Function GetZippedFileName(ByVal context As HttpContext, ByVal FullZipFileName As String, ByRef XmlFileName As String) As Boolean
239: Dim ZipPath1 As String = System.Configuration.ConfigurationManager.AppSettings("ZipPath") 'C:\Program Files (x86)\7-Zip\7z.exe
240: Dim ZipParm1 As String = " l " & FullZipFileName
241: Dim ZipCacheDir1 As String = context.Server.MapPath(ConfigurationManager.AppSettings("Cache").ToString) 'C:\WWW\LocalUser\delmar\WWW\Temp1
242: Dim Zip1 As New System.Diagnostics.Process
243: Zip1.StartInfo.RedirectStandardOutput = True
244: Zip1.StartInfo.UseShellExecute = False
245: Zip1.StartInfo.CreateNoWindow = True
246: Dim ZipLog1 As String = ""
247: Try
248: Zip1.StartInfo.FileName = ZipPath1
249: Zip1.StartInfo.Arguments = ZipParm1
250: Zip1.StartInfo.WorkingDirectory = ZipCacheDir1
251: Zip1.Start()
252: Zip1.WaitForExit()
253: ZipLog1 = Zip1.StandardOutput.ReadToEnd()
254: Zip1.Close()
255: Catch ex As Exception
256: TraceLog(context, "Receive_Step4: (ZipError) " & ", ZipParm=" & ZipParm1 & ", Ziplog=" & ZipLog1 & " ; " & ex.Message)
257: Return False
258: End Try
259: '
260: 'парсим журнал, чтобы найти имя файла
261: If Len(ZipLog1) < 40 Then
262: TraceLog(context, "Receive_Step4: (ZipError) " & "ZIP не удалось определить имя файла, содержащееся в ZIP-архиве" & ", ZipParm=" & ZipParm1 & ", Ziplog=" & ZipLog1)
263: Return False
264: Else
265: ZipLog1 = ZipLog1.Replace(" ", " ")
266: Dim Pos2 As Integer = ZipLog1.IndexOf(".xml")
267: If Pos2 = 0 Or Pos2 < 40 Then
268: TraceLog(context, "Receive_Step4: (ZipError) " & "ZIP не удалось определить имя файла, содержащееся в ZIP-архиве" & ", ZipParm=" & ZipParm1 & ", Ziplog=" & ZipLog1)
269: Return False
270: Else
271: Dim Pos1 As Integer = Mid(ZipLog1, 1, Pos2).LastIndexOf(" ")
272: XmlFileName = Mid(ZipLog1, Pos1 + 1, Pos2 - Pos1 + 4).Trim
273: If XmlFileName.Contains(".xml") Then
274: Return True
275: Else
276: TraceLog(context, "Receive_Step4: (ZipError) " & "ZIP не удалось определить имя файла, содержащееся в ZIP-архиве" & ZipLog1)
277: Return False
278: End If
279: End If
280: End If
281: End Function
282:
283: Function UnZip(ByVal context As HttpContext, ByVal FullZipFileName As String, ByVal XmlFileName As String, ByRef FullOutXmlFileName As String) As Boolean
284: Dim OutFileDir As String = IO.Path.GetFileNameWithoutExtension(FullZipFileName) 'f160a80d-183d-46a3-b0a7-e3ad2d008a0e
285: Dim FullOutFileDir As String = IO.Path.Combine(context.Server.MapPath(ConfigurationManager.AppSettings("Cache").ToString), OutFileDir) 'G:\Projects\DelmarDisk\www\Temp\f160a80d-183d-46a3-b0a7-e3ad2d008a0e
286: Dim FullOutFileName As String = IO.Path.Combine(FullOutFileDir, XmlFileName) 'G:\Projects\DelmarDisk\www\Temp\f160a80d-183d-46a3-b0a7-e3ad2d008a0e\offers.xml
287: Dim ZipPath As String = System.Configuration.ConfigurationManager.AppSettings("ZipPath") 'C:\Program Files (x86)\7-Zip\7z.exe
288: Dim ZipParm As String = " e -y " & FullZipFileName & " -o" & FullOutFileDir
289: Dim ZipCacheDir As String = context.Server.MapPath(ConfigurationManager.AppSettings("Cache").ToString) 'E:\
290: Dim Zip As New System.Diagnostics.Process
291: Dim ZipLog As String = ""
292: Zip.StartInfo.UseShellExecute = False
293: Zip.StartInfo.CreateNoWindow = True
294: Zip.StartInfo.RedirectStandardOutput = True
295: 'Раззиповывываем и возвращаем имя раззипованного файла
296: Try
297: Zip.StartInfo.FileName = ZipPath
298: Zip.StartInfo.Arguments = ZipParm
299: Zip.StartInfo.WorkingDirectory = ZipCacheDir
300: Zip.Start()
301: Zip.WaitForExit()
302: ZipLog = Zip.StandardOutput.ReadToEnd()
303: Zip.Close()
304: If My.Computer.FileSystem.FileExists(FullOutFileName) Then
305: 'единственный нормальный исход чтобы двигаться дальше
306: FullOutXmlFileName = FullOutFileName
307: Return True
308: Else
309: 'нет результата исполнения команды
310: TraceLog(context, "Receive_Step4: (ZipError) " & "ZIP output empty" & ", ZipParm=" & ZipParm & ", Ziplog=" & ZipLog)
311: Return False
312: End If
313: Catch ex As Exception
314: TraceLog(context, "Receive_Step4: (ZipError) " & ", ZipParm=" & ZipParm & ", Ziplog=" & ZipLog & " ; " & ex.Message)
315: Return False
316: End Try
317:
318: End Function
319:
320: #End Region
321:
322: #Region "Передача заказов в 1С"
323:
324: Sub Send_Step1(ByVal context As HttpContext)
325: Dim GUID1 As Guid = Guid.NewGuid
326: context.Session("SessionID") = GUID1.ToString
327: context.Response.ContentType = "text/plain"
328: context.Response.Write("success" & vbCrLf & "SessionID" & vbCrLf & GUID1.ToString & vbCrLf & vbCrLf)
329: '
330: TraceLog(context, "Send_Step1: " & "type=" & context.Request.QueryString("type") & ",mode=" & context.Request.QueryString("mode") & ",SessionID=" & context.Session("SessionID") & ",Cookies=" & GetAllCookies(context))
331: End Sub
332:
333: Sub Send_Step2(ByVal context As HttpContext)
334: context.Response.ContentType = "text/plain"
335: context.Response.Write("zip=yes" & vbCrLf & "file_limit=" & DataSegmentSize.ToString & vbCrLf)
336: '
337: TraceLog(context, "Send_Step2: " & "type=" & context.Request.QueryString("type") & ",mode=" & context.Request.QueryString("mode") & ",Cookies=" & GetAllCookies(context))
338: End Sub
339:
340: Sub Send_Step3(ByVal context As HttpContext)
341: Dim ResultCode As String = "failure"
342: '
343: TraceLog(context, "Send_Step3 (start): " & "type=" & context.Request.QueryString("type") & ",mode=" & context.Request.QueryString("mode") & ",Cookies=" & GetAllCookies(context))
344: '
345: Dim FullZipFileName As String = ""
346: Dim FullDirName As String = ""
347: Dim Len As Integer
348: Try
349: '
350: '(1) - получили XML для передачи в 1С
351: Dim XMLString As String = ""
352: RaiseEvent SendXMLRequest(XMLString)
353: If XMLString = "" Then
354: ResultCode = "progress"
355: GoTo EndSave
356: End If
357: '
358: '(2) - записали его на диск
359: If Not WriteXMLToFile(context, context.Request.Cookies("SessionID").Value, XMLString, FullDirName) Then 'context.Request.Cookies("SessionID").Value="f57feea1-3033-4780-bae2-2660c9ae9a3a"
360: GoTo EndSave
361: End If
362: '
363: '(3) - зазиповали директорию
364: If Not ZipDir(context, FullDirName, FullZipFileName) Then
365: GoTo EndSave
366: End If
367: '
368: '(4) - выполнили учет
369: Dim FileInfo As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(FullZipFileName)
370: Len = FileInfo.Length
371: SaveSendingFileName(context, context.Request.Cookies("SessionID").Value, 0, IO.Path.GetFileName(FullZipFileName), Len) 'context.Request.Cookies("SessionID").Value="f57feea1-3033-4780-bae2-2660c9ae9a3a"
372: '
373: '(5) - передали данные
374: context.Response.ContentType = "application/octet-stream" '"text/xml"
375: context.Response.BinaryWrite(My.Computer.FileSystem.ReadAllBytes(FullZipFileName))
376: Catch ex As Exception
377: TraceLog(context, "Send_Step3: (error): " & ex.Message)
378: GoTo EndSave
379: End Try
380:
381: EndSave:
382: context.Response.ContentType = "text/plain"
383: context.Response.Write(ResultCode & vbCrLf)
384: '
385: TraceLog(context, "Send_Step3 (end): " & "type=" & context.Request.QueryString("type") & ",mode=" & context.Request.QueryString("mode") & ",Cookies=" & GetAllCookies(context) & ", FullDirName=" & FullDirName & ", FullZipFileName=" & FullZipFileName & ", Len=" & Len.ToString)
386: ''
387: End Sub
388:
389: Sub Send_Step4(ByVal context As HttpContext)
390: '
391: SaveSendingResult(context, context.Request.Cookies("SessionID").Value, context.Request.QueryString("mode"))
392: '
393: TraceLog(context, "Send_Step4: " & "type=" & context.Request.QueryString("type") & ",mode=" & context.Request.QueryString("mode") & ",Cookies=" & GetAllCookies(context))
394: End Sub
395:
396: #End Region
397:
398: #Region "Файловые операции при передаче заказов в 1С"
399: 'записали XML файла с заказами на диск
400: Function WriteXMLToFile(ByVal context As HttpContext, ByVal SessionID As String, ByVal XMLstring As String, ByRef FullDirName As String) As Boolean
401: Try
402: Dim WorkingDir As String = context.Server.MapPath(ConfigurationManager.AppSettings("Cache").ToString)
403: FullDirName = IO.Path.Combine(WorkingDir, SessionID)
404: If My.Computer.FileSystem.DirectoryExists(FullDirName) Then
405: My.Computer.FileSystem.DeleteDirectory(FullDirName, FileIO.DeleteDirectoryOption.DeleteAllContents)
406: End If
407: My.Computer.FileSystem.CreateDirectory(FullDirName)
408: Dim FullFileName As String = IO.Path.Combine(FullDirName, SessionID & ".xml")
409: My.Computer.FileSystem.WriteAllText(FullFileName, XMLstring, False, Text.Encoding.UTF8)
410: Return True
411: Catch ex As Exception
412: context.Response.ContentType = "text/plain"
413: context.Response.Write("failure" & vbCrLf)
414: '
415: TraceLog(context, "Send_Step3: (WriteXMLToFile) " & ex.Message)
416: Return False
417: End Try
418: Return True
419: End Function
420:
421: 'зазиповали директорию
422: Function ZipDir(ByVal context As HttpContext, ByVal FullDirName As String, ByRef FullZipFileName As String) As Boolean
423: Dim ZipPath1 As String = System.Configuration.ConfigurationManager.AppSettings("ZipPath") 'C:\Program Files (x86)\7-Zip\7z.exe
424: Dim ZipCacheDir1 As String = context.Server.MapPath(ConfigurationManager.AppSettings("Cache").ToString) 'C:\WWW\LocalUser\delmar\WWW\Temp1
425: Dim DirName As String = IO.Path.GetFileName(FullDirName)
426: FullZipFileName = IO.Path.Combine(ZipCacheDir1, DirName & ".zip")
427: Dim ZipParm1 As String = " a -tzip " & FullZipFileName & " " & FullDirName & "\*"
428: Dim Zip1 As New System.Diagnostics.Process
429: Zip1.StartInfo.RedirectStandardOutput = True
430: Zip1.StartInfo.UseShellExecute = False
431: Zip1.StartInfo.CreateNoWindow = True
432: Dim ZipLog1 As String = ""
433: Try
434: Zip1.StartInfo.FileName = ZipPath1
435: Zip1.StartInfo.Arguments = ZipParm1
436: Zip1.StartInfo.WorkingDirectory = ZipCacheDir1
437: Zip1.Start()
438: Zip1.WaitForExit()
439: ZipLog1 = Zip1.StandardOutput.ReadToEnd()
440: Zip1.Close()
441: Catch ex As Exception
442: TraceLog(context, "Send_Step3: (ZipError) " & ZipParm1 & " ; " & ex.Message & " ; " & ZipLog1)
443: Return False
444: End Try
445: '
446: If My.Computer.FileSystem.FileExists(FullZipFileName) Then
447: Return True
448: Else
449: TraceLog(context, "Send_Step3: (ZipError) " & "ZIP output not found" & ", ZipParm=" & ZipParm1 & ", ZipLog=" & ZipLog1)
450: Return False
451: End If
452: End Function
453: #End Region
454:
455: #Region "Доступ к Посгресу и ведение журнала с ошибками "
456: 'Вся работа приложения ведется в одном коннекте к Постгресу
457: Dim PG1 As PG1.SQL_Postgres
458: Sub PG_Safe_Connection(ByVal context As HttpContext)
459: If context.Session("PG1") IsNot Nothing Then
460: PG1 = context.Session("PG1")
461: Else
462: PG1 = New PG1.SQL_Postgres
463: context.Session("PG1") = PG1
464: End If
465: PG1.CheckConnect()
466: End Sub
467:
468: 'Журнал
469: Sub TraceLog(ByVal context As HttpContext, ByVal Data As String)
470: Try
471: If System.Configuration.ConfigurationManager.AppSettings("1С_FullLog") Then
472: PG1.PG.ExecRDR("Insert into ""_Delmar"".""TerminalError""(crdate,txt) values (now(), '" & Data & "');")
473: End If
474: Catch ex As Exception
475: context.Response.ContentType = "text/plain"
476: context.Response.Write("failure" & vbCrLf & "BAD SQL COMMAND:" & ex.Message)
477: Exit Sub
478: End Try
479: End Sub
480:
481:
482: Function GetAllCookies(ByVal context As HttpContext) As String
483: Dim Cook_Str As String = ""
484: For Each One As String In context.Request.Cookies.AllKeys
485: Cook_Str &= context.Request.Cookies(One).Name & "= " & context.Request.Cookies(One).Value & ";"
486: Next
487: Return Cook_Str
488: End Function
489:
490: #End Region
491:
492:
493: #Region "SQL-операции при приеме данных из 1С"
494:
495: 'Достать из базы все принятые в последней сессии сегменты данных
496: Private Function GetAllDataSegment(ByVal context As HttpContext, ByVal SessionID As String) As Data.DataTable
497: Dim DT1 As New Data.DataTable
498: Try
499: Dim RDR1 As Npgsql.NpgsqlDataReader = PG1.PG.ExecRDR("select * from ""_Delmar"".""1C_Exchange"" where ""SessionID""='" & SessionID & "' order by i asc;")
500: If RDR1.HasRows Then
501: DT1.Load(RDR1)
502: End If
503: RDR1.Close()
504: Return DT1
505: Catch ex As Exception
506: TraceLog(context, "1c_exchange (GetAllDataSegment): " & ex.Message)
507: context.Response.ContentType = "text/plain"
508: context.Response.Write("failure" & vbCrLf & "BAD SQL COMMAND:" & ex.Message)
509: End Try
510: End Function
511:
512: 'Достать из базы последний DataSegmentNum в этой сессии
513: Private Function GetLastDataSegmentNum(ByVal context As HttpContext, ByVal SessionID As String) As Integer
514: Dim LastDataSegmentNum As Integer = 0
515: Try
516: Dim RDR1 As Npgsql.NpgsqlDataReader = PG1.PG.ExecRDR("select * from ""_Delmar"".""1C_Exchange"" where ""SessionID""='" & SessionID & "' order by i desc limit 1;")
517: If RDR1.Read Then
518: LastDataSegmentNum = RDR1("DataSegmentNum")
519: End If
520: RDR1.Close()
521: Return LastDataSegmentNum
522: Catch ex As Exception
523: TraceLog(context, "1c_exchange (GetLastDataSegmentNum): " & ex.Message)
524: context.Response.ContentType = "text/plain"
525: context.Response.Write("failure" & vbCrLf & "BAD SQL COMMAND:" & ex.Message)
526: End Try
527: End Function
528:
529: 'Сохраняем имя принятого сегмента данных
530: Sub SaveReceiveFileName(ByVal context As HttpContext, ByVal SessionID As String, ByVal DataSegmentNum As Integer, ByVal FileName As String, ByVal Length As Integer)
531: Try
532: PG1.PG.ExecRDR("INSERT INTO ""_Delmar"".""1C_Exchange"" (""CrDate"", ""SessionID"", ""DataSegmentNum"", ""FileName"", ""Length"") VALUES (now(),'" & SessionID & "', '" & DataSegmentNum & "', '" & FileName & "', '" & Length & "' );")
533: Catch ex As Exception
534: TraceLog(context, "1c_exchange (SaveReceiveFileName): " & ex.Message)
535: context.Response.ContentType = "text/plain"
536: context.Response.Write("failure" & vbCrLf & "BAD SQL COMMAND:" & ex.Message)
537: Exit Sub
538: End Try
539: End Sub
540:
541: 'отметились у себя в базе о результатах згрузки данных из 1С
542: Sub SaveXMLLoadCode(ByVal context As HttpContext, ByVal SessionID As String, ByVal ResultCode As String)
543: Try
544: PG1.PG.ExecRDR("UPDATE ""_Delmar"".""1C_Exchange"" SET ""Result"" = '" & ResultCode & "' where ""SessionID""='" & SessionID & "';")
545: Catch ex As Exception
546: TraceLog(context, "1c_exchange (SaveXMLLoadCode): " & ex.Message)
547: context.Response.ContentType = "text/plain"
548: context.Response.Write("failure" & vbCrLf & "BAD SQL COMMAND:" & ex.Message)
549: Exit Sub
550: End Try
551: End Sub
552:
553: #End Region
554:
555:
556: #Region "SQL-операции при передаче заказов в 1С"
557:
558: Sub SaveSendingFileName(ByVal context As HttpContext, ByVal SessionID As String, ByVal DataSegmentNum As Integer, ByVal FileName As String, ByVal Length As Integer)
559: Try
560: PG1.PG.ExecRDR("INSERT INTO ""_Delmar"".""1C_Exchange"" (""CrDate"", ""SessionID"", ""DataSegmentNum"", ""FileName"", ""Length"") VALUES (now(),'" & SessionID & "', '" & DataSegmentNum & "', '" & FileName & "', '" & Length & "' );")
561: Catch ex As Exception
562: TraceLog(context, "1c_exchange (SaveSendingFileName): " & ex.Message)
563: context.Response.ContentType = "text/plain"
564: context.Response.Write("failure" & vbCrLf & "BAD SQL COMMAND:" & ex.Message)
565: Exit Sub
566: End Try
567: End Sub
568:
569: Sub SaveSendingResult(ByVal context As HttpContext, ByVal SessionID As String, ByVal ResultCode As String)
570: Try
571: PG1.PG.ExecRDR("UPDATE ""_Delmar"".""1C_Exchange"" SET ""Result"" = '" & ResultCode & "' where ""SessionID""='" & SessionID & "';")
572: Catch ex As Exception
573: TraceLog(context, "1c_exchange (SaveSendingResult): " & ex.Message)
574: context.Response.ContentType = "text/plain"
575: context.Response.Write("failure" & vbCrLf & "BAD SQL COMMAND:" & ex.Message)
576: Exit Sub
577: End Try
578: End Sub
579:
580:
581: #End Region
582:
583: Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
584: Get
585: Return False
586: End Get
587: End Property
588:
589: End Class
Как видите, мой код 1С-шлюза исключительно хорошо самодокументирован и, думаю, в дополнительном комментировании не нуждается. Скорее требуется четко обьяснить внешние условия исполнения этого кода.
Прежде всего это наличие конечно 7zip и необхоимых параметров в конфиге сайта:
<add key="1С_FullLog" value="True" /> <add key="Cache" value="Temp" /> <add key="ZipPath" value="C:\Program Files (x86)\7-Zip\7z.exe" />
Особое внимание при разработке шлюза я уделил журналированию. Шлюз создает подробнейший журнал работы. Журналивание ведется в такую таблу.
1: CREATE SEQUENCE "_Delmar".debug_i_seq
2: INCREMENT 1
3: MINVALUE 1
4: MAXVALUE 9223372036854775807
5: START 1
6: CACHE 1;
7:
8: CREATE TABLE "_Delmar"."TerminalError"
9: (
10: i integer NOT NULL DEFAULT nextval('"_Delmar".debug_i_seq'::regclass),
11: crdate timestamp without time zone DEFAULT now(),
12: status integer,
13: "TerminalID" character varying(250),
14: txt character varying(4000),
15: CONSTRAINT iiiiiiiiii PRIMARY KEY (i)
16: )
17: WITH (
18: OIDS=FALSE
19: );
Вторая табла используется для сборки единого ZIP-файла из маленьких отдельных фрагментов, которые передает 1С. В ней также можно видеть общий итог сеанса связи с 1С в обоих направлениях:
1: CREATE SEQUENCE "_Delmar"."1C_i_seq"
2: INCREMENT 1
3: MINVALUE 1
4: MAXVALUE 9223372036854775807
5: START 27
6: CACHE 1;
7:
8: CREATE TABLE "_Delmar"."1C_Exchange"
9: (
10: i integer NOT NULL DEFAULT nextval('"_Delmar"."1C_i_seq"'::regclass),
11: "CrDate" date NOT NULL,
12: "DataSegmentNum" character varying(255) NOT NULL,
13: "FileName" character varying(255) NOT NULL,
14: "Length" integer,
15: "SessionID" character varying(255),
16: "Result" character varying(255),
17: CONSTRAINT "1C_pkey" PRIMARY KEY (i)
18: )
19: WITH (
20: OIDS=FALSE
21: );
Разумеется имя схемы безопастности PostgreSQL, которое стоит у меня "Delmar" - вы должны заменить на имя своей схемы безопасности, прием как в коде, так и в SQL. Это имя я привел просто для примера - чтобы код шлюза был полностью корректным.
Надеюсь вы понимаете, как создать две таблички и внести в конфиг три параметра, чтобы заработал код моего шлюза. Вам собственно остается только наслаждаться, работая с XML своей прикладной системы, а всю черновую работу выполнит мой шлюз.
Как я написал вначале, код работы с XML может быть монотонным и весьма обьемым - однако в этом направлении никаких полезных универсальных решений предсказать невозможно - слишком многое зависит от специфики вашей системы. В моем конкретном случае XML-парсеры я построил вот по такому принципу - сделал сначала вот такую крошечную обвязку, которая гарантировано (не падая) достает значение заданного XML-тега:
1: Public Function GetTagText(ByVal XqueryPatch As String, ByVal Node As Xml.XmlNode) As String
2: If Node.SelectNodes(XqueryPatch).Count > 0 Then
3: If Node.SelectNodes(XqueryPatch & "/text()")(0) IsNot Nothing Then
4: Return Node.SelectNodes(XqueryPatch & "/text()")(0).Value
5: Else
6: Return ("")
7: End If
8: Else
9: Return ("")
10: End If
11: End Function
Потом идут довольно обьемные определения вот такого плана в которых из XML выковыриваются важные параметры 1С, управляющие алгоритмом парсинга XML. Затем следует выполнить основной алгоритм XML-парсера (тоже довольно обьемный) предствляющий собой вложенную систему циклов примерно такого плана. Основной алгоритм вызывает операции доступа к базе (это тоже код приличного обьема). Конечно, весь этот весьма обьемный код публиковать нет никакого смысла, ибо это с одной стороны коммерческая тайна проекта, с другой стороны - это монотонный и ничем не поучительный код.
Однако изюминка этого шлюза - слой программного обеспечения самого низшего уровня, непосредственно взаимодействующий с 1С - этот код, как вы видите, компактный и совершенно универсальный.

<SITEMAP> <MVC> <ASP> <NET> <DATA> <KIOSK> <FLEX> <SQL> <NOTES> <LINUX> <MONO> <FREEWARE> <DOCS> <ENG> <CHAT ME> <ABOUT ME> < THANKS ME> |