Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Problem with HTTP response

Status
Not open for further replies.

Richard Guelzow

Programmer
Jan 6, 2022
22
0
1
US
Hi everyone,

I don't seem able to figure out how to find the correct response for this particular API server. I am supposed to receive this json:

{
"species": "MjAyMS0wNi0wOFQxMToxMzoxNVpbR01UXQ",
"breeds": "MjAyMS0wNi0wOFQxMToxMDo0M1pbR01UXQ",
"genders": "MjAxNS0wNy0yNFQxMjowMDowMFpbR01UXQ",
"tests": "MjAyMy0xMC0yNVQxNTo1MzoyOC4wMDBa"
}

What I receive in both .responsetext & .responsebody [converted with ""+ .responsebody] is what looks like a long javascript document to me:

<!DOCTYPE html>
<html ng-app="vcp.app" lang="{{locale}}">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />

<script
src=" type="text/javascript"
></script>
<script
type="text/javascript"
src="/vendor/datadog/datadog-rum-config.js"
></script>

Here is my code with dummy credentials:
Code:
m.cUrl = '[URL unfurl="true"]https://partner.vetconnectplus.com/api/v1/ref/versions'[/URL]
m.cUser =  'username'  
m.cPass =  'password' 
m.cUP64 = STRCONV(m.cUser +':'+ m.cPass , 13)
m.cGettext = '{{"Authorization", "Basic " + ALLTRIM(m.cUP64) }}'

loHTTP = CREATEOBJECT("MSXML2.ServerXMLHTTP.6.0")
loHTTP.OPEN("GET", m.cUrl ,.F.)
loHTTP.SEND(m.cGettext)

Response:

loHTTP.status = 200
loHTTP.statustext = ‘OK’
loHTTP.responsetext = The javascript that I listed above.

Any thought to what I am doing wrong? Thanks in advance. Richard
 
Hows that API defined to be used?
Basic Auth authentication works differently than sending a body with your Gettext. So either the API calls this "basic auth" and doesn't mean the HTTP protocols basic authentication principle, or you got this wrong.

Basic authentication works this way:
Code:
Local lcURL, lcUser, lcPass, lcBasicAuth
lcUrl = '[URL unfurl="true"]https://partner.vetconnectplus.com/api/v1/ref/versions'[/URL]
lcUser =  'username'  
lcPass =  'password' 
lcBasicAuth = "Basic " + Strconv(m.lcUser+":"+m.lcPass,13)

loHTTP = CREATEOBJECT("MSXML2.ServerXMLHTTP.6.0")
loHTTP.open("GET", m.lcUrl ,.F.)
[highlight #FCE94F]loHTTP.setRequestHeader("Authorization",m.lcBasicAuth)[/highlight]
loHTTP.send()

I've not seen basic authetication put into the request body described anywhere, so that would be API specific and not by HTTP standards, at least not as I learned them.
And request headers are not sent with a request by send. What you add there is the request body, not the request head.

I assume the resulttext you get trhere is expected to be shown in a browser and would be the login form, as the basic authentication failed and your request therefore will be redirected to the login. As you get this into loHTTP.responseText, it's not a browser and the javascript isn't executed. A good API should no just redirect to some other web page, as API usage, often by libraries like CURL, isn't acting as a browser would do.

My guess: If you store this into a HTML file and open that with a browser, you'll be asked for login (And the html file will not work, as it's a local file and not coming from the vetconnectplus.com domain, but iun principle the whole response is just a redirect to a webpage, not the usual response to a request with correct authentication.

Chriss
 
Chris Thank you very much for your help. That makes sense that what I am receiving in a webpage. When I try your code I receive status code of 401 - unauthorized. Here is the documentation that I received for the API call using a slightly different url:

Code:
var client = new HttpClient();
var request = new HttpRequestMessage
{
  Method = HttpMethod.Get,
  RequestUri = new Uri("integration.vetconnectplus.com/api/v1/ref/versions"),
  Headers =
  {
    { "Authorization", "Basic {token}" }
  }
};
using (var response = await client.SendAsync(request)) {}

When I told the API support that your method along with the dummy credentials that they provided produced a 401 unauthorized status. They sent me this. A curl request that they executed with my dummy credentials.

Code:
I just did the get Versions API call in Postman using your credentials without issue
•	PIMS ID is 3aecc833-cf8f-4f2f-a30b-4903983f7XXX
•	VC+ Username is vetlink_XXX
•	VC+ Password is rH80J0Xm0XXX

curl --location '[URL unfurl="true"]https://integration.vetconnectplus.com/api/v1/ref/versions'[/URL] --header 'Authorization: Basic dmV0bGlua191czE6ckg4MEowWG0wNHdV' --header 'Cookie: identity=uR4yzVZ6RuPYva3FqmVlqvaj29lDjYg9Cx6PrXEmhfNi1GFXMmxcYflhUzJda5k2-V3PvW+-ieoZanp9tq5Au27k1mXs4BJihlNghMVH5G4fosI12x+PKS3R+4y5T-GxjYiph5uBdTZW5dQfBH5u4wMKKvebKI8lcvfwmV5iWy8'

Response
{
    "species": "MjAyMS0wNi0wOFQxMToxMzoxNVpbR01UXQ",
    "breeds": "MjAyMS0wNi0wOFQxMToxMDo0M1pbR01UXQ",
    "genders": "MjAxNS0wNy0yNFQxMjowMDowMFpbR01UXQ",
    "tests": "MjAyMy0xMC0yNVQxNTo1MzoyOC4wMDBa"
}

Next, I used your code with & without another setRequestHeader for the PIMS ID.

Code:
Local lcURL, lcUser, lcPass, lcBasicAuth, lcPimsId
lcUrl = '[URL unfurl="true"]https://partner.vetconnectplus.com/api/v1/ref/versions'[/URL]
lcUser =  'username'  
lcPass =  'password' 
lcBasicAuth = "Basic " + Strconv(m.lcUser+":"+m.lcPass,13)
lcPimsId = '3aecc833-cf8f-4f2f-a30b-4903983f7XXX'

loHTTP = CREATEOBJECT("MSXML2.ServerXMLHTTP.6.0")
loHTTP.open("GET", m.lcUrl ,.F.)
loHTTP.setRequestHeader("Authorization",m.lcBasicAuth)
loHTTP.setRequestHeader("X-Pims-Id",m.lcPimsId)

loHTTP.send()

I continue to receive the status 401 - unauthorized. Thank you.
 
Look at your curl code, you have a cookie header you don't set in the VFP code, and the curl line has no X-Pims-Id.

But one thing is for sure, the sample code you post sets a [tt]Header= ...[/tt] So that compares to setRequestHeader when using MSXML2.ServerXMLHTTP.6.0.

Your translation from curl to MSXML2.ServerXMLHTTP.6.0 usage simply is not complete and not 1:1, that's all I can see from here.

Chriss
 
Thanks again Chris - I was told by API support to ignore the cookie. Thanks for setting me straight on the correct format for the authentication. Richard
 
Thanks again Chris - I'll take a look at that. I generally get these things to work, but this one has stumped. Thanks.
Richard
 
Morning Chris - I wanted to let you know. I figured this out. The have different requirements for the various subdomains that provide API services. On one subdomain basic authentication is as you describe. On another they use base64 encoding on the word "Basic" as well as the user/pass. As you said, they all have their hurdles.... Thanks again for your time & patience. Richard
 
Fine,

I've seen servers rejecting the padding with = characters in the Base64 encoding, but that's something very oddly differing from the standard.
See:
They likely won't care to change, as that would mean existing wrong but working authentication to fail, the solution to that is to allow both to include "Basic" in the Base64 encoding or not.
You often don't even reach to the developers or admins who could change that or the the API developers don't have hands on the exact behavior of hosted servers.

I think you know yourself, the first line of PHP or ASP or anything else you have under your control is after the web server has already handled redirects and other protocol related things, including basic authentication. They should at least mention it in the API documentation.



Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top