StravaのAPIにアクセスしてアカウント情報を取得する

##はじめに
StravaにAPIがある！でも日本語ドキュメントがない！わかりづらい！
という状況に陥り、自分が割と苦労したので、アスリートエンジニアのために記録を残したいと思います。
みんなで運動して健康な体でコーディングしよう！！

##Stravaとは
アスリート向けのSNSです。
ランニングや自転車、水泳など様々なアクティビティを記録することができます。
stravaでは[開発者向けAPI](
https://developers.strava.com/)が提供されており、このAPIを使うことでアスリート情報や登録してある自転車やシューズの情報を取得できます。

##この記事の内容
Stravaにアプリを登録し、HTTPリクエストを投げて自転車の情報を取得するまでを記載しています。
GET？POST？なにそれ？っていう状態から作ったので、初心者の方でもわかりやすい内容になっているのではないかと思います。

##開発環境
特に今回は開発環境に依存しないかとは思いますが一応記載しておきます。

- OS:mac OS HighSierra
- Eclipse4.8
- Tomcat 9.0.26
- JDK 1.8.0

##Stravaにアプリを登録
まずstravaにログインし、設定ページの[MyAPIアプリケーション](
https://developers.strava.com/)アクセスすると自分のアプリを登録することができます。

この時点では別にアプリが出来ていなくても大丈夫なので、適当に登録しちゃいましょう。
ひとまずウェブサイトを

> http://127.0.0.1:8080

とします。
:8080はtomcat使ってアプリ作るつもりだったので8080にしてあります。(8080はtomcatで利用するポート番号です。)

認証コールバックドメインも

> 127.0.0.1:8080

としておきます。

![スクリーンショット 2019-09-22 21.42.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137041/a75ba51b-48c4-8637-cfe2-f082f7f00c45.png)

アイコンも適当に登録するとクライアントIDや、クライアントシートなどの値が与えられます。
![スクリーンショット 2019-09-22 21.49.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137041/d9d6ad2d-0714-0921-fd39-6f16f3eacb8f.png)

##WebAPIにアクセス
作業としては大きく分けて2つです。

1. WebAPIを叩くために必要となるアクセストークンを取得します。
2. その次にそのトークンを利用してWebAPIにアクセスし、情報を取得します。

###1.アクセストークンの取得
StravaではOauth2.0という仕組みを導入しています。
Oauthに関しては以下のQiitaの記事がめちゃめちゃわかりやすかったので、こちらを参照ください。
[一番分かりやすい OAuth の説明](https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be)

基本的にはstravaの[公式ドキュメント](https://developers.strava.com/docs/authentication/
)に沿ってやっていきます。

トークンを取得するためには以下のURLにヘッダー情報を加えたものをGETメソッドを利用してHTTPリクエストを飛ばします

> https://www.strava.com/oauth/mobile/authorize

GETメソッドにおいて情報を追加する場合はURLの後ろに追加して行きます。
今回トークンの取得に当たって必要となる情報が次の5つです。

1. clientid
先ほどのStravaにアプリを登録した際に表示されていたクライアントIDの値です。

2. redirect_uri
認証完了後にリダイレクトされる先のURLを与えます。
**注意:このリダイレクト先のURLのドメインと、Stravaにアプリを登録した際の認証コールバックドメインに含まれていないとリクエストが失敗します**
なので今回であればredirect_uriのドメインも127.0.0.1である必要があります。

3. response_type
この値は必ず**code**です。

4. approval_prompt
**force**もしくは、**auto**のどちらかを選択します。
**force**：毎回認証が行われます。
**auto**：一度認証が通ればしばらく(?)は自動的に認証が行われます。

5. scope
ユーザー情報にたいしてアクセスできる範囲を決められます。
**read**,**read_all**,**profile:read_all**など様々なものが用意されているので
自分のアプリでやりたいことに適したものを選択しましょう。

例としてヘッダーを加えたURLは以下のようなものになります。
> https://www.strava.com/oauth/authorize?client_id=****&response_type=code&redirect_uri=http://127.0.0.1:8080/hoge&scope=read&approval_prompt=auto

このURLにアクセスすると、以下のような画面が表示されるかと思います。
![スクリーンショット 2019-09-22 22.21.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137041/9a200b59-de22-ea0f-38e6-d2877372657f.png)

ここで許可を押すと、Stravaのログイン画面に遷移し、ログインすると認証されリダイレクトURL先へと遷移します。
リダイレクトした先のURLに"code="という部分が含まれているかと思います。
> hogehoge/state=&**code=hogehogehogehoge**&scope....

**このコードの値を利用するので控えておいてください。**

次に、以下のURLに対してPOSTメソッドを使ってHTTPリクエストを行います。
> https://www.strava.com/oauth/token

このPOSTメソッドのbodyに必要な情報を与えることで、アクセストークンが取得できます。
bodyに与える情報は以下の5つです。

1. clientID
Stravaにアプリを登録したときに与えられたクライアントIDの値

2. client_secret
Stravaにアプリを登録したときに与えられたクライアントシートの値
(クライアントシークレットの誤り？)

3. code
先ほどのリダイレクト先のURLに含まれていたcodeの値

4. grant_type
必ず**authorization_code**としてください。
POSTメソッドの実装はアプリを実装する環境によって異なるかと思いますが、Javaの場合以下のような形で実装しました。

```java:request_token.java
    URL url = new URL("https://www.strava.com/oauth/token");
    HttpURLConnection con = (HttpURLConnection)url.openConnection();
    con.setRequestMethod("POST");
    con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    con.setDoOutput(true);
    con.setDoInput(true);
    con.connect();

    String body = "client_id=****&client_secret=****&code=****&grant_type=authorization_code";
    byte[] postData = body.getBytes(StandardCharsets.UTF_8);
    try(DataOutputStream outputStream = new DataOutputStream(con.getOutputStream())){
        outputStream.write(postData);
    }
```

そして、このリクエストに対するレスポンスのなかに**access_token**が存在しているので、このトークンを取得します。
サンプルコードとしては以下のような感じですかね。

```java:gettoken.java
    String responseData = "";
    InputStream stream = con.getInputStream();
    StringBuffer sb = new StringBuffer();
    String line = "";
    BufferedReader br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
    while((line = br.readLine()) != null) {
        sb.append(line);
    }
    try {
        stream.close();
    }catch(Exception e) {
        e.printStackTrace();
    }
    responseData = sb.toString();
    con.disconnect();

    JSONObject json = new JSONObject(responseData);
    String token = json.getString("access_token");
```

###2.情報の取得
先ほど取得したアクセストークンを使って実際に自転車情報を取得してみたいと思います。
自転車情報を取得するためには、アスリート情報を取得すればその中に書いてあるみたいなので、アスリート情報を取得してみます。

[公式のドキュメント](https://developers.strava.com/docs/reference/#api-Athletes)によると、
> https://www.strava.com/api/v3/athlete

に対し、tokenをプロパティに与えてGETすればレスポンス返してあげるよ、と書いてあるので言われた通りにやってみます。

サンプルコードとしては以下のような感じですね。
tokenの頭に"Bearer"をつけるのを忘れないようにしてください。

```java:getathlete.java
    url = new URL("https://www.strava.com/api/v3/athlete");
    HttpURLConnection con = (HttpURLConnection)url.openConnection();
    con.setRequestMethod("GET");
    String authorization = "Bearer " + token;  //tokenは先ほど取得したtokenの値
    con.setRequestProperty("Authorization", authorization);
    con.setDoOutput(false);
    con.setDoInput(true);
    con.connect();

    responseData = "";
    stream = con.getInputStream();
    sb = new StringBuffer();
    line = "";
    br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
    while((line = br.readLine()) != null) {
        sb.append(line);
    }
    try {
        stream.close();
    }catch(Exception e) {
        e.printStackTrace();
    }
    responseData = sb.toString();
    con.disconnect();
    json = new JSONObject(responseData);
```

公式ドキュメント見てもらえればわかりますが、自転車情報に関してはbike[]という配列の入れ子構造となってます。
こういう場合はjsonarrayなどを使って取得する必要があります。

```java:getbikes.java
    JSONArray BIKE = json.getJSONArray("bikes");
    int distance = BIKE.getJSONObject(Integer.parseInt(0)).getInt("distance");
    String bikeid = BIKE.getJSONObject(Integer.parseInt(0)).getString("id");
    String bikename = BIKE.getJSONObject(Integer.parseInt(0)).getString("name");
```

##最後に
Stravaにアプリを登録してからAPIを叩いて情報を取得する一連の流れを書きました。
リクエストする部分によっては実装環境によって異なってくるかとは思いますが、おおまかな流れは同じではないでしょうか？
ぜひStravaのAPIをうまく使って効率よく運動して行きましょう！