Not sure how to explain this but I have a property (User) in the Session object which is defined as a Structure type. So if I reference Session.User.ID, it will give me an Integer value of the User’s ID. Likewise, the built-in Session.ClientTime returns a Date object of the current time based on the client’s browser.
I am now developing the API request routines and am running into an issue where upon receiving the first PageRecevied event from an HTTPS.Post request, I receive a NilObjectException whenever I access a property of the Session object. The ErrorNumber reported is 0 and the Message is blank.
Sub PageReceived(url as string, httpStatus as integer, headers as internetHeaders, content as string)
MsgBox CStr(Session.User.TimezoneID) + " (PageReceived)" <<<<< NilObjectException
MsgBox Session.ClientTime.SQLDateTime + " ((PageReceived)" <<<<< NilObjectException
Prior to sending the Post request, both Session MsgBox statements execute without error.
You are going to want to do that synchronously. Offhand I do not believe by the time the event fires that the HTTP Socket has any idea what the Session object is.
Is the session embedded in the page? Otherwise, it has no way of knowing. You could give it a property that holds a session reference, I suppose.
Session is the Xojo WebSession object that hold properties and information regarding the current session. The property User I referred to is defined in that object. After the Post call is made, I no longer have access to anything in that object from any module, container, page, etc.
Right because when you run that asynchronously the framework threads it behind the scenes. Well that new thread is not related to your current Session thread. Therefore the Session thread you expect to find is not available. You could keep a reference to it like Tim said or you could do the work synchronously. Perhaps if you dropped the socket on the web page it might work.
HTTPSocket events fire on the main thread and there’s no session info there. Now what you could do is store the SessionID and the RemoteIP on the socket subclass (not the session itself because you’ll probably get a memory leak). When the event comes back, you can get the right session back with:
[code]Dim sess as WebSession = App.SessionWithIdentifier(SessionID, RemoteIP)
dim w as new WebSessionContext(sess)
// Do the stuff you need…
I’m seeing there are two thought paths here (correct me if I’m wrong).
One is, as Greg and Tim are suggesting more suited for an API Engine such as RESTful requests which I assume is asynchronous. I am developing a companion RESTful API webapp which this is definitely the way to go and would allow (shared) access to the main thread’s properties, classes, etc.
The other is, as Phillip suggests to do the work synchronously which would be typical of a client side application (webapp) where you are making calls TO an API and receiving data back to work with (in the same client session).
Looking at my original post, I wasn’t clear on which of these I am trying to accomplish. I’m designing the billing portion of my web app which makes RESTful API calls to a 3rd party billing service. The process a user goes through is log into the app, go to their profile, and say, create a credit card. I gather the information, make the API call to the billing service and upon receiving the response, process the information (save it in my database, display any errors, update the Session.User data structure, etc.)
I have defined an HTTPS1 object with it super set as HTTPSecureSocket. I have dropped the HTTPS1 object on the pgProfile web page naming it HTTPSBilling (ConnectionType = 2, Secure = Yes). From an event of the ccCreditCard1 container control on the same page, I set the form (Dictionary) key/value pairs and execute the HTTPSBilling.Post method. In the HTTPSBilling.PageReceived event (this is where I loose the Session), I determine which Control Container on the Web Page is to receive the response. In this scenario, the user is creating/attaching a credit card so I call a DataReceived public method on the ccCreditCard1 control and pass off the response. If the action was successful, I update the user’s profile in the database and the Session.User.LastUpdate variable. I use the Session.User property (Structure) to track various user profile settings throughout the user’s session.
Basically, I’m thinking of the API call as being synonymous to a database call. User enters data, I call a method to create the credit card, and process the response received. How do I do this without leaving the current client session? I assume synchronously. I recall reading somewhere about this but can’t remember where or how to switch to the sync method.
Sorry for the short novel.
Well I don’t believe you can do it synchronously with the Post method. That’s outdated anyway. You will want to use the ‘SendRequest’ method and pass ‘POST’ as the method parameter. If you look at the documentation you can see that if you do a:
Dim response As String
response = HTTPSocket.SendRequest([parameters], 5)
This will run it synchronously. The key is setting a response object and also at the end you have to add an integer for the timeout. In the example above I’m saying wait 5 seconds (thread is paused while waiting) until a response is returned. Usually it will happen faster. You want your timeout to be long enough that you get a response but short enough that if a response never comes you can handle it appropriately.
Hope that helps.
Thanx Phillip. Your explanation helped a bunch. I have taken the example one step further and have put it in a Module which returns a String. Now to figure out how to add a ptr for a callback to a function/sub so I can return the URL, status, and headers with the response.
[code]Function APISendRequest(form As Dictionary, Optional ID As String) As String
Dim Request, Response As String
Dim response As String
Dim HTTPSStripe As new HTTPSecureSocket
Dim status As Integer
HTTPSStripe.SetRequestHeader(“Authorization”, "Bearer " + App.StripeKey)
HTTPSStripe.ConnectionType = 2
HTTPSStripe.Secure = True
HTTPSStripe.Yield = True
Response = HTTPSStripe.SendRequest("POST", App.Options.StripeAPI + Request, 5)
status = HTTPSStripe.HTTPStatusCode