こんにちは、サイオステクノロジーの佐藤 陽です。
今回はStravaAPI記事 第三弾、待望の完結編になります。
この三部作を読めばStrava APIを活用したアプリが作れるようになるので
是非第一弾からご覧ください!
【お試し編】とりあえずStravaのAPI使ってみよう!
【実践編】OAuthの認可フローを使ってみよう!
【発展編】Azure上でアプリを構築してみよう!←今回
はじめに
前回までにStrava APIの実行方法や、認可フローについて学んできたので
今回はそれらの知識を生かしてアプリケーション開発を行ってみたいと思います。
なお本記事はアプリが動作することを最優先としているため、セキュリティ面やコードの品質度外視で実装しています。
実際にアプリを作る際には気を付ける必要が多々あるかと思うので、そのあたりはご了承ください。
またフロントの開発経験が皆無で、入門向けページ見ながら実装したレベルなので、見苦しいコードがあるかもしれません。
温かい目で見守っていただけると幸いです。
環境
今回利用する環境や、リソース類は以下の通りです。
- VSCode(Azure Extension群)
- .NET 6
- ASP.NET Core 6.0
- C#
- Azure App Service
自分の経験上、.NET系に寄ってますが、言語などに大きく依存する部分は無いかと思うので
.NETの経験無い方も最後までご覧いただければと思います。
また、.NETの環境やVSCodeのC#, Azureあたりのエクステンションを入れてもらえれば
すぐに環境再現できるかと思うので、是非自分の手で試してみてください!
アプリケーション開発
今回作るアプリのゴールとしては以下の通りです
- 特定のボタンを押すと、Stravaの認可フローが行われる
- Stravaの認可フローが完了すると、API経由で取得したプロフィール情報が表示される
それでは早速作っていきましょう。
ベースアプリの構築
まずベースとなるアプリを用意します。
ゼロベースで作っていってもいいのですが、今回は時間短縮のため既存のものを使いまわします。
適当な場所に新規のディレクトリを作成し、VSCodeのターミナル画面で
dotnet new mvc -o MyStravaApp
code -r MyStravaApp
を実行します。
すると、MyStravaAppという名前のMVC形式の新規プロジェクトが生成されます。
dotnetコマンド使えないよ、っていう人は.NETのダウンロードをお願いします。
ダウンロードが完了したら、早速実行してみます。
dotnet run Program.cs
すると、アプリが起動しました。
https://localhost:7125
にアクセスすると、サンプルのアプリが表示されていることが分かります。
今回は、ここにStrava要素を付け加えていきたいと思います。
追加実装
ベースのアプリにStravaの認可フローを実装していきます。
前回のブログを参照してもらえれば分かると思うのですが
- 認可エンドポイントに遷移
- リダイレクトURLにて認可コードを受け取り、トークンエンドポイントに対してアクセストークンを要求する
- アクセストークンを利用してAPIを実行
といった流れを実装していきます。
1.から順番に実装していきたくなりますが、
1.で必要となるリダイレクトURLを先に用意したいので、2.から行っていきたいと思います。
また、コードは必要な部分のみ抽出しています。
実装に際して必要となるServiceの追加や、Packageのインストールなどは適宜対応ください。
アクセストークン取得
ここで実現したい処理としては、以下の流れです。
- 認可コードをクエリパラメータから取得する
- 認可コードを基にアクセストークンを要求する
- 返ってきたアクセストークンをセッションに保存しておく
ということで、実装してみました。
//Stravaから認証後にリダイレクトされる先
//<param name="code">認可コード</param>
[HttpGet("strava/token")]
public async Task<ActionResult> Token([FromQuery] string code)
{
//トークン取得に必要な情報を整備
StravaAuthContent authContent = new StravaAuthContent(code);
string body = JsonConvert.SerializeObject(authContent);
HttpContent content = new StringContent(body, Encoding.UTF8, @"application/json");
HttpRequestMessage request = new HttpRequestMessage()
{
Method = HttpMethod.Post,
Content = content,
RequestUri = new Uri("https://www.strava.com/api/v3/oauth/token")
};
//アクセストークン取得
HttpResponseMessage response = await httpClient.SendAsync(request);
//取得したトークンをセッションに保存
JObject jObject = JsonConvert.DeserializeObject<JObject>(await response.Content.ReadAsStringAsync()) ?? new JObject(string.Empty);
string accessToken = jObject["access_token"]?.ToString() ?? string.Empty;
HttpContext.Session.SetString("access_token", accessToken);
return View();
}
//トークン取得に必要なパラメータ群をModel化
//WARNING:secretをハードコードしてますが、実際にはセキュアな場所に保管してください
private class StravaAuthContent
{
[JsonProperty("client_id")]
public readonly string clientId = "36112";
[JsonProperty("client_secret")]
public readonly string secret = "3004cdfa501241242b770a0604";
[JsonProperty("code")]
public readonly string authCode;
[JsonProperty("grant_type")]
public readonly string grantType = "authorization_code";
public StravaAuthContent(string code)
{
authCode = code;
}
}
認可エンドポイントへの遷移
認可フローの手順的には一旦戻って、認可コードの取得フローです。
上の工程で、リダイレクトURL遷移先(https://{app-name}/strava/token)が判明したのでこのURLを使って、認可エンドポイントへのアクセスを試みます。
今回はベースのアプリに対して一つボタンを追加し、ボタンが押された時に認可エンドポイントに遷移するよう実装しました。
_Layout.cshtmlの20行目あたりですね
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
<!--Added-->
<li class="nav-item">
<a class="nav-link text-dark" href="http://www.strava.com/oauth/authorize?client_id=36112&response_type=code&redirect_uri=https://localhost:7125/strava/token&approval_prompt=force&scope=read">Strava</a>
</li>
</ul>
</div>
おそらくURL直書きは悪い実装で、JavaScript経由等でURL取得した方がよいと思うのですが
今回はそのあたり無視して実装していっています、ご了承ください。
リダイレクトURLの中身自体は前回のブログでも紹介したおとりですね。
redirect_uriが先ほど作成したエンドポイントになっていることだけ注意してください。
この状態で、アプリを起動すると
先ほどのベースアプリに[Strava]というボタンが追加され、ボタンをクリックするとStravaのログイン画面 or 認証画面へ遷移することが確認できます。
↓↓
かつ、ここで[許可する]ボタンを押すと、指定されたリダイレクトURLへ遷移し、クエリパラメータとしてアクセスコードが取得できます。
このアクセストークンはクライアント側に返すのはセキュリティ的にNGですので、今回はセッションに保存しておきます。
API実行
アクセストークンが取得できたので、後はAPIを実行するだけです。
今回、以下のようなコントローラーを作成しました。
AthleteAPIから取得した情報のうち、
- プロフィール画像
- 姓
- 名
- 居住地
- 体重
を抽出して、Viewへ渡します。
//Athlete情報を取得するAPI
public async Task<ActionResult> Athlete()
{
//セッションからアクセストークン取得
string token = HttpContext.Session.GetString("access_token") ?? string.Empty;
HttpRequestMessage request = new HttpRequestMessage()
{
Method = HttpMethod.Get,
RequestUri = new Uri("https://www.strava.com/api/v3/athlete")
};
//Bearerトークンであるため、Headerにアクセストークンを入れる
request.Headers.Add("Authorization", $"Bearer {token}");
//API実行
HttpResponseMessage response = await httpClient.SendAsync(request);
string json = await response.Content.ReadAsStringAsync();
//レスポンスから必要な情報のみを抽出し、View側に返す
StravaAthleteModel? athlete = JsonConvert.DeserializeObject<StravaAthleteModel>(json);
return View(athlete);
}
public class StravaAthleteModel
{
[JsonProperty("firstname")]
public string FirstName { get; set; } = string.Empty;
[JsonProperty("lastname")]
public string LastName { get; set; } = string.Empty;
[JsonProperty("state")]
public string State { get; set; } = string.Empty;
[JsonProperty("weight")]
public float Weight { get; set; } = 0;
[JsonProperty("profile")]
public string Profile { get; set; } = string.Empty;
}
View側は以下のように実装しました。
@{
ViewData["Title"] = "Strava Profile";
}
<h1>@ViewData["Title"]</h1>
<h3>Your Profile</h3>
<img src="@Model.Profile">
<p>名:@Model.FirstName</p>
<p>姓:@Model.LastName</p>
<p>居住地:@Model.State</p>
<p>体重:@Model.Weight</p>
ちなみにAthlete()のControllerを実行するためのView(Token.cshtml)は以下のようになっています。
これはControllerのToken()で返されるViewでもありますね。
@{
ViewData["Title"] = "Strava Profile";
}
<h1>@ViewData["Title"]</h1>
<p>This is Strava Profile Page</p>
<a asp-area="" asp-controller="Strava" asp-action="Athlete">Get your profile</a>
ここまで実装すると、Strava認証後に以下のページへとリダイレクトされ
Get your profile のリンクを押すことで、Stravaから取得されたプロフィール情報が表示できました。
これで大きな流れが実装できました!
後はAPIを切り替えたりして好きな情報を表示すれば、アプリの完成ですね。
アプリの公開
せっかくアプリを作成したので、アプリを公開しましょう。
今回はAzureのWeb Appにデプロイしていきたいと思います。
リソース作成
AzureのPortal画面からWeb Appを作成していきます。
ランタイムは.NET 6としておきましょう。
プランはお試しであればFreeプランで問題なしです。
リダイレクトURLの修正
今回、認可エンドポイントへのリクエストパラメータとして、リダイレクトURLは以下のように設定していました。
https://localhost:7125/strava/token
<li class="nav-item">
<a class="nav-link text-dark" href="http://www.strava.com/oauth/authorize?client_id=36101&response_type=code&redirect_uri=https://{app-name}.azurewebsites.net/strava/token&approval_prompt=force&scope=read">Strava</a>
</li>
Strava側の設定変更
Strava側にもリダイレクトURLの情報を入力していたため、こちらも書き換えます。
デプロイ
ここまで完了したら実際にデプロイ作業を行っていきます。
デプロイ作業はVSCodeのAppServiceのExtensionで簡単に行うことができます。
対象のAppServiceで右クリックし、[Deploy to Web App]をクリックします。
ものの数秒でデプロイが完了します。
デプロイが完了したら、アプリにアクセスしてみましょう!
すると先ほどローカルで開発していたものが、実際に公開されています!
これにてデプロイ完了です!お疲れ様でした!!
まとめ
今回は実際にアプリを作成するという事で、プロジェクトの作成から公開まで一貫して行ってみました!
冒頭にも書きましたが、実現するために必要最低限の実装しか行っていないため、このまま運用しては問題が山積みです。
※特にClientSecretをハードコードしてるのが一番NGです、間違えてもGitHub等にアップロードしないようにしましょう。
とはいえ、大きな流れは理解いただけたのではないかと思うので
これをベースに自分なりに改良して素晴らしいアプリを開発してもらえればと思います。
以上、3部作と長くなりましたが、閲覧いただきありがとうございました。
一緒にたくさん運動して健康的なエンジニアを目指しましょうε=┌( ・д・)┘.