今からでも間に合う

技術を学ぶのは今からでも遅くない

GitHubのTrafficを取得して一覧表示する

冬休みの自由研究アウトプット

年末年始ひたすら調べて実験して最終的に作ったもの

作ったもの

GitHubのユーザーのリポジトリごとのTrafficをブラウザで表示するページ。
GitHubからも見れるけど、リポジトリ単位でしか見れなくて面倒だったので。
数が少ないのは気にしない。。。始めたばかりだし。

調べたもの
  • 基本的なこと
    • Server - Client
  • GO
    • 基本構文
    • 配列の書き方
    • ループの回し方
    • HTTPのハンドラの書き方
    • デバッグの仕方
    • pkgのダウンロード
  • Blazor
    • IHttpClientFactoryの使い方、使えるようにする方法
    • ApiControllerの作り方
  • github
  • その他

構成


大体こんな感じ。
結局今回意識しなくても動いてしまったけど、「CORS」の対応方法がまだよくわかってないので引き続き勉強要。
セキュリティとか王道パターンとか全く知らずの自己満なので細かいところは知りません。

中身

◆Blazor WebAssembly

Program.cs

builder.Services.AddHttpClient("GitHub", client =>
{
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
});

これを追加しておかないとIHttpClientFactoryがInjectionされない。

index.razor

@page "/"
@using BlazorWebServer.Shared;
@inject IHttpClientFactory Factory

<PageTitle>GitHub Traffic</PageTitle>

<button id="githubtraffic" @onclick="OnClick">Get</button>

@if (forecasts == null)
{
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Repos</th>
                <th>Views</th>
                <th>Clones</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast?.Name</td>
                    <td>@forecast?.Views</td>
                    <td>@forecast?.Clones</td>
                </tr>
            }
        </tbody>
    </table>
}

html部は大したことしていない。サンプルをまねてちょっと書き換えただけ。
@inject IHttpClientFactory Factoryがポイント。Program.csに追加したコードのコールバックが呼ばれる。アクセス先が複数ある場合名前を付けて振り分ける。

@code {
    private GitHub[]? forecasts;
    private HttpClient _client;
    protected override async Task OnInitializedAsync()
    {
        _client = Factory.CreateClient("GitHub");
        await base.OnInitializedAsync();
    }
    private async Task OnClick(MouseEventArgs e)
    {
        forecasts = await _client.GetFromJsonAsync<GitHub[]>("api/GitHub");
    }
}

ボタンクリックでWEBサーバに要求を出す。

◆Blazor Server

Program.cs

// Add services to the container.
builder.Services.AddHttpClient("GitHubRestApi", client =>
{
    client.BaseAddress = new Uri("http://localhost:8080");
});

こちらもAPIサーバとHTTP通信するようにInjectionする準備コードを追加。

GitHubController.cs

namespace BlazorWebServer.Server.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class GitHubController : ControllerBase
    {
        private readonly HttpClient _client;
        public GitHubController(IHttpClientFactory factory)
        {
            _client = factory.CreateClient("GitHubRestApi");
        }
        [HttpGet]
        public IEnumerable<GitHub> Get()
        {
            return GetAsync().Result;
        }

        private async Task<IEnumerable<GitHub>> GetAsync()
        {
            var ret = await _client.GetAsync("/traffic");
            return await ret.Content.ReadFromJsonAsync<GitHub[]>();
        }
    }
}

Frameworkで隠されすぎてて正直仕組みがわからない。初見殺し。 RouteAttribute[controller]部とか、サンプル見ないと意図わからないし、コンストラクタの引数もどこに何を書いたら渡されるようになるかもわからない。
とにかく、クライアントからの要求をAPIサーバにぶん投げるだけ。

◆GO

書きたいことが多すぎるので全部コメントに押し込んだ。

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    //go getのコマンド使って取得
    "github.com/google/go-github/github"
    "golang.org/x/oauth2"
)

// GitHubから取得したデータを返すようの構造体定義
// 重複定義しなきゃどうしようもないものなのか?
type GitHub struct {
    Name   string // リポジトリ名
    Views  int
    Clones int
}

// PATは各自取得。azure devopsでPATは知ってたからよかった
var ts = oauth2.StaticTokenSource(
    &oauth2.Token{AccessToken: "<Youre PAT>"},
)

// Traffic取得要求に対する処理の本体
func getTraffic(w http.ResponseWriter, r *http.Request) {

    tc := oauth2.NewClient(oauth2.NoContext, ts)

    client := github.NewClient(tc)

    // 自分の全てのリポジトリ取得
    // インテリセンスって取得したpkgには効かないの?
    // 結局ソース見に行って使えそうなもの調べる羽目に。
    repos, _, _ := client.Repositories.List(context.Background(), "super-string", nil)

    // 配列用意して追加情報込みで結果に詰める。
    // この辺りが調べたらわかるのだけど時間かかった。
    data := []*GitHub{}
    for _, d := range repos {
        v, _, _ := client.Repositories.ListTrafficViews(context.Background(), "super-string", *d.Name, nil)
        c, _, _ := client.Repositories.ListTrafficClones(context.Background(), "super-string", *d.Name, nil)
        data = append(data, &GitHub{*d.FullName, *v.Count, *c.Count})
    }
    // 構造体→JSONフォーマットのバイト配列化。これは便利
    jsonData, err := json.Marshal(data)
    if err != nil {
        fmt.Println(err)
    }
    // レスポンスとして書き込み
    fmt.Fprintf(w, string(jsonData))
}

func main() {
    // ハンドラを充実させていけばAPIサーバとして使える? 
    http.HandleFunc("/traffic", getTraffic)
    http.ListenAndServe(":8080", nil)
}
Blazor側のソース

GitHub - super-string/BlazorWebServer

おしまい

有意義な冬休みでした。

プライバシーポリシー


d払いポイントGETモール