dmdata.jp からの情報の取得を楽にするための非公式ライブラリ
- 冗長性WebSocketコントローラー (
RedundantDmdataSocketController) を追加しました。- 複数のエンドポイントに同時接続して耐障害性が向上できるモードです。
- 自動で再接続されるため、単一コネクションの管理にも使えます。
- 再接続可能な単一接続クラス (
ReconnectableDmdataSocket) を追加しました。- 単一エンドポイントへの自動再接続機能付きWebSocket接続です。
- リトライ機能付きのWebSocket APIとして単独でも使用できます。
- V1 API系が廃止されました。
- OAuth Introspect API の公開終了に伴い Obsolate 属性を付与しました。
DmdataApiErrorExceptionを追加しました。APIレスポンスがエラーの場合はこの例外が発生します。DmdataNotValidContractExceptionを追加しました。認証情報は正しいが、情報を取得するための契約が存在しない場合に発生します。DmdataV2ApiClientにAllowPararellRequestプロパティを追加しました。- デフォルトでは
falseのため実質並列リクエストが許可されなくなることになります。
- デフォルトでは
- EEW GD APIを追加しました(thx! @iedred7584)
- WebSocket接続時、任意のサーバーに接続できるようになりました。(利用方法は下記参照)
- OAuth 利用時、回線に問題が起きたときに
DmdataAuthenticationExceptionが発生しないようにしました。 - .NET 5 への対応を終了し、 .NET 7 に対応させました。
- 主にDPoP対応のため内部構造を変更しています。
UseOAuthClientCredential , UseOAuthRefreshToken が非推奨となりました。UseOAuth を使用してください。
- 直接認可情報(
OAuthRefreshTokenCredential)を返すようになりました。- それを
builder.UseOAuthでセットして使用します。 - 保存した認可情報を使用する場合は
OAuthRefreshTokenCredentialのインスタンスを自分で作成してください。
- それを
- ローカルホストにおけるリダイレクトURIのチェックの緩和に適応できるようにしました。
- ポートの指定が動的に行えるようになりました。
- DPoP を使用した認証の開始に対応しました。鍵は自動で生成されます。
- (この規格はまだドラフト段階にあるため広く公開するアプリケーションでの利用はまだ推奨しません。)
- 認可コードフロー/リフレッシュトークンフローにおいて DPoP に対応しています。
- (この規格はまだドラフト段階にあるため広く公開するアプリケーションでの利用はまだ推奨しません。)
- 各Credentialクラスが Introspect API に対応しました。
実装する際は必ずDM-D.S.Sのドキュメントを読みながら進めてください。
DM-D.S.Sの管理画面からOAuthクライアントを作成します。
- リダイレクトURIは
http://127.0.0.1/を設定してください。 - 各URIは厳密に判定されており、大文字が使用できません。
- 認証周りについては以下のように設定してください。
- クライアントの種類:
公開 - 使用するフロー:
認可コードフロー/リフレッシュトークンフロー- 現状このライブラリはインプリシットフローに対応していません。
- クライアントの種類:
APIを叩くためのインスタンスを作成します。 まずはBuilderを作成し、UserAgentなどを設定しておきます(任意)。
// using DmdataSharp;varbuilder=DmdataApiClientBuilder.Default.UserAgent("アプリ名").Referrer(newUri("リファラにいれるURL"));リフレッシュトークンを保存した場合、この手順はスキップすることができます。
1で作成したOAuthクライアントIDと許可を求めたい(呼びたいAPIが該当する)スコープを SimpleOAuthAuthenticator.AuthorizationAsync に渡して各種トークン(資格情報)の取得を行います。
// using DmdataSharp.Authentication.OAuth;varclientId="クライアントID";varscopes=new[]{"contract.list","telegram.list","socket.start","telegram.get.earthquake"};// 認可を得るvarcredential=awaitSimpleOAuthAuthenticator.AuthorizationAsync(builder.HttpClient,clientId,scopes,"DmdataSharp サンプルアプリケーション",
u =>{Process.Start(newProcessStartInfo("cmd",$"/c start {u.Replace("&","^&")}"){CreateNoWindow=true});});// 得た資格情報を登録する(4の内容)builder=builder.UseOAuth(credential);{好きなポート} の部分は好みで設定してください。
後述する 内部でホストするHTTPサーバー で使用します。
AuthorizationAsync の解説をしておきます。
Task<OAuthRefreshTokenCredential>AuthorizationAsync(HttpClientclient,// 内部でAPIを呼ぶ際に使用するHttpClient 今回はBuilderで作成したHttpClientを使用しますstringclientId,// OAuthクライアントIDstring[]scopes,// 認可を求めるスコープstringtitle,// 認可時にブラウザ上に表示されるアプリケーション名Action<string>openUrl,// URLが求められた際にブラウザを開くためのデリゲートbooluseDpop=true,// DPoPを使用するか ※まだ試験中の機能のため実験目的以外の利用は推奨しませんCancellationToken?token=null,// 認可フロー自体の CancellationToken 指定した場合中断させることができるushort?listenPort=null)// 内部でホストするHTTPサーバーのポート 未指定の場合はランダムこの認可フローはWebブラウザを使用した方式であるため、認可ボタンを押した後ライブラリ内で建てたHTTPサーバーにリダイレクトすることでトークンの取得を行います。
尚、Listenするポートがすでに使用されているなどの問題などが発生した場合 HttpListenerException が発生します。リトライを行うなど、適当に対処してください。
作成していたBuilderに3で取得した資格情報の登録を行います(3のコード内に含まれています)。
リフレッシュトークンは長期間使用することができるため、起動のたびにブラウザを開かないようにするためにも、アプリケーションに組み込むときは保存しておくとよいでしょう。
保存したリフレッシュトークンなどを使用したい場合、 OAuthRefreshTokenCredential のコンストラクタに指定します。
クライアント・クレデンシャルフローを使用する場合も同様に OAuthClientCredential のコンストラクタにクライアントID・シークレットを指定します。
builder=builder.UseOAuth(credential);なお、この認可情報についてはトークンの無効化などで使用するため変数として保持しておくようにしましょう。
BuildV2ApiClient でAPIクライアントを作成します。
usingvarclient=builder.BuildV2ApiClient();これで各種APIが呼べるようになりました。
Disposeにアクセストークンの失効が含まれているため、アプリケーションの終了時などにDisposeを忘れないようにしましょう。
vartelegramList=awaitclient.GetTelegramListAsync(limit:10);これで最新の電文を10件取得することが可能です。
レスポンスなどはAPIドキュメントを参考にしてください。
ポーリングする場合は必ず cursorToken オプションを使用しましょう。
usingvarstream=awaitclient.GetTelegramStreamAsync(id);keyに取得する電文のKeyパラメータを指定します。これでStreamインスタンスが取得可能です。
メモリ消費削減のためStreamをそのまま返しているため、usingもしくはDisposeを忘れないようにしましょう。
vartelegramString=awaitclient.GetTelegramStringAsync(id);Streamの扱いがめんどくさい人向けにstringに変換する処理を追加したメソッドもあります。
XDocumentdocument;XmlNamespaceManagernsManager;using(vartelegramStream=awaitApiClient.GetTelegramStreamAsync("電文のId"))using(varreader=XmlReader.Create(telegramStream,newXmlReaderSettings{Async=true})){document=awaitXDocument.LoadAsync(reader,LoadOptions.None,CancellationToken.None);nsManager=newXmlNamespaceManager(reader.NameTable);}nsManager.AddNamespace("jmx","http://xml.kishou.go.jp/jmaxml1/");// 地震情報の場合以下の追記が必要// nsManager.AddNamespace("eb", "http://xml.kishou.go.jp/jmaxml1/body/seismology1/");// nsManager.AddNamespace("jmx_eb", "http://xml.kishou.go.jp/jmaxml1/elementBasis1/");// XPathを使用して電文のタイトルが取得できるvartitle=document.Root.XPathSelectElement("/jmx:Report/jmx:Control/jmx:Title",nsManager)?.Value;アプリケーションの連携を解除する際はリフレッシュトークンの失効が必要です。
3,4で作成した認可情報のインスタンスから失効を行います。
awaitcredential.RevokeRefreshTokenAsync();usingvarsocket=newDmdataV2Socket(client);1で作成したAPIクライアントを引数にソケットのインスタンスを作成します。
データを受信した際のイベントハンドラを登録します。
socket.Connected+=(s,e)=>Console.WriteLine("EVENT: connected");socket.Disconnected+=(s,e)=>Console.WriteLine("EVENT: disconnected");socket.Error+=(s,e)=>Console.WriteLine("EVENT: error c:"+e.Code+" e:"+e.Error);socket.DataReceived+=(s,e)=>{Console.WriteLine($@"EVENT: data type: {e.Head.Type} key: {e.Id} valid: {e.Validate()} body: {e.GetBodyString().Substring(0,20)}...");};接続が完了し、 start を受信したときに発火します。
メッセージの内容をそのまま参照することができます。
切断された・切断したときに発火します。
error を受信したときに発火します。
メッセージの内容をそのまま参照することができます。
data を受信したときに発火します。
publicboolValidate()電文が正しいかどうかの検証を行います。
正しくない場合、大抵のケースはこのライブラリのバグです。
publicStreamGetBodyStream()圧縮されているかなどを自動で判別し展開やデコードを行います。
こちらも念の為usingもしくはDisposeを忘れないように注意してください。
publicstringGetBodyString(Encoding?encoding=null)GetBodyStream にstringに変換する処理を追加したものです。
GetTelegramを同じノリで取得できます
XDocumentdocument;XmlNamespaceManagernsManager;using(vartelegramStream=data.GetBodyStream())using(varreader=XmlReader.Create(telegramStream,newXmlReaderSettings{Async=true})){document=awaitXDocument.LoadAsync(reader,LoadOptions.None,CancellationToken.None);nsManager=newXmlNamespaceManager(reader.NameTable);}nsManager.AddNamespace("jmx","http://xml.kishou.go.jp/jmaxml1/");// 地震情報の場合以下の追記が必要// nsManager.AddNamespace("eb", "http://xml.kishou.go.jp/jmaxml1/body/seismology1/");// nsManager.AddNamespace("jmx_eb", "http://xml.kishou.go.jp/jmaxml1/elementBasis1/");// XPathを使用して電文のタイトルが取得できるvartitle=document.Root.XPathSelectElement("/jmx:Report/jmx:Control/jmx:Title",nsManager)?.Value;awaitsocket.ConnectAsync(newSocketStartRequestParameter(TelegramCategoryV1.Earthquake,TelegramCategoryV1.Scheduled,TelegramCategoryV1.Volcano,TelegramCategoryV1.Weather){AppName="アプリ名",});SocketStartRequestParameter の引数には受信したい情報のカテゴリを、 AppName は管理画面の 状況 ページで表示される メモ の指定が行なえます。(文字数制限に注意)
その他にも Types で電文のフィルタなども行えますのでご活用ください。
冗長性を確保したい等、接続先のサーバーを指定したいときは追加で引数を指定することもできます。
awaitsocket.ConnectAsync(newSocketStartRequestParameter(TelegramCategoryV1.Earthquake),DmdataV2SocketEndpoints.Osaka);v0.6.0.0で追加された ReconnectableDmdataSocket を使用することで、単一のエンドポイントへの自動再接続機能付きWebSocket接続が利用できます。
// using DmdataSharp.Redundancy;usingvarreconnectableSocket=newReconnectableDmdataSocket(client,"your-endpoint-url");varreconnectionOptions=newReconnectionOptions{InitialDelay=TimeSpan.FromSeconds(5),// 初回再接続遅延MaxDelay=TimeSpan.FromMinutes(5),// 最大遅延時間BackoffMultiplier=2.0,// 遅延倍率MaxAttempts=10// 最大試行回数(0で無制限)};usingvarreconnectableSocket=newReconnectableDmdataSocket(client,"your-endpoint-url",reconnectionOptions);// 基本的なWebSocketイベントreconnectableSocket.Connected+=(s,e)=>Console.WriteLine("Connected");reconnectableSocket.DataReceived+=(s,e)=>Console.WriteLine($"Data: {e.Head.Type}");reconnectableSocket.Disconnected+=(s,e)=>Console.WriteLine("Disconnected");reconnectableSocket.Error+=(s,e)=>Console.WriteLine($"Error: {e?.Error}");// 再接続関連のイベントreconnectableSocket.ReconnectionAttempt+=(s,e)=>Console.WriteLine($"Reconnecting... Attempt {e.AttemptCount}, Delay: {e.Delay.TotalSeconds}s");reconnectableSocket.ReconnectionSucceeded+=(s,e)=>Console.WriteLine($"Reconnection succeeded to {e.EndpointName}");reconnectableSocket.ReconnectionFailed+=(s,e)=>Console.WriteLine($"Reconnection failed: {e.Reason}");awaitreconnectableSocket.ConnectAsync(newSocketStartRequestParameter(TelegramCategoryV1.Earthquake,TelegramCategoryV1.Scheduled){AppName="再接続アプリ",});- 自動再接続: 接続断時に指数バックオフで自動再試行
- 設定可能な再接続戦略: 遅延時間・試行回数・倍率を調整可能
- 詳細なイベント: 再接続の進行状況を細かく監視
- 単独利用可能: 冗長性が不要な場合の軽量な選択肢
v0.6.0.0で追加された RedundantDmdataSocketController を使用することで、複数のエンドポイントに同時接続し、耐障害性を向上させることができます。
// using DmdataSharp.Redundancy;usingvarredundantSocket=newRedundantDmdataSocketController(client);通常の DmdataV2Socket と同様にAPIクライアントを引数に指定します。
冗長性コントローラーでは、通常のWebSocketイベントに加えて、詳細な接続状態やデータの流れを監視できます。
// 基本的なデータ受信(重複除去後)redundantSocket.DataReceived+=(s,e)=>{Console.WriteLine($"Data: {e.Head.Type} from {redundantSocket.ActiveConnectionCount} connections");};// 生データ受信(重複除去前、接続別)redundantSocket.RawDataReceived+=(s,e)=>{Console.WriteLine($"Raw data from {e.EndpointName}, Duplicate: {e.IsDuplicate}");};// 個別接続の確立redundantSocket.ConnectionEstablished+=(s,e)=>{Console.WriteLine($"Connection established: {e.EndpointName}");};// 個別接続の切断redundantSocket.ConnectionLost+=(s,e)=>{Console.WriteLine($"Connection lost: {e.EndpointName}, Reason: {e.Reason}");};// 全接続の切断redundantSocket.AllConnectionsLost+=(s,e)=>{Console.WriteLine($"ALL CONNECTIONS LOST! Will retry in {e.NextReconnectAttempt.TotalSeconds}s");};// 冗長性復旧redundantSocket.RedundancyRestored+=(s,e)=>{Console.WriteLine($"Redundancy restored via {e.RestoredEndpoint}");};// 冗長性状況変更redundantSocket.RedundancyStatusChanged+=(s,e)=>{Console.WriteLine($"Status: {e.Status}, Active: {e.ActiveConnections}");};// 接続エラーredundantSocket.ConnectionError+=(s,e)=>{Console.WriteLine($"Error on {e.EndpointName}: {e.Exception?.Message}");};// デフォルト(東京+大阪)に接続awaitredundantSocket.ConnectAsync(newSocketStartRequestParameter(TelegramCategoryV1.Earthquake,TelegramCategoryV1.Scheduled){AppName="冗長性アプリ",});エンドポイントを明示的に指定することも可能です:
// カスタムエンドポイントに接続awaitredundantSocket.ConnectAsync(newSocketStartRequestParameter(TelegramCategoryV1.Earthquake),[DmdataV2SocketEndpoints.Tokyo,DmdataV2SocketEndpoints.Osaka,DmdataV2SocketEndpoints.Apne1Az4]);Console.WriteLine($"Status: {redundantSocket.Status}");Console.WriteLine($"Active connections: {redundantSocket.ActiveConnectionCount}");Console.WriteLine($"Connected endpoints: {string.Join(", ",redundantSocket.ConnectedEndpoints)}");Console.WriteLine($"Total messages: {redundantSocket.TotalMessagesReceived}");Console.WriteLine($"Duplicates filtered: {redundantSocket.DuplicateMessagesFiltered}");varoptions=newRedundantSocketOptions{DefaultEndpoints=new[]{DmdataV2SocketEndpoints.Tokyo,DmdataV2SocketEndpoints.Osaka},DeduplicationCacheSize=2000,// 重複除去キャッシュサイズ(デフォルト: 1000)EnableRawDataEvents=true,// 生データイベントを有効化(デフォルト: true)ReconnectDelay=TimeSpan.FromSeconds(10)// 再接続間隔(デフォルト: 5秒)};usingvarredundantSocket=newRedundantDmdataSocketController(client,options);- 複数エンドポイント同時接続: デフォルトで東京・大阪の2箇所に接続
- 重複メッセージ除去: ハッシュによる自動重複検出・除去
- 詳細な状態監視: 個別接続の状態変化を細かく追跡
- 自動フェイルオーバー: 一部接続が失われても継続動作
- 統計情報: 受信数・重複数・接続状況の統計
APIキー認証の場合、メッセージにAPIキーが含まれている場合文字の置き換えを行います。
各種資格情報が失効しているか、認可されませんでした。
認証情報が不正です。
認証情報が不正です。
使用中の資格情報に権限がない場合などに発生します。
APIv1の場合はAPIキーが不正な場合もこの例外が発生します。
認証情報は正しいが、情報を取得するための契約が存在しない場合に発生します。
APIレスポンスがエラーです。 エラーの詳細は例外クラスのプロパティに代入されます。
APIをリクエストした際にタイムアウトしました。
レートリミットに引っかかりました。
このライブラリは同時アクセスの制御を行いません。
しばらく待ってアクセスし直してください。
上記の例外が継承している基底クラスです。
いくつかのエラーで使用されています。
また、ネットワークエラーの場合はその状況に合わせた例外が発生します。
このライブラリには実装されていない、dmdata.jpのAPIを使用したい場合は DmdataV2ApiClient を継承したクラスを作ることで拡張可能です。DmdataApiClientBuilder の Build メソッドの型引数に継承したクラスを入れることで既存の機能と同様に初期化することができます。
注意点としては、コンストラクタの引数は継承元のクラスに合わせるようにしてください。protected なメソッドを使用することでライブラリの認証機能などを使用することができます。詳細は DmdataV2ApiClient のコードなどを読んでみてください。

