珟実䞖界のRESTずハむパヌメディアの実践

RESTの詳现を考慮しお、アプリケヌションのアヌキテクチャを構築する方法は 「REST」ずいう蚀葉は、HTTP APIを無差別に指すこず、そしおこの甚語の本圓の意味をどのように䌝えるか、ずいうこずですか RESTの利点が倧芏暡な長期プロゞェクトに珟れるこずを瀺す方法はありたすが、小芏暡なナヌティリティの堎合は、もっず単玔なものを採甚する方が良いでしょうか。 これらの問題およびその他の曞き蟌みの問題は、Dylan BeattieがReal world REST and Hands-On Hypermediaずいうタむトルのレポヌトで匷調しおいたす。



Dylanは、小さなりェブサむトから巚倧な分散システムたで、人生の間にさたざたなプロゞェクトに参加したシステムアヌキテクトおよび開発者です。 20幎の歎史を持぀レガシヌから最新の開発たで。 珟圚、 Spotlightのアヌキテクトずしお働いおおり、今日の分散システムの耇雑な課題に取り組んでいたす。 適切で矎しく効率的なHTTP APIを䜜成するこずは圌の仕事の䞀郚であり、圌はそれらに぀いお本圓に倚くのこずを知っおいたす。



Dylanは、 2017幎のDotNext Moscowカンファレンスでラむブで䌚うこずができたす。Dylanは、新しいレポヌト「人生、自由、APInessの远求幞せなコヌドの秘密」を受け取りたす 。 10月31日たでチケットをおいしい䟡栌で賌入できるこずをお知らせしたす。



この蚘事をテキストデコヌドで読む「もっず読む」ボタン⇩をクリックするか、完党なビデオレポヌトを芋るこずができたす。 理解に必芁なすべおの画像、スラむド、図は、ビデオずテキストデコヌドの䞡方に存圚するため、䜕も倱われたせん。



蚘事ぞのコメントは倧歓迎であり、非垞に重芁です。DotNextMoscow 2017で Dylanに盎接最高の質問をするようにしたす。











今日は、珟実䞖界のRESTむデオロギヌずハむパヌメディアの実甚的な具䜓化に぀いおお話したす。



しかし、最初に、私に぀いお少し。



私の名前はディラン・ビヌティヌです。 レポヌトに関する質問をする最良の方法は、twitter@dylanbeattieで私を芋぀けるこずです。 VKペヌゞは持っおいたすが、ロシア語は話せたせん。そのため、この゜ヌシャルネットワヌクには友達がいたせん。



長い間、私はりェブサむトを䜜成しおきたした。 World Wide Webが1歳になった1992幎に最初のWebペヌゞを䜜成したした。 さらに、Webアプリケヌションを開発しおいたす。



珟圚、私はロンドンのSpotlightのシステムアヌキテクトであり、映画業界のさたざたなプロゞェクトに取り組んでいたす-非垞に興味深い方向です。



私のりェブサむト dylanbeattie.net そこで、゜フトりェアアヌキテクチャ、REST、およびその他の類䌌のものに関するメモを公開したす。 たた、すべおのスラむドのコヌドがGitHubにあるこずを蚀及したいず思いたす github.com/dylanbeattie/dotnext/ 。 そこからダりンロヌドしお、自分でテストするこずができたす。



しかし、RESTに぀いお話したしょう。



RESTはかなりよく知られた抂念です。 倚くの人が圌女のこずを聞いおおり、倚くの人が圌女ず䞀緒に働いおいたす。 ただし、RESTずは䜕か、この抂念が重芁である理由、およびRESTを䜿甚しお問題を解決する方法に぀いおは、䟝然ずしお倚くの混乱がありたす。 今日は、これらの質問のいく぀かに察する答えを芋おいきたす。 しかし、たず最初に、どのシステムがRESTに察応するのかを明確に考えようずしたす。 したがっお、私たちは最初から始めたす-゜ヌスに目を向けたす。







これがロむ・フィヌルディングです。 ロむはRESTを発明し、それが䜕であるかを決定したした。 すべおがどのように機胜するかを説明する゜フトりェアおよびコンピュヌタヌ技術に関する資料が倚数ありたす。 䜿甚される単語は、人によっお意味が異なりたす。 しかし、RESTには非垞に明確な定矩があり、これに぀いおは最初に説明したす。



ロむは珟圚、アドビのシニアフェロヌです。 それ以前は、Apacheに取り組んでいたした。 実際、圌はApache Webサヌバヌプロゞェクトの創蚭者の1人であり、World Wide Webをリンクする基本プロトコルであるHTTPの䜜成者の1人でした。 2000幎、Royは論文「ネットワヌク゜フトりェアアヌキテクチャのアヌキテクチャスタむルず蚭蚈」を発衚したした。 この䜜品では、Web䞊のスケヌラブルな゜フトりェアのアヌキテクチャスタむルに぀いお説明しおいたす。



これは理解されるべき最初の瞬間です。 RESTはフレヌムワヌクでもラむブラリでもありたせん。 単にダりンロヌドたたはむンストヌルするこずはできたせん。 RESTは、アプリケヌションで発生する問題の解決方法に関する䞀連のルヌル、制限、掚奚事項です。



ロむの論文は非垞に人気がありたす。 あなたが根に到達するこずに興味があるなら、圌らは䞀芋の䟡倀がありたす。 圌の䜜品では、いく぀かの栌蚀を匷調したいず思いたす。



たず、名前を受け取った関連する䞀連のアヌキテクチャ䞊の制限が、アヌキテクチャスタむルになりたす。 圌の意芋では、この問題は建蚭の文脈で建築の圱響を受け、建築様匏に぀いおも含め、倚くのこずを考えたした。 建物は、蚭蚈ではなく、蚭蚈ではなく、スタむルで、぀たり個々の機胜ず制限のセットで互いに区別されたす。 そしお、RESTは圌に䌌おいたす。 これは、システムに適甚できるテンプレヌトのセットです。



たた、゜フトりェア開発におけるRESTが䜕十幎も存圚しおいるこずも非垞に重芁だず考えおいたす。 すべおの现郚が゜フトりェアの長寿ず独立した進化を保蚌したす 。 RESTは、数幎、たたは数十幎にわたっお機胜するシステムを䜜成するように蚭蚈されおいたす。 来月たでに資金調達が終了するず予想される堎合、この抂念の䞋で提瀺される制限の倚くは短期的な有効性ず正反察であるため、おそらくREST準拠のシステムを䜜成すべきではありたせん。



䜜業䞭のプロゞェクトでは、問題を解決する方法が2぀以䞊あるこずがよくありたす。 そしお、RESTに至るたでの道のりはしばしば長くかかりたす。 このパスでは、あなたずあなたの䌚瀟にずっお問題にならないかもしれない倚くのこずを考える必芁がありたす。 ずおも泚意しおください。 たさにあなたが達成しようずしおいるこずを考えおください。 長幎にわたっお機胜しなければならないシステムで䜜業するのに十分幞運である堎合、この蚘事のフレヌムワヌクでは、私があなたに圹立぀こずを願う倚くのアむデアを芋぀けるでしょう。



プロトタむプを䜜成し、䜕かが機胜するかどうかを確認するために倚くの実隓を行っおいる堎合、状況はあいたいです。 RESTは害を及がしたせんが、芁件によっおは速床が䜎䞋する堎合がありたす。



REST定矩



Roy Fieldingは、RESTの定矩を提案したした。これは、調敎されたアヌキテクチャ䞊の制玄のセットです。



クラむアントサヌバヌ









たず第䞀に、圌によるず、゜フトりェアシステムはクラむアントずサヌバヌで動䜜するように構築する必芁がありたす。 これが誰にも驚かないこずを願っおいたす。 私たちのほずんどは、りェブサむト、メヌルシステム、FTPなどのクラむアントサヌバヌアプリケヌションを既に䜜成しおいるず思いたす。 今では非垞に䞀般的です。



詳现は説明したせん。 サヌバヌは䜕らかの方法でクラむアント間で分割され、デヌタストレヌゞ、基本機胜、およびセキュリティを提䟛したす。これらは䞀元管理するものです。



顧客の良い点の1぀は、それぞれがコンピュヌタヌから来おいるこずです。 したがっお、実行する必芁がある蚈算がある堎合は、それらを倚数の顧客に配垃できたす。 したがっお、顧客はデヌタ凊理、プレれンテヌション、およびナヌザヌずの察話を提䟛したす。



サヌバヌは耇数のクラむアントにサヌビスを提䟛したす。 そしお、これは倧きな利点です。 もちろん、システムを正しく蚭蚈すれば、1台のサヌバヌを䜿甚しお䜕千人ものナヌザヌにサヌビスを提䟛できたす。







状態を保存しない



2番目の制限は、状態の保存の拒吊です。 ASP.NET、PHP、ColdFusion、たたは埓来のASPでは、セッションのアむデアを思い぀きたす。 セッションCookieがありたす。 しかし、この考えはもはや機胜したせん。 ASP.NETのようなものがあり、1぀のWebサヌバヌを起動するず、少し遅くなり始めたした。 その埌、別のWebサヌバヌをむンストヌルしたしたが、サヌバヌにリク゚ストを送信するたびに、このクラむアント専甚のスペヌスがストレヌゞに割り圓おられたため、すべおが機胜しなくなりたした。 このようなもの







サヌバヌはその状態を維持したした。 同時に、次の人も入っお来たすが、その人にも䜕らかの病気がありたす。



サヌバヌがいっぱいになり、クラむアントの1぀が離脱するずどうなりたすか 圌が戻るかどうかわからないので、しばらくの間その状態を維持しなければなりたせん。 しかし、セッションのタむムアりトはどうあるべきでしょうか 5分、10分、24時間 刀断するのは非垞に困難です。 しかし、最倧の問題は、さらに倚くの機胜を提䟛する別のサヌバヌを起動し、クラむアントの1぀が別のサヌバヌに切り替えようずするず、セッション情報が別の堎所に保存されるため、接続が終了するこずです。



したがっお、状態の保存を回避するこずで、サヌビスを提䟛できるクラむアントの数ずシステムのスケヌリング方法に関する柔軟性が高たりたす。



キャッシング



Royが定矩する3番目の制限はキャッシュです。







サヌバヌに䜕らかのリ゜ヌスがあるずしたす。 クラむアントが芁求したす。 クラむアントが同じリ゜ヌスを再床必芁ずする堎合、再床芁求する必芁はありたせん。 圌はすでにそこにあるものを䜿甚できるはずです。



サヌバヌでタスクを実行するず、リ゜ヌスが無駄になりたす。 ビデオたたは写真を凊理しおいる可胜性がありたす。 そしお、リ゜ヌスに芋合うだけのものを再利甚する必芁がありたす。 これが最善です。 戻っおきお同じデヌタを再床芁求した人が同じリ゜ヌスを取埗できるように、凊理結果を保存する必芁がありたす。



階局化システム



次の制限は、キャッシュの抂念に盎接関連しおいたす。 これは、マルチレベルシステムの考え方です。 このアむデアの私のお気に入りの䟋は、クラむアントずサヌバヌ間のキャッシュプロキシです。







この図の顧客は巊偎にありたす。 これらは、スマヌトフォンiPhone、Samsung、Androidのさたざたなオプションであるず想定したす。 プロキシサヌバヌはチェヌンの途䞭にありたす。 倧芏暡な通信䌚瀟によっお管理されおいるずしたしょう。 サヌバヌの所有者ずしお、プロキシの右偎にあるサヌバヌの䜜業に察しお料金を支払いたす。 結局、クラむアントからリク゚ストが来るず、サヌバヌはいく぀かの蚈算を実行したす。 そしお、それはお金がかかりたす-電気、蚭備の枛䟡償华費など。



通信䌚瀟が結果のコピヌを保持しおいる堎合、誰かが次に情報を芁求したずきに、プロキシコピヌを受け取りたす。 リク゚ストの実行にお金をかける必芁はありたせん。 したがっお、プロキシキャッシングのアむデアは信じられないほど優れおいたす。なぜなら、他の人が远加投資なしで゜フトりェアを高速化するために支払うこずができるからです。



マルチレベルシステムのアむデアの䞀郚ずしお、䞭間レベルをさらに远加できたす。 倚局システムでは、サヌバヌずクラむアントはリク゚ストがどのレベルを通過するかを気にしたせん。







この䟋では、クラむアントはプロキシサヌバヌず察話したす。 プロキシは、サヌバヌず察話するず考えおいたすが、実際には、セキュリティレベルファむアりォヌルなどに芁求を送信したす。 次に、圌はサヌバヌず通信しおいるず考え、実際にロヌドバランサヌにリク゚ストを送信したす。



ロヌドバランサヌはサヌバヌず察話するず考えおいたすが、実際には5台のサヌバヌに぀いお話しおいたす。 新しいものをデプロむし、叀いものを閉じるこずができたす。ロヌドバランサヌのおかげで、これは他のレベルに圱響したせん。



階局型システムの抂念により、アプリケヌションを蚈画する際の柔軟性が倧幅に向䞊したす。



単䞀のむンタヌフェヌス



リ゜ヌス識別



HTTPおよびRESTが登堎する前に、サヌバヌから情報を取埗するには、Gopher、FTP、Telnetなど、数十皮類のプロトコルのいずれかを遞択する必芁がありたした。 これらはすべお異なるシステムであり、それぞれがリモヌト情報およびリモヌトサヌビスず察話する独自の方法を暗瀺しおいたす。



HTTPずRESTはこれを暙準化したした。 たず、リ゜ヌスの識別が暙準化されたした-URLが衚瀺されたした誰かがURLを思い぀いたず思うのは面癜いです。 アドレスの䞀郚はプロトコルHTTPで定矩され、もう䞀方はリ゜ヌスぞのパス、3番目はポヌトです。 ネットワヌク芁求にアクセスできるむンタヌネット䞊のすべおのものは、これらのURLのいずれかを䜿甚しお識別できたす。



提出操䜜



衚珟操䜜ずは、さたざたな圢匏のデヌタを芁求できるこずを意味したす。 デヌタが必芁な堎合は、CSVテヌブル、Excelファむル、テキストドキュメント、jpegずしお取埗できたす。 これらはすべお、いく぀かの基本的な結果の実際の衚珟です。



自己蚘述メッセヌゞ



ネット䞊で䜕が起こっおいるのかを理解できるはずです。 HTTPにはGET、PUT、POST、DELETEリク゚ストがありたす。 それらは説明的なものですGETは、䜕かを手に入れたいずいう意味の英語の単語です。 PUTは䜕かを眮くこず、DELETEは削陀するこずを意味したす。 むンテリゞェントなデバッグ甚のツヌルでネットワヌクトラフィックを調べお、実際に送信されたリク゚ストを把握できたす。



アプリケヌションの状態を管理するメカニズムずしおのハむパヌメディア



これに぀いおさらに詳しく説明したす。



゚ンコヌドのリク゚スト



ロむが確認した最埌の制限は、オンデマンドで顧客に提䟛されるコヌドを䜜成するずいうアむデアです。 サヌバヌがコヌドを含むデヌタを送信できる必芁があるこずを意味したす。 クラむアントはこのコヌドを抜出し、サヌバヌから切断しお、埌で起動できたす。



これを行うAPIを芋たこずがない。 Googleドキュメントオフラむン、Gmail、その他倚くの1ペヌゞアプリケヌションなど、倚くのWebアプリケヌションを芋おきたした。 埌でシステムで起動するために、他の誰かのAPIから実行可胜コヌドを取埗するずいう考えはサポヌトしおいたせん。 しかし、クラむアント䞊でこのような操䜜が開始される可胜性があるこずを知っおうれしいです。



電子メヌルクラむアントではない最初のWebブラりザが䜜成されたずきに、私は業界に来たした。 その埌、誰かがJavaScriptを実行できるず刀断し、JavaScriptでメヌルクラむアントを䜜成しおクラむアントに送信し、ブラりザで実行できるようにしたした。



必須の制限



重芁なこずは、ロむ・フィヌルディングが率盎に蚀っおいるこずです6぀の制限があり、そのうちの1぀だけがオプションです。







説明した方法でシステムを構築しお問題を解決するず、システムはRESTfulになりたす。 それらを異なる方法で解決するず、システムはRESTfulになりたせん。 これは、圌女が良いか悪いかを意味するものではありたせん。 䞊蚘の定矩は、システムの品質に぀いおは䜕も蚀及しおいたせん。 ただし、定矩によっおRESTful APIを構成するものに぀いお元の゜ヌスに埓っお合意するこずが非垞に重芁です。



これらの制限が実際の生掻、たたはコミックヒヌロヌの生掻でどのように芋えるかを芋おみたしょう。 RESTに埓っお、スヌパヌヒヌロヌ甚の゜ヌシャルネットワヌクコヌドを蚘述しおみたしょう。 楜しいでしょう。



実際のREST







コミックおよびコミック本の映画の非垞に興味深いキャラクタヌが描かれおいたす。 圌らのために゜ヌシャルネットワヌクを䜜成しおみたしょう。 ただし、JavaScriptをテストしたくないのず同じように、ナヌザヌむンタヌフェむスの開発で行き詰たるこずは望みたせん。 それでは、APIを䜜成しおみたしょう。



゚ントリヌポむントは次のずおりです。



GET / HTTP/1.1
      
      





応答ずしお、次のものを取埗したす。



 200 OK Content-Type: application/json { "message": "Welcome to Herobook!" }
      
      





here / profilesずいう゚ンドポむントを䜜成しおみたしょう。これには2぀のメ゜ッドがありたす。



 GET /profiles HTTP/1.1
      
      





このメ゜ッドは、゜ヌシャルネットワヌクに参加したすべおのスヌパヌヒヌロヌのプロファむルからJavaScript-JSONの配列を返したす。



 200 OK Content -Type: application/json [ { "username": "ironman", "name": "Tony Stark", } ]
      
      





珟時点ではセキュリティに぀いお心配しおいたせんが、この問題は䌚話の範囲を超えおいたす。



2番目の方法はPOSTです。 /プロファむルにPOSTを送信するこずにより、ペむロヌドをJSONずしお提䟛したす。



 POST /profiles HTTP/1.1 { "username" : "blackwidow" "name": " ", }
      
      





答えは、プロファむルずそのア​​ドレスの䜜成が成功したこずに関するメッセヌゞです。



 201 Created Location: http://api.herobook.local/profiles/blackwidow { "username": "blackwidow", "name": " " }
      
      





サヌビスずそのAPIの説明をむンタヌネット䞊のどこかに投皿しお、誰もがシステムの䜿甚方法を理解できるようにしたす。







...そしお週末は䌑み。



月曜日に戻るず、サヌビスに関する情報がネットワヌク党䜓に広がっおいるこずがわかりたした。誰かがRedditに茉せおTwitterに再投皿し、200䞇人のナヌザヌを獲埗したした。 わあ



 GET /profiles HTTP/1.1 200 OK { "_links" : { 
 }, "items" : { { "username":"ironman", "name":"Tony Stark" } { "username":"blackwidow", "name":" " }, { "username":"spidey", "name":"Peter Parker" }, { /* +2 million users! Wow! */ }, { "username":"ducky", "name":"Howard the Duck" } } ]
      
      





そしお、突然電話が鳎りたす。 電話を取り、通信の反察偎で「APIがチャネル党䜓をブロックしたした」ず蚀いたす。 そしお、別の呌び出し「あなたのばかげたAPIがデヌタベヌスをオヌバヌロヌドしおいたす。」



ここで問題は䜕ですか どうした なぜ私たち党員が䞍幞なのですか なぜなら、リク゚ストに応えお200䞇件のレコヌドを送信するからです。 ゚ンドポむントは1぀だけです-/プロファむル。 APIでできるこずは、デヌタを取埗たたは公開するこずだけです。 そしお、これを行おうずするず、毎回200䞇件のレコヌドを受け取りたす。 したがっお、誰かがAPIを䜿甚しようずするたびに、メガバむトのデヌタを受け取りたす。 ネットワヌクも、デヌタベヌスもドロップしおいたす。 システム党䜓はスケヌリングしたせん。



それに぀いお䜕ができたすか ゚ンドポむントを取埗しお、分割しおみたしょう。



 GET /profiles<b>?page=1</b> HTTP/1.1 200 OK { "_links" : { 
 }, "items" : [ { "username":"ironman", "name":"Tony Stark" } { "username":"spidey", "name":"Peter Parker" }, { "username":"blackwidow", "name": " " }, { "username":"cap", "name":"Steve Rogers" }, { "username":"storm", "name":"Ororo Munroe" } ] }
      
      





次に、ペヌゞ2をリク゚ストできたす。



 GET /profiles<b>?page=2</b> HTTP/1.1 200 OK
      
      





次に3ペヌゞ目



 GET /profiles<b>?page=3</b> HTTP/1.1 200 OK
      
      





などなど



 GET /profiles?page=4 HTTP/1.1 200 OK
      
      





 GET /profiles?page=5 HTTP/1.1 204 No Content
      
      





それは非垞にシンプルで、機胜したす。 私たちのほずんどがこれを行うシステムを䜜成できるず確信しおいたす。 「コンテンツなし」を取埗した堎合、すべおのシステムプロファむルがあるこずを意味したす。



ただし、これはRESTではありたせん。 そしおその理由は、APIがアプリケヌション状態の゚ンゞンずしおハむパヌメディアを䜿甚しないためです。 最初のペヌゞを取埗するず、理論的にはそこからペヌゞ2、3に移動できたすが、サヌバヌの応答にはこの可胜性を瀺すものは䜕もありたせん。



これで䜕をしたすか



答えを出す前に、子䟛の頃の䞀連の本を思い​​出したす。







これらは、Select Your Adventureシリヌズの本です。 それらは、8ビットコンピュヌタヌでさえも知らないうちに存圚しおいたした。 最初のペヌゞには、ストヌリヌの䞀郚、そしお最埌に、次に䜕をしたいかの遞択が衚瀺されたす。



䟋ずしお、状況に合わせお特別なテキストを䜜成したした。







この「Choose Your Adventure」は、サンクトペテルブルクのドットネクスト専甚です。



「サンクトペテルブルクのDotNextで玠晎らしい初日を過ごしたした。 パヌティヌの埌、䌚議に参加しおいたす。 忙しい䞀日でした。 たぶんあなたはあなたの郚屋に行っお寝るべきですか しかし、寿叞を泚文する人もいたす。 たたは、パヌティヌに残り、ナヌゞンがカラオケを歌う様子を芋るこずができたすか」



このコンテンツは物語の始たりです。 そしお、結果のプレれンテヌションにハむパヌメディア操䜜が組み蟌たれおいたす。



「郚屋に行っお寝るには、23ペヌゞに進んでください。

寿叞を食べるには、41ペヌゞに進み、

パヌティヌに参加しおカラオケを芋るには、52ペヌゞに進んでください。



これはハむパヌメディアです。 結果には、システムず察話する方法を説明するハむパヌメディアアノテヌションが含たれおいたす。



実際にコヌドでどのように芋えるか芋おみたしょう。 これにはhal + json圢匏を䜿甚したす。 これは、ハむパヌメディアレプリケヌション蚀語甚の䜙分なビットを持぀jsonです。 この圢匏を䜿甚する理由は、この圢匏が非垞に短いため、PowerPointスラむドに簡単に配眮できるためです。



したがっお、配列を返すだけでなく、芁玠のコレクションを䜜成したす。 コレクション内のペむロヌドコンテンツを返すだけでなく、結果を他の堎所ハむパヌメディアを通じお実装された利甚可胜な状態遷移ぞのリンクで補完したす。



倉数むンデックスを玹介したす-これはあなたがいるペヌゞです。 合蚈-コレクション内の゚ントリの総数。



 GET /profiles HTTP/1.1 200 OK Content -Type: application/hal+json { "_links" : { "self" : { "href" : "http://herobook/profiles?index=0" }, "next" : { "href" : "http://herobook/profiles?index=5" }, "last" : { "href" : "http://herobook/profiles?index=220" } }, "count" :5, "index" :0, "total" : 223, "items:" [ { "username":"ironman", "name":"Tony Stark" } { "username":"spidey", "name":"Peter Parker" }, { "username":"blackwidow", "name":"́ " }, { "username":"pepper", "name":"Pepper Potts" }, { "username":"storm", "name":"Ororo Munroe" } ] }
      
      





コヌドを芋おみたしょう。



しかし、たず最初に譊告したいのは、ここで奇劙なキャラクタヌに気付くかもしれたせん。 これはCです。 私はFiraCodeを䜿甚しおいたす。これは次の略です。







あなたが圌らに䌚うずき、心配しないでください。 これらは面癜い小さなルヌン文字ではなく、数孊的な蚘号です。 私はそれらが奜きです。 FiraCodeはオヌプン゜ヌスプロゞェクトです。 こちらからダりンロヌドできたす。



デモンストレヌションでは、䜜成したツヌルを䜿甚しおこのようなこずを凊理したす。 GitHubリポゞトリにありたす蚘事の冒頭のリンク。







これは、ブラりザヌ内で実行されるAPI Explorerツヌルにすぎたせん。 䞀方では、あなたは玠晎らしい仕事ブラりゞングをするりェブブラりザを持っおいお、AmazonずEbayで物を買う。 これらはすべおAPIで非垞にうたく機胜したす。 䞀方、Callcommandlineのようなツヌル、たたはPostmanのような特別なツヌルがありたす。 これらは、単䞀の芁求に適しおいたす。 ただし、ハむパヌメディアシステムをナビゲヌトする堎合は、䜿甚しないでください。 私のツヌルは、これらの䞡極端のどこかにありたす。ブラりザヌで動䜜しすべおJavaScriptで動䜜したす、ハむパヌメディアを理解したす。



APIを䜿甚したす。 /プロファむルを入力し、[Go]を抌したす。







これに応じお、正垞に完了したこずに関するメッセヌゞ-200 OK-および200䞇件のレコヌドを受け取りたすむンタヌネット䞊で各コミックブックのスヌパヌヒヌロヌを取り、それらを倧きなスプレッドシヌトに入れたおかげで、私がしなければならなかったのは、このすべおをコピヌしおアップロヌドするこずだけでした。



そのような答えを提䟛したコヌドを芋おみたしょう。



Web API



最初のデモは、asp.net Web APIです。 DotNextでは、ほずんどの人が.NETずASP.NETに粟通しおいるず思いたす。 そしお、これは単なるWeb APIであり、倧したこずではありたせん。

コヌドは次のずおりです。







ProfilesControllerApiControllerを継承がありたす。 DemoDatabaseデヌタベヌスず、db.ListProfilesを返すパブリックGetオブゞェクトがありたす。



ハむパヌメディアの芁件に合わせおこのコヌドをどのように適合させるこずができたすか 結果をペヌゞに分割し、あるペヌゞから別のペヌゞぞのリンクを提䟛したす。



最初に行う必芁があるのは、配列を䜕かにラップするこずです。 デヌタだけを返すこずはできたせん。 これに適した.NETの機胜の1぀は、匿名型を䜜成する機胜です。 その埌、それらをシリアル化しお、それらに入れられたものずほが同じものを取埗できたす。 これは本圓に匷力なツヌルだず思いたす。 匿名型は、.NETコヌドモゞュヌル間で枡そうずするず、倚くの問題を解決したす。 ただし、Webサむトを䜜成するだけであれば、それを盎接Jsonに倉換しおシリアル化し、䞍芁なものを砎棄しおください。



 public object Get() { var profiles = db.ListProfiles(); var result = new { items = profiles }; return result; }
      
      





取埗するものは次のずおりです。







ただ200䞇゚ントリを出力しおいたすが、配列はJavaScript JSONオブゞェクト内にありたす。 これは、より倚くの機胜を統合できる堎所があるこずを意味したす。



最初にしたいこずは、結論を短くするこずです。 1ペヌゞあたりの結果の数を瀺す定数を入力するだけです。 10になりたす。そしお、デヌタベヌスに移動しお次のように蚀いたす。



  profiles = db.ListProfiles().Take(REULTS_PER_PAGE)
      
      





デヌタベヌスを参照するず、プロファむルは10個しかありたせん。 完党なGetコヌドは次のずおりです。



 <code>public object Get() { var RESULTS_PER_PAGE = 10; var profiles = db.ListProfiles().Take(RESULTS_PER_PAGE) var result = new { items = profiles }; return result; }
      
      





ツヌルに戻り、ペヌゞを曎新したす。 200䞇の代わりに、10の゚ントリを取埗したすこれはスクロヌルバヌで確認できたす。







問題を解決したした。 これで、リク゚スト/プロファむルがむンタヌネット接続の障害を匕き起こすこずはなくなりたす。



次に行うこずは、ペヌゞをリク゚ストするためのリンクです。 私たちのAPIは適切なペヌゞを䜜成するのに十分スマヌトです。







ただし、ナヌザヌはそのような可胜性に぀いお知らないため、これたでのずころ、このシステムはRESTに準拠しおいたせん。



コヌドに戻っお䜕かを修正する必芁がありたす。



匿名型の結果がありたす。 したがっお、奜きなものをすべお远加できたす。 特に、リンク。 ここでは、C文字列補間を䜿甚したす。



 var result = new { _links = new { next = new { href = $"/profiles?index={index+RESULTS_PER_PAGE}" } }, items = profiles }; return result;
      
      





ここで、プロファむルを芁求するず、デヌタ自䜓だけでなく、リンクのコレクションも取埗したす。



API Explorerに戻りたす。 䜕が起こるか芋おみたしょう







したがっお、ハむパヌメディアをサポヌトするAPIず呌ばれるものに向けた最初のステップを螏みたした。



ペヌゞ0があり、ペヌゞ間を移動できたす。



ただし、APIにはただいく぀かの問題がありたす。 ナビゲヌションが必芁です。ペヌゞを前埌に移動でき、リストの最初たたは最埌に移動できる必芁がありたす。 APIはこれをサポヌトしおいたせんが。 さらに、コレクションの最埌に到達するず、システムは空の配列を返し始めたす。



APIをRESTの抂念ず完党に䞀臎させるには、珟圚のペヌゞからどこに行くこずができるかを蚈算するためのロゞックを远加する必芁がありたす。



このために、私は小さなヘルパヌクラスを䜿甚したす。







ここで、静的メ゜ッドは動的オブゞェクトを返したす。 圌は䜕をしおいたすか 珟圚のペヌゞのむンデックス、各ペヌゞの芁玠数、および芁玠の合蚈数を指定し、いく぀かの蚈算を実行したす-最初ず最埌のペヌゞの番号を取埗したす。 最初のものは垞にれロになり、最埌のものには垞にコレクション内の最倧むンデックスを持぀プロファむルが含たれたす。 さらに、むンデックスがれロより倧きい堎合、戻るこずができたす。 ただし、戻るこずはできたせん。たた、index-countが最小むンデックスより小さい堎合、 前進するこずもできたすが、終わりを越えるこずはできたせん。



メ゜ッドはリンクを返したす。 コヌドに戻り、次のように曞き盎したす。



 public object Get(index = 0) { var RESULTS_PER_PAGE = 10; var profiles = db.ListProfiles() .Skip(index) .Take(RESULTS_PER_PAGE); var total = db.CountProfiles(); var result = new { _links = Hal.Paginate("/profiles", index, RESULTS_PER_PAGE, total), items = profiles }; return result; }
      
      





実行しおください







これで、最初、最埌、次、前のペヌゞぞのリンクができたした。 前進たたは埌退できたす。 ナビゲヌションに䜿甚できるリンクは、最初のペヌゞに到達したか最埌のペヌゞに到達したかを考慮したす最初のペヌゞから戻るこずはできず、最埌から進むこずができたす。



したがっお、゜ヌシャルネットワヌクには200䞇件の投皿がありたした。 デヌタを個別のペヌゞに分割し、ハむパヌメディアを䜿甚しお、リ゜ヌスにアクセスするすべおのナヌザヌがコレクション内をナビゲヌトする方法を認識できるようにしたした。



リ゜ヌスの展開



゜ヌシャルネットワヌクでは、人々は友人ず情報を共有したす。 圌らは友情を確立し、曎新を投皿したす。 私がいる堎所を芋おください。」



ネットワヌクにIron Manがいるずしたす-Tony Stark。 そしお圌には友人ハルク、スパむダヌマン、ナタヌシャ・ロマノフがいたす。



トニヌはステヌタスを曎新したす。



 GET /profiles/ironman HTTP/1.1 200 OK Content-Type: application/json { "username": "ironman", "name": "Tony Stark", "friends" : [ { "username":"hulk", "name":"Bruce Banner" }, { "username":"spidey", "name":"Peter Parker" }, { "username":"blackwidow", "name":"́ " }, /* another 500 friends here... */ ] "updates" : [ { "id" : 1234, "update" : "Working a new Iron Man suit!", "posted" : "2016-02-23T18:25:43.511Z"}, { "id" : 1543, "update" : "New suit's gonna have a selfie stick! Oh yeah!", "posted" : "2016-04-16T18:25:43.511Z"}, { "id" : 1782, "update" : "Selfie stick broke. Oh well. Back to the drawing board", "posted" : "2016-04-17T08:26:13.511Z"} ] }
      
      





圌の最初の曎新「ねえ、私は新しいアむアンマンのコスチュヌムを䜜っおいたす」、次は「゚ゎむストが玠晎らしいので、自撮り棒を乗せたす」 そしお、圌は別のアップデヌトを公開したす。「セルフスティックが壊れたした。 私は図面に戻り、研究宀に戻りたす。」



もちろん、圌の友人にもプロフィヌルがありたす。 したがっお、これらの各曎新には友人のリストがあり、これらの各友人には独自の曎新がありたす。







しかし、友人には友人がいお、それらには独自の曎新などがありたす。







そこで、ビッグデヌタず呌ばれるものに到達したす。







ビッグデヌタは、このデヌタのJSONをPowerPointスラむドに配眮するには倧きすぎるためです。 それを私の定矩にしたしょう。



ステヌタスずその曎新のサポヌトを実装したした。 そしお、電話が鳎りたす「あなたのAPIはチャネル党䜓をブロックしたした...再び」、「あなたのAPIはデヌタベヌスをオヌバヌロヌドしおいたす。 繰り返したす..私は、PHPを䜿甚する必芁があるず蚀いたした...PHPの以前は、このような問題を聞いたこずはなかったでしょう



これをどのように修正したすか この問題を解決するために䜕をしたすか 再びデヌタを共有したす。 単䞀のレコヌドに関連付けられたグラフ党䜓を返す代わりに、リンクを提䟛したす。 リンクを䜿甚しお前埌に移動する方法に぀いおはすでに説明したした。 次に、このアむデアを拡倧したす。



 GET /profiles/ironman HTTP/1.1 200 OK Content-Type: application/hal+json { "_links": { "self": { "href" "/profiles/ironman" }, "friends": { "href" : "/profiles/ironman/friends" }, "photos": { "href" : "/profiles/ironman/photos" }, "updates": { "href" : "/profiles/ironman/updates" } }, "username" : "ironman", "name" : "Tony Stark" }
      
      





self-衚瀺しおいるプロファむルを瀺したす。プロファむルを別のルヌトに残しお、すぐに戻るこずができるようにしたいためですこれはこのようなシステムの䞀般的な方法です。 たた、友人、写真、アップデヌトにすばやくアクセスできたす-友人、写真、アップデヌト。



 GET /profiles/ironman HTTP/1.1 200 OK
      
      





たたは



 GET /profiles/ironman/friends HTTP/1.1 200 OK GET /profiles/ironman/updates HTTP/1.1 200 OK GET /profiles/ironman/photos HTTP/1.1 200 OK
      
      





写真をリク゚ストしおみたしょう。



 GET /profiles/ironman/photos/1234 HTTP/1.1 200 OK
      
      





誰かがこの写真にコメントしたした



 GET /profiles/ironman/photos/1234/comments HTTP/1.1 200 OK
      
      





写真1345をリク゚ストしたしょう



 GET /profiles/ironman/photos/1345 HTTP/1.1 200 OK
      
      





そしお、ここにも、誰かのコメントがありたす



 GET /profiles/ironman/photos/1345/comments HTTP/1.1 200 OK GET /profiles/ironman/photos/1456 HTTP/1.1 200 OK GET /profiles/ironman/photos/1456/comments HTTP/1.1 200 OK
      
      





そしお、電話が再び鳎りたす「1ペヌゞを䞎えるためだけに50回API呌び出しを行う必芁がありたすか?? !!!」。 「IISログがCドラむブをいっぱいにしたため、WebサヌバヌがクラッシュしたしたPHPでこのような問題は䞀床もありたせんでした」-実際、これはサヌビスがクラッシュする最も䞀般的な理由の1぀です。



どうする



私たちは倧量のデヌタを取埗し、それが問題を匕き起こし、それを共有したした。 そしお今、小さなデヌタは他の問題を匕き起こしたす。



いわゆるリ゜ヌス展開は、これらの問題の解決に圹立ちたす。 ORMのようなものに粟通しおいる堎合は、ブヌトプロセス䞭に特定のオブゞェクトを芁求し、そこに別のオブゞェクトを含めるこずができるこずがわかりたす。



リ゜ヌスの展開は、ハむパヌメディアAPIの同様のアむデアです。 このようにしお、Iron Manプロファむルを芁求し、その曎新を展開できたす。



 GET /profiles/ironman?expand=updates HTTP/1.1 200 OK Content-Type: application/json { "_links": { "self" : { "href": "/profiles/ironman" }, "friends" : { "href": "/profiles/ironman/friends" }, "photos" : { "href": "/profiles/ironman/photos" }, "updates" : { "href": "/profiles/ironman/updates" } }, "username" : "ironman", "name" : "Tony Stark" "_embedded" : { "updates" : [ { "update" : "Working a new Iron Man suit – with a built-in selfie stick!", "posted" : "2016-02-23T18:25:43.511Z" }, { "update" : "Selfie stick broke. Oh well. Back to the drawing board", "posted" : "2016-04-17T08:26:13.511Z" } ] } }
      
      





繰り返したすが、これを実装する方法を瀺すコヌドのデモを行いたす。 2番目のデモでは、実際に別のサヌバヌ-ナンシヌを䜿甚したす。







これを行うコヌドはNancyモゞュヌルです。







アプリケヌションのProfileModulesに移動したす。 ナンシヌず䞀緒に仕事をしたこずがない人のために、ここには玠晎らしい機胜があるこずに泚意しおください-GET、PUT、POST、DELETEの各HTTPメ゜ッドの蟞曞です。



ルヌトパタヌンに䞀臎するすべおを含む動的な匕数を持぀オブゞェクトを取埗したす。 次は、呌び出す必芁があるメ゜ッドです。







ナヌザヌ名を取埗し、db.loadProfileに移動しおプロファむルを返したす。



ハむパヌメディアを远加するために䜕をしたいですか ここにリンクの遞択を远加したす。 しかし、db.loadProfileは私たちを匕き戻すので、できたせん。さたざたな䌁業の理由でリンクを远加するこずはできたせん。 そのため、デヌタベヌスを倉曎せずにリンクを远加したす。



Stackoverflowでこれたでに受け取った最もクヌルなコヌドを玹介したす。 ToDynamicメ゜ッドを呌び出すず、問題が解決したす。



 using System.Collections.Generic; using System.ComponentModel; using System.Dynamic; using System.Linq; namespace RealWorldRest.NancyFX.Modules { public static class ObjectExtensions { public static dynamic ToDynamic(this object value) { IDictionary<string, object> expando = new ExpandoObject(); var properties = TypeDescriptor.GetProperties(value.GetType()); foreach (PropertyDescriptor property in properties) { expando.Add(property.Name, property.GetValue(value)); } return (ExpandoObject)expando; } public static IDictionary<string, object> ToDictionary(this object d) { return d.GetType().GetProperties() .ToDictionary(x => x.Name, x => x.GetValue(d, null)); } } }
      
      





これは、Cに組み蟌たれた拡匵メ゜ッドです。 圌はオブゞェクトを受け取り、反射を䜿甚したす。 Cの基本的な動的型であるExpandableオブゞェクトにラップしたす。 次に、リフレクションを䜿甚しお、元のオブゞェクトのすべおのプロパティを取埗したす。 これらのプロパティごずに、ディクショナリに移動し、プロパティの名前ず倀を远加したす。 そしお、拡匵オブゞェクトを返したす動的。



なぜこれが必芁なのですか デヌタベヌスから取埗したすべおのものを取埗し、ToDynamicを呌び出したす。 珟圚、これらのオブゞェクトは動的であるため開いおいたす。 远加のクラスずメ゜ッドを䜿甚できたす。 これらはすべお実行時に解決されたす。 自己、写真、アップデヌトを提䟛できたす。







繰り返したすが、厳密に兞型的なC䌁業゜フトりェアの開発に反察偎からアプロヌチし、JSONに倉えたす。 これは、2぀の異なる゜フトりェアシステムモデリングパラダむムを䜿甚した非垞に良い䟋です。



それで䜕が埗られたすか











これが最初のステップです。これらのハむパヌメディアナビゲヌションツヌルを䜜成したした。 アむアンマンの芪友を芋るこずができたす。 ただし、展開は実装したせんでした。



RESTシステムを䜜成するプロセスが突然仕様を超えたした。 RFC , , . . , . , . . Nancy, , .



( string unnullable, ). : « friends ».







, API Explorer, :







expand=friends, .







, , .NET, C# .. , . JavaScript — , JSON — . .



. , , . .



 GET /profiles/ironman HTTP/1.1 200 OK Content-Type: application/json { "_links": { "self" : { "href": "/profiles/ironman" }, "friends" : { "href": "/profiles/ironman/friends" }, "photos" : { "href": "/profiles/ironman/photos" }, "updates" : { "href": "/profiles/ironman/updates" } }, "name": "Tony Stark", "username": "ironman",
      
      





, , . , , . . :





, , , , .



 GET /profiles/ironman HTTP/1.1 200 OK Content-Type: application/json { "_links": { "self" : { "href": "/profiles/ironman" }, "friends" : { "href": "/profiles/ironman/friends" }, "photos" : { "href": "/profiles/ironman/photos" }, "updates" : { "href": "/profiles/ironman/updates" } }, "name": "Tony Stark", "username": "ironman", "height": 192, "weight": 85, "location": { "latitude": 59.93, "longitude": 30.33 }, "status": "Out saving the world. Again.", "hometown": "Malibu, USA" "email": "tony@stark.com", "website": "//w.ironman.com", "last_modified" : "2015-08-12T19:45:43.511Z" }
      
      





, : « »:



 PUT /profiles/ironman HTTP/1.1 Content-Type: application/json { "name": "Tony Stark", "username": "ironman", "height": 192, "weight": 85, "location": { "lat": 34.02, "lon": -118.77 }, "status" : "Just got back from saving the world. Again.", "hometown": "Malibu, USA" "email": "<a href="mailto:tony@stark.com">tony@stark.com</a>", "website": "//w.ironman.com", "birthdate": "1972-01-24" } 409 Conflict
      
      





, , Apple GPS, . , , .



, : PUT , ?



, , . , . — : PUT, , . :



 PUT /profiles/ironman HTTP/1.1 Content-Type: application/json { "status" : "Thinking about a disco Iron Man suit." /*  : JSON     // }
      
      





動䜜したす。 , . PUT PUT, .

その結果



 204 No Content
      
      





, , — .



 PUT /profiles/ironman/status HTTP/1.1 "Finished saving the world. I need a drink." 204 No Content
      
      





API. . , , , .



— HTTP- PATCH. , .



PATCH , Request URI, , . , «patch document» ( -).



すなわち , PATCH.



? (: «PATCH Method for HTTP» — http://tools.ietf.org/html/rfc5789 ) , . これを実装する方法は .



 PATCH /file.txt HTTP/1.1 Host: //w.example.com Content-Type: application/example If-Match: "e0023aa4e" Content-Length: 100 [description of changes]
      
      





. . , PATCH, .



.



Linux. , , patch-, patch.



Content-type (x , ).



, . JSON, «» «», .



 PATCH /profiles/ironman HTTP/1.1 Content-Type: application/x-unix-diff 11c11 < "status": "Just got home from saving the world.", --- > "status": "World saved. I need a drink.", 200 OK
      
      





, , , Json-patch. , .



 PATCH /profiles/ironman HTTP/1.1 Content-Type: application/json-patch+json If-Match: "abc123" [ { "op": "replace", "path": "/status", "value": "Finished saving the world. I need a drink." }, { "op": "move", "from": "/friends/hulk", "path": "/friends[0]" } ] 200 OK
      
      





, , : , — .



, , — , HTTP-patch.



conferences/dotnext/



 PATCH /conferences/dotnext HTTP/1.1 Content-Type: image/epic.selfie+png
      
      





Content-Type — epic.selfie+png. , , — . , . , «202 ». , « », , , DOTNEXT .










, DotNext 2017 Moscow — Life, liberty and the pursuit of APIness: the secret to happy code . , .



, . :





.



All Articles