424 lines
19 KiB
VB.net
424 lines
19 KiB
VB.net
Imports System.Xml
|
|
Imports System.Net
|
|
Imports System.IO
|
|
Imports System.Web
|
|
Imports System.Text.RegularExpressions
|
|
Imports tsUtilidades.Extensiones.BinaryReaderExtensions
|
|
|
|
''' <summary>
|
|
''' Esta clase es una alternativa para cuando no puedes usar un cliente WCF (Referencia de Servicio) o la interfaz generada por wdsl.exe de .Net Framework 2.0.
|
|
''' Permite invocar métodos de un servicio web conociendo la URL del "endpoint" del servicio web, pero con la pega de que los mensajes que se envían para
|
|
''' invocar los servicios deben ser generados manualmente.
|
|
''' </summary>
|
|
Public Class ClienteServicioWeb
|
|
Public Property Url() As String
|
|
Get
|
|
Return m_Url
|
|
End Get
|
|
Set(value As String)
|
|
m_Url = value
|
|
End Set
|
|
End Property
|
|
Private m_Url As String
|
|
Public Property Method() As String
|
|
Get
|
|
Return m_Method
|
|
End Get
|
|
Private Set(value As String)
|
|
m_Method = value
|
|
End Set
|
|
End Property
|
|
Private m_Method As String
|
|
Private RequestString As String = [String].Empty
|
|
Private Username As String = [String].Empty
|
|
Private Password As String = [String].Empty
|
|
Private sAuth As String = [String].Empty
|
|
Public Params As New Dictionary(Of String, String)()
|
|
Public ResponseSOAP As XDocument = XDocument.Parse("<root/>")
|
|
Public ResultXML As XDocument = XDocument.Parse("<root/>")
|
|
Public ResultString As String = [String].Empty
|
|
|
|
Public Sub New()
|
|
Url = [String].Empty
|
|
Method = [String].Empty
|
|
End Sub
|
|
Public Sub New(baseUrl As String)
|
|
Url = baseUrl
|
|
Method = [String].Empty
|
|
End Sub
|
|
Public Sub New(baseUrl As String, methodName As String)
|
|
Url = baseUrl
|
|
Method = methodName
|
|
End Sub
|
|
|
|
#Region "Métodos públicos"
|
|
''' <summary>
|
|
''' Añade un parámetro a la llamada al método del servicio web.
|
|
''' </summary>
|
|
''' <param name="name">Nombre del parámetro (sensible a mayúsculas).</param>
|
|
''' <param name="value">Valor del parámetro.</param>
|
|
''' <remarks>Intermanente estos parámetros se mandan como parámetros POST.</remarks>
|
|
Public Sub AddParameter(name As String, value As String)
|
|
Params.Add(name, value)
|
|
End Sub
|
|
''' <summary>
|
|
''' Añade credenciales para autenticarse en el servicio web usando autenticación HTTP básica.
|
|
''' </summary>
|
|
''' <param name="username"></param>
|
|
''' <param name="password"></param>
|
|
''' <remarks>Se usa codificación UTF-8 para transmitir estas credenciales.</remarks>
|
|
Public Sub AddBasicAuthenticationCredential(ByVal username As String, ByVal password As String)
|
|
Me.sAuth = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username & ":" + password))
|
|
Me.Username = username
|
|
Me.Password = password
|
|
End Sub
|
|
Public Sub SetRequestString(ByVal sRequest As String)
|
|
Me.RequestString = sRequest
|
|
End Sub
|
|
Public Sub Invoke(Optional ByVal ignoreSSLErrors As Boolean = False)
|
|
Invoke(Method, True, ignoreSSLErrors = ignoreSSLErrors)
|
|
End Sub
|
|
''' <summary>
|
|
''' Invoca un método del servicio web, identificado por su nombre.
|
|
''' </summary>
|
|
''' <param name="methodName">Nombre del método del servicio web.</param>
|
|
Public Sub Invoke(methodName As String,
|
|
Optional ByVal ignoreSSLErrors As Boolean = False)
|
|
|
|
Invoke(methodName, True, ignoreSSLErrors = ignoreSSLErrors)
|
|
End Sub
|
|
''' <summary>
|
|
''' Limpia todos los datos del objeto excepto la URL del endpoint del servicio web.
|
|
''' Es útil para realizar subsecuentes llamadas al mismo servicio web, con otros datos o invocando a otros métodos.
|
|
''' </summary>
|
|
Public Sub CleanLastInvoke()
|
|
ResponseSOAP = InlineAssignHelper(ResultXML, Nothing)
|
|
ResultString = InlineAssignHelper(Method, [String].Empty)
|
|
Params = New Dictionary(Of String, String)()
|
|
End Sub
|
|
#End Region
|
|
#Region "Métodos auxiliares públicos"
|
|
''' <summary>
|
|
''' Remove all xmlns:* instances from the passed XmlDocument to simplify our xpath expressions
|
|
''' </summary>
|
|
Public Shared Function RemoveNamespaces(oldXml As XDocument) As XDocument
|
|
' FROM: http://social.msdn.microsoft.com/Forums/en-US/bed57335-827a-4731-b6da-a7636ac29f21/xdocument-remove-namespace?forum=linqprojectgeneral
|
|
Try
|
|
Dim newXml As XDocument = XDocument.Parse(Regex.Replace(oldXml.ToString(), "(xmlns:?[^=]*=[""][^""]*[""])", "", RegexOptions.IgnoreCase Or RegexOptions.Multiline))
|
|
Return newXml
|
|
Catch [error] As XmlException
|
|
Throw New XmlException([error].Message + " at WSCUtils.RemoveNamespaces")
|
|
End Try
|
|
End Function
|
|
''' <summary>
|
|
''' Remove all xmlns:* instances from the passed XmlDocument to simplify our xpath expressions
|
|
''' </summary>
|
|
Public Shared Function RemoveNamespaces(oldXml As String) As XDocument
|
|
Dim newXml As XDocument = XDocument.Parse(oldXml)
|
|
Return RemoveNamespaces(newXml)
|
|
End Function
|
|
''' <summary>
|
|
''' Elimina todos los espacios de nombres de un documento XML
|
|
''' </summary>
|
|
Public Shared Function EliminarEspaciosDeNombres(xDocumento As XDocument) As XDocument
|
|
' FROM: http://social.msdn.microsoft.com/Forums/en-US/bed57335-827a-4731-b6da-a7636ac29f21/xdocument-remove-namespace?forum=linqprojectgeneral
|
|
Try
|
|
Dim sRespuestaSinNamespaces As String = System.Text.RegularExpressions.Regex.Replace(xDocumento.ToString(),
|
|
"(xmlns:?[^=]*=[""][^""]*[""])", "",
|
|
System.Text.RegularExpressions.RegexOptions.IgnoreCase Or System.Text.RegularExpressions.RegexOptions.Multiline)
|
|
sRespuestaSinNamespaces = System.Text.RegularExpressions.Regex.Replace(sRespuestaSinNamespaces,
|
|
"<\w+:", "<",
|
|
System.Text.RegularExpressions.RegexOptions.IgnoreCase Or System.Text.RegularExpressions.RegexOptions.Multiline)
|
|
sRespuestaSinNamespaces = System.Text.RegularExpressions.Regex.Replace(sRespuestaSinNamespaces,
|
|
"</\w+:", "</",
|
|
System.Text.RegularExpressions.RegexOptions.IgnoreCase Or System.Text.RegularExpressions.RegexOptions.Multiline)
|
|
Return XDocument.Parse(sRespuestaSinNamespaces)
|
|
Catch [error] As XmlException
|
|
Throw New XmlException([error].Message + " at WSCUtils.EliminarEspaciosDeNombres")
|
|
End Try
|
|
End Function
|
|
''' <summary>
|
|
''' Converts a string that has been HTML-enconded for HTTP transmission into a decoded string.
|
|
''' </summary>
|
|
''' <param name="escapedString">String to decode.</param>
|
|
''' <returns>Decoded (unescaped) string.</returns>
|
|
Public Shared Function UnescapeString(escapedString As String) As String
|
|
Return HttpUtility.HtmlDecode(escapedString)
|
|
End Function
|
|
#End Region
|
|
#Region "Métodos auxiliares privados"
|
|
Private Function GetCredential() As CredentialCache
|
|
'ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3
|
|
Dim credentialCache As New CredentialCache()
|
|
credentialCache.Add(New System.Uri(Me.Url), "Basic", New NetworkCredential(Me.Username, Me.Password))
|
|
Return credentialCache
|
|
End Function
|
|
''' <summary>
|
|
''' Checks if the WebService's URL and the WebMethod's name are valid. If not, throws ArgumentNullException.
|
|
''' </summary>
|
|
''' <param name="methodName">Web Method name (optional)</param>
|
|
Private Sub AssertCanInvoke(Optional methodName As String = "")
|
|
If Url = [String].Empty Then
|
|
Throw New ArgumentNullException("You tried to invoke a webservice without specifying the WebService's URL.")
|
|
End If
|
|
If (methodName = "") AndAlso (Method = [String].Empty) Then
|
|
Throw New ArgumentNullException("You tried to invoke a webservice without specifying the WebMethod.")
|
|
End If
|
|
End Sub
|
|
''' <summary>
|
|
''' Invokes a Web Method, with its parameters encoded OrElse not.
|
|
''' </summary>
|
|
''' <param name="methodName">Name of the web method you want to call (case sensitive)</param>
|
|
''' <param name="encode">Do you want to encode your parameters? (default: true)</param>
|
|
Private Function Invoke(methodName As String, encode As Boolean,
|
|
Optional ByVal ignoreSSLErrors As Boolean = False) As String
|
|
|
|
AssertCanInvoke(methodName)
|
|
Dim soapStr As String = "<?xml version=""1.0"" encoding=""utf-8""?>" & vbCr & vbLf & " <soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""" & vbCr & vbLf & " xmlns:xsd=""http://www.w3.org/2001/XMLSchema""" & vbCr & vbLf & " xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">" & vbCr & vbLf & " <soap:Body>" & vbCr & vbLf & " <{0} xmlns=""http://tempuri.org/"">" & vbCr & vbLf & " {1}" & vbCr & vbLf & " </{0}>" & vbCr & vbLf & " </soap:Body>" & vbCr & vbLf & " </soap:Envelope>"
|
|
|
|
Dim req As HttpWebRequest = DirectCast(WebRequest.Create(Url), HttpWebRequest)
|
|
|
|
If ignoreSSLErrors Then
|
|
req.ServerCertificateValidationCallback() = Function(sender, certificate, chain, sslPolicyErrors) True
|
|
End If
|
|
|
|
req.Headers.Add("SOAPAction", (Convert.ToString("""http://tempuri.org/") & methodName) + """")
|
|
req.ContentType = "text/xml;charset=""utf-8"""
|
|
req.Accept = "text/xml"
|
|
req.Method = "POST"
|
|
|
|
'If Not String.IsNullOrWhiteSpace(sAuth) Then
|
|
' req.Headers.Add("Authorization", "Basic " + sAuth)
|
|
'End If
|
|
|
|
If Not String.IsNullOrWhiteSpace(Me.Username) AndAlso Not String.IsNullOrWhiteSpace(Me.Password) Then
|
|
req.Credentials = GetCredential()
|
|
req.PreAuthenticate = True
|
|
End If
|
|
|
|
Using stm As Stream = req.GetRequestStream()
|
|
Dim postValues As String = ""
|
|
For Each param In Params
|
|
If encode Then
|
|
postValues += String.Format("<{0}>{1}</{0}>", HttpUtility.UrlEncode(param.Key), HttpUtility.UrlEncode(param.Value))
|
|
Else
|
|
postValues += String.Format("<{0}>{1}</{0}>", param.Key, param.Value)
|
|
End If
|
|
Next
|
|
|
|
soapStr = String.Format(soapStr, methodName, postValues)
|
|
Using stmw As New StreamWriter(stm)
|
|
stmw.Write(soapStr)
|
|
End Using
|
|
End Using
|
|
|
|
Using responseReader As New StreamReader(req.GetResponse().GetResponseStream())
|
|
Dim result As String = responseReader.ReadToEnd()
|
|
ResponseSOAP = XDocument.Parse(UnescapeString(result))
|
|
End Using
|
|
|
|
Me.ResultString = ResponseSOAP.ToString
|
|
Me.ResultXML = ResponseSOAP
|
|
|
|
Return ResponseSOAP.ToString
|
|
End Function
|
|
Public Function InvokeUsingRequestString(ByVal methodName As String, ByVal sRequest As String,
|
|
Optional ByVal cert As System.Security.Cryptography.X509Certificates.X509Certificate2 = Nothing,
|
|
Optional ByVal ignoreSSLErrors As Boolean = False) As XDocument
|
|
|
|
PreInvoke()
|
|
AssertCanInvoke(methodName)
|
|
|
|
Dim req As HttpWebRequest = DirectCast(WebRequest.Create(Url), HttpWebRequest)
|
|
|
|
If ignoreSSLErrors Then
|
|
req.ServerCertificateValidationCallback() = Function(sender, certificate, chain, sslPolicyErrors) True
|
|
End If
|
|
|
|
req.ContentType = "text/xml;charset=""utf-8"""
|
|
req.Accept = "text/xml"
|
|
req.Method = "POST"
|
|
|
|
If cert IsNot Nothing Then
|
|
req.ClientCertificates.Add(cert)
|
|
End If
|
|
|
|
'If Not String.IsNullOrWhiteSpace(sAuth) Then
|
|
' req.Headers.Add("Authorization", "Basic " + sAuth)
|
|
'End If
|
|
|
|
If Not String.IsNullOrWhiteSpace(Me.Username) AndAlso Not String.IsNullOrWhiteSpace(Me.Password) Then
|
|
req.Credentials = GetCredential()
|
|
req.PreAuthenticate = True
|
|
End If
|
|
|
|
Using stm As Stream = req.GetRequestStream()
|
|
Using stmw As New StreamWriter(stm)
|
|
stmw.Write(sRequest)
|
|
End Using
|
|
End Using
|
|
|
|
Dim respuesta As String = String.Empty
|
|
Dim resultado As String = String.Empty
|
|
Dim sbError As New Text.StringBuilder
|
|
sbError.AppendLine("<WebExceptionsList>")
|
|
|
|
Try
|
|
Using responseReader As New StreamReader(req.GetResponse().GetResponseStream())
|
|
respuesta = responseReader.ReadToEnd()
|
|
End Using
|
|
For Each linea As String In respuesta.Split(New String() {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
|
|
If Not (linea.StartsWith("Content-") OrElse linea.StartsWith("--uuid:")) Then
|
|
resultado += linea & Environment.NewLine
|
|
End If
|
|
Next
|
|
Catch exTO As TimeoutException
|
|
resultado = "<WebException>Tiempo de espera agotado. El servidor del servicio web no respondió a la petición.</WebException>"
|
|
Catch ex As WebException
|
|
Using response As WebResponse = ex.Response
|
|
Dim httpResponse As HttpWebResponse = DirectCast(response, HttpWebResponse)
|
|
If httpResponse IsNot Nothing Then
|
|
Try
|
|
sbError.AppendLine(String.Format("<WebException>({0}) {1}</WebException>", DirectCast(httpResponse.StatusCode, Integer), httpResponse.StatusDescription))
|
|
Catch ex2 As Exception
|
|
sbError.AppendLine("<WebException>Error desconocido del servidor del servicio web.</WebException>")
|
|
End Try
|
|
Else
|
|
sbError.AppendLine("<WebException>No hay objeto de tipo HttpWebResponse.</WebException>")
|
|
End If
|
|
Dim sRespuesta As String = String.Empty
|
|
|
|
If response IsNot Nothing Then
|
|
Try
|
|
Using data As Stream = response.GetResponseStream()
|
|
Using reader = New StreamReader(data)
|
|
sRespuesta = reader.ReadToEnd()
|
|
End Using
|
|
End Using
|
|
Catch ex2 As Exception
|
|
resultado = sbError.ToString
|
|
End Try
|
|
Else
|
|
sbError.AppendLine("<WebException>No hay objeto de tipo WebResponse.</WebException>")
|
|
End If
|
|
|
|
If sRespuesta IsNot Nothing AndAlso sRespuesta.Length > 0 Then
|
|
Dim xRespuesta As New XDocument
|
|
Try
|
|
xRespuesta = XDocument.Parse(sRespuesta)
|
|
resultado = sRespuesta
|
|
Catch ex3 As Exception
|
|
'Nada
|
|
End Try
|
|
If xRespuesta.ToString.Length < 1 Then
|
|
resultado = sbError.ToString
|
|
End If
|
|
Else
|
|
resultado = sbError.ToString
|
|
End If
|
|
sbError.AppendLine(String.Format("<WebException>{0}</WebException>", ex.ToString))
|
|
sbError.AppendLine("</WebExceptionsList>")
|
|
resultado = sbError.ToString
|
|
End Using
|
|
If String.IsNullOrWhiteSpace(resultado) Then
|
|
sbError.AppendLine(String.Format("<WebException>{0}</WebException>", ex.ToString))
|
|
sbError.AppendLine("</WebExceptionsList>")
|
|
resultado = sbError.ToString
|
|
End If
|
|
End Try
|
|
|
|
Dim unescapedString As String = UnescapeString(resultado.Trim)
|
|
Try
|
|
ResponseSOAP = XDocument.Parse(unescapedString.Trim)
|
|
Catch ex As XmlException
|
|
ResponseSOAP = XDocument.Parse(resultado.Trim)
|
|
End Try
|
|
|
|
PosInvoke()
|
|
|
|
Me.ResultString = ResponseSOAP.ToString
|
|
Me.ResultXML = ResponseSOAP
|
|
|
|
Return ResponseSOAP
|
|
End Function
|
|
''' <summary>
|
|
''' Realiza una petición a un servicio web usando un nombre de método, una cadena para la petición, y recogiendo la petición como un array de bytes.
|
|
''' </summary>
|
|
''' <param name="methodName">Nombre del método.</param>
|
|
''' <param name="sRequest">Cadena con la petición que se realizará al servicio web.</param>
|
|
''' <param name="cert"></param>
|
|
''' <returns>Un array de bytes con el contenido de la respuesta realizada al servicio web.</returns>
|
|
''' <remarks>Este método solo debería usarse con descargas que quepan en memoria RAM, teniendo en cuenta las posibles restricciones de memoria que el sistema operativo puda tener para procesos individuales.</remarks>
|
|
Public Function InvokeBinaryUsingRequestString(ByVal methodName As String, ByVal sRequest As String,
|
|
Optional ByVal cert As System.Security.Cryptography.X509Certificates.X509Certificate2 = Nothing,
|
|
Optional ByVal ignoreSSLErrors As Boolean = False) As Byte()
|
|
|
|
PreInvoke()
|
|
AssertCanInvoke(methodName)
|
|
|
|
Dim req As HttpWebRequest = DirectCast(WebRequest.Create(Url), HttpWebRequest)
|
|
|
|
If ignoreSSLErrors Then
|
|
req.ServerCertificateValidationCallback() = Function(sender, certificate, chain, sslPolicyErrors) True
|
|
End If
|
|
|
|
req.ContentType = "text/xml;charset=""utf-8"""
|
|
req.Accept = "text/xml"
|
|
req.Method = "POST"
|
|
|
|
If cert IsNot Nothing Then
|
|
req.ClientCertificates.Add(cert)
|
|
End If
|
|
|
|
'If Not String.IsNullOrWhiteSpace(sAuth) Then
|
|
' req.Headers.Add("Authorization", "Basic " + sAuth)
|
|
'End If
|
|
|
|
If Not String.IsNullOrWhiteSpace(Me.Username) AndAlso Not String.IsNullOrWhiteSpace(Me.Password) Then
|
|
req.Credentials = GetCredential()
|
|
req.PreAuthenticate = True
|
|
End If
|
|
|
|
Using stm As Stream = req.GetRequestStream()
|
|
Using stmw As New StreamWriter(stm)
|
|
stmw.Write(sRequest)
|
|
End Using
|
|
End Using
|
|
|
|
Dim respuesta As String = String.Empty
|
|
Dim resultado As Byte() = New Byte(0) {}
|
|
Dim sError As String = String.Empty
|
|
Dim sb As New Text.StringBuilder
|
|
|
|
Dim binaryBuffer As Byte()
|
|
|
|
Using binaryReader As New BinaryReader(req.GetResponse().GetResponseStream())
|
|
binaryBuffer = binaryReader.ReadAllBytes
|
|
End Using
|
|
|
|
resultado = binaryBuffer
|
|
|
|
PosInvoke()
|
|
|
|
Return resultado
|
|
End Function
|
|
''' <summary>
|
|
''' This method should be called before each Invoke().
|
|
''' </summary>
|
|
Friend Sub PreInvoke()
|
|
CleanLastInvoke()
|
|
' feel free to add more instructions to this method
|
|
End Sub
|
|
''' <summary>
|
|
''' This method should be called after each (successful OrElse unsuccessful) Invoke().
|
|
''' </summary>
|
|
Friend Sub PosInvoke()
|
|
' feel free to add more instructions to this method
|
|
|
|
End Sub
|
|
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
|
|
target = value
|
|
Return value
|
|
End Function
|
|
#End Region
|
|
End Class |