VB.net POST method using HTTPclient - vb.net

Hello I am trying to access a webform with data. However my code does not have error but its not working. It is supposed to send me a text message using the Web API provided by the telco carrier. Any assistance and advise will be greatly appreciated.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
Dim client As New HttpClient
Dim url = $"some url"
Dim data2 = "{""username"": ""someuname"", ""password"":""somepass"", ""msisdn:""some number"", ""content:""Hello, this is a sample broadcast"", ""shortcode_mask:""somemask""}"
Dim payload = Newtonsoft.Json.JsonConvert.SerializeObject(data2)
Dim buffer = Encoding.UTF8.GetBytes(payload)
Dim bytes = New Net.Http.ByteArrayContent(buffer)
bytes.Headers.ContentType = New Net.Http.Headers.MediaTypeHeaderValue("application/json")
Dim request = client.PostAsync(url, bytes)
Catch Ex As Exception
Console.WriteLine()
End Try
End Sub
End Class

There are a few issues in your code. Let me start with the most important one first:
Catch Ex As Exception
Console.WriteLine()
End Try
This code means: If an error occurs, throw away all useful information about the error that could help me find the cause, then write an empty line to the console and pretend nothing bad happened.
Don't do that. Remove the whole try-catch block, then re-run your code again. If you get an exception: That's great, because .NET is now telling you what is wrong with your code! If you understand the error message, use it to fix your code. If you don't understand it, feel free to ask the community on StackOverflow.
Second issue:
This line
Dim request = client.PostAsync(url, bytes)
starts an asynchronous web request. You don't wait for the result, so you don't know whether it succeeded or not.
You're supposed to Await async methods, but if you aren't familiar with the async/await pattern yet, I won't be able to give you all the necessary background in the single StackOverflow answer. Until you have familiarized youself with async/await, you can synchronously wait for the result by accessing the Result property of the Task that has been returned:
Dim response = client.PostAsync(url, bytes).Result
response.EnsureSuccessStatusCode()
Third issue: You JSON-serialize something which is already JSON. If your data is already JSON, just use it as the payload without calling SerializeObject.

Your data2 is a string. When you use the JSON library to serialize it, it probably isn't in the JSON structure that your URL endpoint expects. Create a Type, fill it with data, and pass that to your JsonCoverter

Thank you Heinzi, here is the final working code based on your concepts:
Public Class Form1
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim client As New HttpClient
Dim url = $"some url"
Dim data2 = "
{""username"": ""some uname"", ""password"": ""somepass"", ""msisdn"": ""somenumber"", ""content"": ""Hello, this is a sample broadcast"", ""shortcode_mask"" :""somemask""}"
Dim buffer = Encoding.UTF8.GetBytes(data2)
Dim bytes = New ByteArrayContent(buffer)
bytes.Headers.ContentType = New Headers.MediaTypeHeaderValue("application/json")
Dim request1 = Await client.PostAsync(url, bytes)
Dim response = client.PostAsync(url, bytes).Result
response.EnsureSuccessStatusCode()
End Sub
End Class
I can now receive text messages :)

Related

Why am I getting error: BC30452 Operator '&' is not defined for types 'String' and 'Image', for this code?

I have an application which has a server and a client file. On the server side, the server's camera (in my case, this is my camera), get's streamed to the client file, and then the clients camera (in my case, my camera because I'm using this for educational purposes only) get's streamed to the server file.
Right now I'm in the process of writing this program, but I'm getting an error when I'm trying to stream the client's camera.
This is what the client program looks like:
Now, on the Form_Load sub, this code is declared
Public Class videocallfinal
Public xxx As Integer
Public Yy As String = "|U|"
Dim pic As New Drawing.Bitmap(500, 400)
Dim Touchless As New TouchlessLib.TouchlessMgr
Dim camera1 As TouchlessLib.Camera = Touchless.Cameras.ElementAt(0)
Private Sub videocallfinal_Load(sender As Object, e As EventArgs) Handles MyBase.Load
q.Comet.Send("camerastream" & Yy & PictureBox2.Image)
PictureBox2.Image = camera1.GetCurrentImage
Touchless.CurrentCamera = camera1
Touchless.CurrentCamera.CaptureHeight = 130
Touchless.CurrentCamera.CaptureWidth = 130
Me.BringToFront()
Me.Text = "You're currently in a video call with " + videocall.Label6.Text
Label6.Text = videocall.Label6.Text
End Sub
So, as you can see, I'm trying to send the PictureBox image to the server file on this line of code:
q.Comet.Send("camerastream" & Yy & PictureBox1.Image)
Under PictureBox1.Image, I get this error
BC30452 Operator '&' is not defined for types 'String' and 'Image'.
I've done some re-search on this issue, but it's usually with different controls, and I've never dealt with an error like this with a PictureBox.
Can someone explain what I'm doing wrong here?
Thanks!
You can convert the image to a Base64 string by using a MemoryStream to get the bytes. The following untested code, written from the docs for MemoryStream and Image, will do exactly this:
Dim s As New MemoryStream
PictureBox1.Image.Save(s, System.Drawing.Imaging.ImageFormat.Bmp)
Dim asBase64 = System.Convert.ToBase64String(s.ToArray())

Get file size before download it in vb

I have been working on a web browser in visual basic..Now,what I want to do is to the get file size before download it and when I click download I want to to get the number of the alrady downloaded Mbs(watch the picture)
Thank's for help!
I've done some research, and this would probably be the most simple and "cleanest" way of getting a download's size (in bytes):
Public Function GetDownloadSize(ByVal URL As String) As Long
Dim r As Net.WebRequest = Net.WebRequest.Create(URL)
r.Method = Net.WebRequestMethods.Http.Head
Using rsp = r.GetResponse()
Return rsp.ContentLength
End Using
End Function
Credit to Reed Kimble, who told me to dispose the WebResponse in my initial MSDN question.
The above code will read the response headers of the file, rather than reading the body of it. This means that the file does not require to get downloaded, just to check it's size.
This is the reason to why some codes requires the file to actually get downloaded at first; they're reading the file's body rather than it's headers.
Hope this helps!
Use the WebClient ResponseHeaders:
Public Shared Function GetFileSize(url As String) As Long
Using obj As New WebClient()
Using s As Stream = obj.OpenRead(url)
Return Long.Parse(obj.ResponseHeaders("Content-Length").ToString())
End Using
End Using
End Function
Request file size before download it
The WebClient's DownloadProgressChanged event's args contains the property TotalBytesToRecieve. That tells you how many bytes the file you're downloading is.
Not the prettiest way, but if you want to get the size of the file before downloading you can start downloading the file then immediately cancel it:
Dim DownloadSize As Long
Private Sub CheckDownloadSize(ByVal URL As String)
WebClient.DownloadFile(URL, IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.Temp, "tempdownload.tmp"))
End Sub
Private WithEvents WebClient As New WebClient
Private Sub WebClient_DownloadProgressChanged(ByVal sender As Object, ByVal e As System.Net.DownloadProgressChangedEventArgs) Handles WebClient.DownloadProgressChanged
DownloadSize = e.TotalBytesToReceive
WebClient.CancelAsync()
End Sub
Otherwise, just remove the .CancelAsync() line.

How do I upload data to a server with valid SSL certificate using a client vb.net program?

I am working on a program and uploads a shipping manifest to a the shippers website. When I try to upload, I get a nondescript error back from their server, and when checking with the shipper, they tell me that "there is an issue with the SSL" I am using.
I've spent quite a bit of time piecing together code that, from what I seem to understand, is supposed to work, but I'm not making any progress. As far as I know everything else is fine with the upload, but there is a problem with my SSL certificate
If I understand what this code is supposed to do correctly, I should get a certificate from the shippers website, which allows certification to my program for a space of time during which I can upload the data. I'm really not sure that this is what my code is doing at all, but the only code examples I have seen show it something like this.
Here's my code with the URLs changed:
'This references a custom class that compiles the manifest I'm going to upload
Dim StringToUpload As String = Compile_Manifest(MyDate, UseTestDB)
Dim webClient As New System.Net.WebClient
webClient.Credentials = System.Net.CredentialCache.DefaultCredentials
'From what I understand,
'this is supposed to set up properties used in next section of code
System.Net.ServicePointManager.SecurityProtocol = Net.SecurityProtocolType.Ssl3
System.Net.ServicePointManager.ServerCertificateValidationCallback = _
AddressOf AcceptAllCertifications
'I can see that this reaches the server,
'but I don't know how it relates to the next section of code
'that actually uploads the manifest
Dim ServerRequest As System.Net.WebRequest = _
System.Net.WebRequest.Create("https://www.certify.some-shippper.com:443/somefolder")
Dim ServerResponse As System.Net.WebResponse
ServerResponse = ServerRequest.GetResponse()
ServerResponse.Close()
'This code works for the upload of the manifest,
'and it seems the above code is unrelated and does not use a SSL certificate.
'When this code runs I get the same error back from the shippers server,
'indicating an issue with my SSL, with or without the two sections of code above.
Dim StrResult As String = ""
Dim WrappedString As String = TransmitPLD.WrapPldFile(StringToUpload)
'This references a custom class that wraps the data to upload
'in information from the shipper.
Dim ByesToUpload As Byte() = _
System.Web.HttpUtility.UrlEncodeToBytes(WrappedString, _
System.Text.ASCIIEncoding.ASCII)
Dim Result As Byte() = _
webClient.UploadData("https://www.certify.some-shippper.com:443/somefolder", _
ByesToUpload)
StrResult = System.Web.HttpUtility.UrlDecode(Result, _
System.Text.ASCIIEncoding.ASCII)
MessageBox.Show(StrResult)
So it turns out I went about it the wrong way. I needed to upload my data through System.Net.WebRequest and it takes care of the certificates for me. Not implementing all the parts of the code I needed, it didn't handle the retrieval of the shipper's certificate.
In case anyone else gets confused about the matter like I did, here's my working code for anyone to see, adapt and use. My resource for fixing the code (and by that I mean starting from scratch) was the MSDN page for the WebRequest class, and it has code examples much the same as what I have below in C++, C#, and VB.NET and here is the link.
First there are some global variables that need to be set and class that needs to be created for to store the upload response:
' This is set in the function that Upload function
' and uploads the data in the ReadCallback sub
Private Shared WrappedString As String
' This is used to wait for the callback in the Upload function
Private Shared allDone As New Threading.ManualResetEvent(False)
Friend Class RequestState
' This class stores the request state of the request.
Public request As Net.WebRequest
Public Sub New()
request = Nothing
End Sub ' New
End Class ' RequestState
Then there is a sub needed for the upload part web request which will be called further below in the upload function:
Private Shared Sub ReadCallback(asynchronousResult As IAsyncResult)
Try
Dim myRequestState As RequestState = CType(asynchronousResult.AsyncState, RequestState)
Dim myWebRequest As Net.WebRequest = myRequestState.request
' End the request.
Dim streamResponse As IO.Stream = myWebRequest.EndGetRequestStream(asynchronousResult)
' Convert the string into a byte array.
Dim byteArray As Byte() = System.Text.Encoding.ASCII.GetBytes(WrappedString)
' Write the data to the stream.
streamResponse.Write(byteArray, 0, byteArray.Length)
streamResponse.Close()
' Allow the main thread to resume.
allDone.Set()
Catch ex As Exception
Throw New Exception("Error in " & Reflection.MethodBase.GetCurrentMethod.Name.ToString & " **" & ex.Message, ex)
End Try
End Sub ' ReadCallback
Finally, this is the function that should be called to upload the data, which uses all the code above:
Public Shared Function Upload(ByVal MyDate As Date) As String
Dim StrResult As String = ""
UploadSucess = False
Try
' This is my code that builds the manifest that I want to upload
Dim StringToUpload As String = Compile_PLD200(MyDate)
WrappedString = TransmitPLD.WrapPldFile(StringToUpload)
Dim myWebRequest As Net.WebRequest
myWebRequest = Net.WebRequest.Create("https://www.some.website.com:443/someplace")
' Create an instance of the RequestState and assign
' myWebRequest to it's request field.
Dim myRequestState As New RequestState()
myRequestState.request = myWebRequest
myWebRequest.ContentType = "multipart/mixed; boundary=BOUNDARY"
myRequestState.request.Method = "POST"
' Start the asynchronous 'BeginGetRequestStream' method call.
Dim r As IAsyncResult = CType(myWebRequest.BeginGetRequestStream(AddressOf ReadCallback, myRequestState), IAsyncResult)
' Pause the current thread until the async operation completes.
allDone.WaitOne()
' Send the Post and get the response.
Dim myWebResponse As Net.WebResponse = myWebRequest.GetResponse()
Dim streamResponse As IO.Stream = myWebResponse.GetResponseStream()
Dim streamRead As New IO.StreamReader(streamResponse)
Dim readBuff(256) As [Char]
Dim count As Integer = streamRead.Read(readBuff, 0, 256)
While count > 0
Dim outputData As New [String](readBuff, 0, count)
Console.WriteLine(outputData)
count = streamRead.Read(readBuff, 0, 256)
StrResult += outputData
End While
' Close the Stream Object.
streamResponse.Close()
streamRead.Close()
' Release the HttpWebResponse Resource.
myWebResponse.Close()
Catch ex As Exception
Throw New Exception("Error in " & Reflection.MethodBase.GetCurrentMethod.Name.ToString & " **" & ex.Message, ex)
End Try
Return StrResult
End Function ' Upload
Again here is the MSDN page for the WebRequest class which has a code example too.
Hope this helps anyone who was stuck like I was. And any criticisms as to the implementation of the code are welcome. This just happen to do what I want, I can't say it is the most efficient implementation.

Memory leak in XmlSerializer as used in VB.net

I seem to have a memory leak when using xmlSerializer in VB.net 2010.
The code below is the guts of a function which is called every 20 seconds or so forever.
Uncommenting the line 'serializer.Serialize(writer, response)' sees the memory usage climb.
Using writer As New StreamWriter(fileName)
Dim request = invoker.CreateRequest()
Dim response = invoker.Invoke(authorization, request)
If Not response Is Nothing Then 'maybe token timeout
Dim serializer = New XmlSerializer(GetType(TResponse)) ', New XmlRootAttribute("TResponse"))
Try
' serializer.Serialize(writer, response)
Catch e As InvalidOperationException
Dim msg As String = "ERROR: Failed to serialize the response:" & vbCrLf & e.Message
AddToRtfEventLog(category.ToUpper & " DATA " & msg, Drawing.Color.Red)
End Try
serializer = Nothing
AddToStatus(Convert.ToString("Response saved as ") & fileName & vbCrLf, Drawing.Color.LightGray, True)
End If
Return response
End Using
From internet reading, I gather this is a bug in the VB xmlSerializer and a possible solution is to use it in a caching manner. Unfortunately I have no idea how to do that so I wonder if someone can help me out here.
Firstly, am I doing it right in the first place (it works fine, just the memory issue) and
Secondly, if I am, can you show me how to fix the leak please?
Thanks
Malcom
Part 2-
With help from Jehof, I've now got the following cache class in place:
Public NotInheritable Class XmlSerializerCache
Private Sub New()
End Sub
Private Shared ReadOnly cache As New Dictionary(Of String, XmlSerializer)()
Public Shared Function Create(type As Type, root As XmlRootAttribute) As XmlSerializer
Dim key = [String].Format(CultureInfo.InvariantCulture, "{0}:{1}", type, root.ElementName)
If Not cache.ContainsKey(key) Then
cache.Add(key, New XmlSerializer(type, root))
End If
Return cache(key)
End Function
End Class
and have replaced the calls with :
Dim xmlRootAttribute = New XmlRootAttribute("TResponse")
Dim serializer = XmlSerializerCache.Create(GetType(TResponse), XmlRootAttribute)
Try
serializer.Serialize(writer, response)
Thats has improved the leak but is the correct way to do this? To me it seems that I am recreating the cache with each call?

How to send a class through named pipe in VB.net

Using VB2008, I have 2 applications on 2 computers that needs to communicate. I setup a named pipe and so far, it's working. I can send strings, back and forth between those 2 programs.
Now, I need to be able to send a class, or an object. I have read somewhere that Serialization is the way to go. So, on the client, I have:
Public Class cTest
Dim Var1 As Boolean
Dim Var2 As String = "a test"
Dim Var3 As New Collections.ArrayList
Public Sub AddItem(ByVal Item As String)
Var3.Add(Item)
End Sub
End Class
Private Sub Button8_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button8.Click
Dim oClasse As New cTest
oClasse.AddItem("StarWars")
oClasse.AddItem("StarTrek")
oPipe.SendToPipe(oClasse)
End Sub
End Class
Public Sub SendToPipe(ByVal test As cTest)
Dim xmlTest As New Xml.Serialization.XmlSerializer(GetType(cTest))
xmlTest.Serialize(pipeClient, test)
End Sub
On the server side (on the remote computer):
Public Function ReadString() As String
Dim len As Integer = 0
len = CType(ioStream.ReadByte(), Integer) * 256
len += CType(ioStream.ReadByte(), Integer)
Try
Dim serializer As New Xml.Serialization.XmlSerializer(GetType(cTest))
Dim Test As cTest
Test = CType(serializer.Deserialize(ioStream), cTest)
Catch ex As Exception
End Try
End Function
The serializer.Deserialize throw an exception saying the XML format is not correct.
what I'm doing wrong?
thanks for your time and help
finally, after a lot of testing and googling, I figured it out:
when using the following code on the client side it works:
Dim oClasse As New cTest
oClasse.AddItem("StarWars")
oClasse.AddItem("StarTrek")
Using PStream As IO.Pipes.NamedPipeClientStream = New IO.Pipes.NamedPipeClientStream(".", "VisionEnginePipeRead1", PipeDirection.Out, PipeOptions.None, TokenImpersonationLevel.None)
PStream.Connect()
Dim xmlTest As New Xml.Serialization.XmlSerializer(GetType(cTest))
xmlTest.Serialize(PStream, oClasse)
End Using
and this, on the server side:
Dim Test As cTest
Using PStream As NamedPipeServerStream = New NamedPipeServerStream(pipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.None)
PStream.WaitForConnection()
Dim serializer As New Xml.Serialization.XmlSerializer(GetType(cTest))
Test = CType(serializer.Deserialize(PStream), cTest)
End Using
If I were you I would use WCF Self Hosted Services and let the two communicate using callbacks
This started as a comment but I was running out of room. I am no expert on named pipe communications but, it has been a couple hours, and it may be that that is not really the problem.
You need to first test the serialization/deserialization in the same application. In other words start by taking the pipes out of the picture. This will isolate whether this is a serialization issue or a named pipe issue. Assuming that you code will work when done in the same application, then you need to compare the xml generated by the two applications - have them both do a Serialize. If the xml is identical (which I doubt) then pass it through the pipe and compare it again.
Going further out on a limb here but you may see that the namespace is different for the ctest object. If this is the case it may help to define your shared classes in a library which is shared between the two applications.