0

This is a continuing quest to get api calls to work in in VB.NET application using RestSharp. When I started this effort I created a Windows forms project so I could manually enter values and test the approach. The first task in running the application is to retrieve a oauth user token from eBay given the refresh token for that user. In the form I enter the refresh token and run the procedure cmdGetCode which is executed by a button press. The procedure returns the user token information in JSON format. This works and returns the usertoken JSON. See code below.

Imports System.Net
Imports RestSharp
Imports System.Text.Json
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Public Class Form1
    Public uT As userToken
    Dim sBaseURL As String = My.Settings.baseURL
    Dim ClientSec As String = My.Settings.ClientSec
    Dim ClientID As String = My.Settings.ClientID
    Dim sTokURL As String = My.Settings.tokenURL
    Dim sFulfillmentURL As String = My.Settings.fulfillmentURL
    Dim dsFulfilment As DataSet
    Dim client As RestClient = New RestClient(sBaseURL)
    Async Sub cmdGetCode_Click(sender As Object, e As EventArgs) Handles cmdGetCode.Click
        Dim Base64Auth As String = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(ClientID & ":" & ClientSec))
        Dim request As RestRequest = New RestRequest(sTokURL, Method.Post)
        With request
            .AddHeader("Content-Type", "application/x-www-form-urlencoded")
            .AddHeader("Authorization", "Basic " & Base64Auth)
            .AddParameter("grant_type", "refresh_token")
            .AddParameter("refresh_token", txtRefreshToken.Text)
            .AddParameter("Scope", "https://api.ebay.com/oauth/api_scope/sell.inventory")
        End With
        Try
            Dim response = Await client.PostAsync(request)
            If response.StatusCode = HttpStatusCode.OK Then
                Dim sR As String = response.Content
                'uT = JsonSerializer.Deserialize(Of userToken)(sR)
                uT = New userToken
                JsonConvert.PopulateObject(sR, uT)
                txtAuthToken.Text = uT.access_token.ToString
                txtDuration.Text = uT.expires_in.ToString
            Else
                txtRefreshToken.Text = "Failed to GetType tokens"
            End If
        Catch ex As Exception
            txtRefreshToken.Text = "Failed to GetType tokens"
        End Try
    End Sub

Given that this worked I then created a class in my application, clsUserToken, to provide the same function. This class in instantiated on application start and then executed to retrieve the user token for each of our users by updating the refresh token and executing the procedure getUserToken in the class. Code follows:


Imports System.Net
Imports RestSharp
Imports System.Text.Json
Imports Newtonsoft.Json

Public Class clsUserToken

    Property uT As userToken
    Dim sBaseURL As String
    Dim ClientSec As String
    Dim ClientID As String
    Dim sTokURL As String
    Dim client As RestClient
    Property refreshToken

    Sub New()
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
        sBaseURL = My.Settings.baseURL
        client = New RestClient(sBaseURL)
        ClientSec = My.Settings.ClientSec
        ClientID = My.Settings.ClientID
        sTokURL = My.Settings.tokenURL

    End Sub

    Async Sub getUserToken()

        Dim Base64Auth As String = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(ClientID & ":" & ClientSec))
        Dim request As RestRequest = New RestRequest(sTokURL, Method.Post)
        With request
            .AddHeader("Content-Type", "application/x-www-form-urlencoded")
            .AddHeader("Authorization", "Basic " & Base64Auth)
            .AddParameter("grant_type", "refresh_token")
            .AddParameter("refresh_token", refreshToken)
            .AddParameter("Scope", "https://api.ebay.com/oauth/api_scope/sell.inventory")
        End With
        Try
            Dim response = Await client.PostAsync(request)
            'Dim responce = Await client.ExecuteAsync(request)
            If response.StatusCode = HttpStatusCode.OK Then
                Dim sR As String = response.Content
                JsonConvert.PopulateObject(sR, uT)
                uT.fail = False
            Else
                uT.fail = True
            End If
        Catch ex As Exception
            ErrorLogger.WriteToErrorLog(ex.Message, ex.StackTrace, "clsUsertToken_getUserToken")
        End Try
    End Sub
End Class

This does not work. The application drops out of the procedure when the line Dim response = Await client.PostAsync(request) is executed. No error message, nothing in the catch.

There is really no difference in the code between the two modules so I am at a loss as to what is happening. Any thoughts or suggestions would be appreciated.

Here is how the class is called.

Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Module Run
    'Public clsUT As clsUserToken = New clsUserToken
    Dim tok As String
    Public UT As userToken
    Public policies As clsPolicies
    Dim d As Dac
    'Public T As test

    Public Sub main()
        Call getCode()
        UT = New userToken 'Instansiate userToken class. Holds user token properties
        policies = New clsPolicies
        d = New Dac
        Call getPolicies()
    End Sub

    Sub getPolicies()

        Dim dt As DataTable
        Dim dr As DataRow
        Dim refreshToken As String
        'Dim token As userToken = New userToken 
        Dim clsUT As New clsUserToken
        Dim sName As String
        Dim iCredentialID As Integer
        d.Connstring = My.Settings.conn
        dt = d.ExecuteDataTable("sp_GetSellers")
        If dt.Rows.Count > 0 Then
            For Each dr In dt.Rows
                refreshToken = dr.Item(2).ToString
                iCredentialID = dr.Item(0)
                sName = dr.Item(1).ToString
                clsUT.uT = UT 'Pass token to Get user token class
                clsUT.refreshToken = refreshToken

                clsUT.getUserToken()
                If UT.fail = False Then
                    tok = UT.access_token
                    policies.sUserToken = tok
                    policies.fulfillmentPolicies()
                    policies.returnPolicies()
                    policies.paymentPolicies()
                    updateFullfillment(policies.sFulfillmentContent, iCredentialID)
                    updatePayment(policies.sPaymentContent, iCredentialID)
                    updateReturn(policies.sReturnContent, iCredentialID)
                End If

            Next
        End If
    End Sub
DougM
  • 109
  • 3
  • 16
  • How are you calling `getUserToken`? Can you show the code where you use a `clsUserToken` instance? – Étienne Laneville May 25 '22 at 04:15
  • Here is how it is called. – DougM May 25 '22 at 15:17
  • 1
    I'd make `getPolicies` Async and Await the `clsUT.getUserToken()` call. – Étienne Laneville May 25 '22 at 15:31
  • The `Call` keyword is a noop and serves no purpose at all in modern VB (since at least 2002... 20 years now!). The only reason it's still there is a convenience when porting forward older code, and it should not be used for new development. Also, modern VB should _always_ use parentheses when calling methods. – Joel Coehoorn May 25 '22 at 15:43
  • clsUT already uses Async Await in the call to get the token. Seems redundant to do again. As to the use of Call, just my old ways. I know I don't need it and will clean up later. The basic question I still have is why the process works when in a forms code module, but won't work in the class. Essentially the same code. – DougM May 25 '22 at 16:59

1 Answers1

0

Async is "viral", in that if you call an Async method your calling code must also be Async, and it must Await the result of the method you are calling. Thus one Async use tends to force a lot of other dependant code to also be Async, which in turn can cause other code to be Async, and it can infest an entire code base.

It works in the Form's button click event because Visual Studio is smart enough to see the event handler method is Async and call it appropriately when the event is raised.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • I can see that but I'm not sure what to change in the class. The only async is around the web request. Guess I'm not as smart as the form. – DougM May 25 '22 at 22:49
  • You don't change anything in the class. You change any code that calls the class's Async method, meaning the `getPolicies()` method must now be async, and `Await clsUT.getUserToken()`. Also, void (Sub) Async methods are kind of bad, so you should probably make `getPolicies()` return a result, rather than set global variables, which is better practice anyway, and then the code that calls `getPolicies()` must also become Async. – Joel Coehoorn May 26 '22 at 13:47
  • Finally got it working. Got rid of the class clsUserToken and went to a standard module with async function GetCode() As Task. Then in the execution used GetCode().Wait. Found a good reference site and examples [link](https://www.programmingnotes.org/6229/vb-net-how-to-send-post-process-a-rest-api-web-request-using-vb-net). – DougM May 27 '22 at 22:42