Cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Debugging "Bad Request" Response when Logging Activity

ANSWERED

Hi,

I am trying to log activity recorded separately into my FitBit account. I have got authorisation and GET requests working well, and now need to log the missing activities. My code, in VB.NET, looks like the following:

 

Dim oParams As New NameValueCollection
oParams.Add("activityId", oActivity.activityId)
oParams.Add("manualCalories", oActivity.calories)
oParams.Add("startTime", Format(oActivity.startTime, "HH:mm:ss"))
oParams.Add("durationMillis", oActivity.duration * 1000)
oParams.Add("date", Format(oActivity.activityDate, "yyyy-MM-dd"))
oParams.Add("distance", Format(oActivity.distance, "#.##"))
webClient.Headers.Add("Accept-Locale", "en_GB")
webClient.Headers.Add("Authorization", sTokenType & " " & sToken)
sCallURI = sCallBase & "user/-/activities.json"
sFileName = "AddActivity" & oActivity.activityDate & ".json"
sJSON = webClient.UploadValues(sCallURI, "POST", oParams).ToString

This is very similar to the working GET code, except for using "POST" and supplying the parameter list.

However I keep getting a 400 "Bad Request" response. I have tried a couple of different ways of making the POST, but all return the same result. I have checked the formatting and values of the parameters and they all seem OK.

Is there any way to get more detail on what is going wrong?

Thanks

Andrew

Best Answer
0 Votes
1 BEST ANSWER

Accepted Solutions

And now, the conclusion...

 

Unlike SOAP, REST services can take input through any combination of four mechanisms:

  • URL Query Parameters
  • Request Headers
  • Request POST Parameters
  • Request body. This is usually but not necessarily JSON, other options like XML are also used

As REST also has no concept of a machine-readable service contract, developers rely on the service documentation to confirm how the parameters work, their syntax and semantics, and, importantly, how they are submitted. Unfortunately the Fitbit documentation omits a lot of this key information, and is arguably misleading with some of the information which is provided.

 

Fortunately there is a partial solution in the form of a Swagger UI page at https://dev.fitbit.com/build/reference/web-api/explore/. This isn't perfect, but does allow you to try the API and see how the request and response are formed. Using this I established that the parameters to LogActivity need to be provided as URL query parameters, not as POST parameters as implied by the documentation.

 

The following VB.NET code does a successful POST:

 

Dim sb As New System.Text.StringBuilder()
webClient.Headers.Add("Accept-Locale", "en_GB")
webClient.Headers.Add("Authorization", sTokenType & " " & sToken)
sCallURI = sCallBase & "user/-/activities.json?"

sb.Append("activityId=").Append(oActivity.activityId) sb.Append("&manualCalories=").Append(Format(oActivity.calories, "#")) sb.Append("&startTime=").Append(Format(oActivity.startTime, "HH:mm:ss")) sb.Append("&durationMillis=").Append(oActivity.duration * 1000) sb.Append("&date=").Append(System.Net.WebUtility.UrlEncode(Format(oActivity.activityDate, "yyyy-MM-dd"))) If oActivity.description = "Top Up" Then sb.Append("&distance=").Append(Format(oActivity.steps, "#")) sb.Append("&distanceUnit=").Append("steps") Else sb.Append("&distance=").Append(Format(oActivity.distance, "#.##")) End If sCallURI = sCallURI & sb.ToString sResponse = webClient.UploadString(sCallURI, "").ToString

 

Note that there are a couple of wrinkles:

  1. manualCalories has to be passed as an integer, although you may receive a floating point value for calorie value in some queries.
  2. distanceUnit is not well documented. The documentation page doesn't explain its format at all, and the Swagger UI page suggests it is an integer. As far as I can see it has two values, missing (= use default distance units for locale) or "steps", which applies only to specified basic running and walking exercises.

I hope this helps.

View best answer in original post

Best Answer
0 Votes
2 REPLIES 2

Silence, save for the faint moan of the wind. A ball of tumbleweed rolls unchecked through the forum. Of the Fitbit team there is no sign.

Forlorn, the developer also tries an electronic mail, but alas, this too goes unanswered.

Saddened but undaunted, our hero is made of sterner stuff. Through his own efforts and much Googling, an answer is found.

To be continued...

Best Answer
0 Votes

And now, the conclusion...

 

Unlike SOAP, REST services can take input through any combination of four mechanisms:

  • URL Query Parameters
  • Request Headers
  • Request POST Parameters
  • Request body. This is usually but not necessarily JSON, other options like XML are also used

As REST also has no concept of a machine-readable service contract, developers rely on the service documentation to confirm how the parameters work, their syntax and semantics, and, importantly, how they are submitted. Unfortunately the Fitbit documentation omits a lot of this key information, and is arguably misleading with some of the information which is provided.

 

Fortunately there is a partial solution in the form of a Swagger UI page at https://dev.fitbit.com/build/reference/web-api/explore/. This isn't perfect, but does allow you to try the API and see how the request and response are formed. Using this I established that the parameters to LogActivity need to be provided as URL query parameters, not as POST parameters as implied by the documentation.

 

The following VB.NET code does a successful POST:

 

Dim sb As New System.Text.StringBuilder()
webClient.Headers.Add("Accept-Locale", "en_GB")
webClient.Headers.Add("Authorization", sTokenType & " " & sToken)
sCallURI = sCallBase & "user/-/activities.json?"

sb.Append("activityId=").Append(oActivity.activityId) sb.Append("&manualCalories=").Append(Format(oActivity.calories, "#")) sb.Append("&startTime=").Append(Format(oActivity.startTime, "HH:mm:ss")) sb.Append("&durationMillis=").Append(oActivity.duration * 1000) sb.Append("&date=").Append(System.Net.WebUtility.UrlEncode(Format(oActivity.activityDate, "yyyy-MM-dd"))) If oActivity.description = "Top Up" Then sb.Append("&distance=").Append(Format(oActivity.steps, "#")) sb.Append("&distanceUnit=").Append("steps") Else sb.Append("&distance=").Append(Format(oActivity.distance, "#.##")) End If sCallURI = sCallURI & sb.ToString sResponse = webClient.UploadString(sCallURI, "").ToString

 

Note that there are a couple of wrinkles:

  1. manualCalories has to be passed as an integer, although you may receive a floating point value for calorie value in some queries.
  2. distanceUnit is not well documented. The documentation page doesn't explain its format at all, and the Swagger UI page suggests it is an integer. As far as I can see it has two values, missing (= use default distance units for locale) or "steps", which applies only to specified basic running and walking exercises.

I hope this helps.

Best Answer
0 Votes