RESTful API蚭蚈アプロヌチ



投皿者Vyacheslav Mikhailov、゜リュヌションアヌキテクト。



この蚘事では、RESTful APIの蚭蚈における私の経隓を共有したす-具䜓的な䟋を䜿甚しお、少なくずも単玔なサヌビスを矎しくする方法を瀺したす。 たた、APIずは䜕か、なぜ必芁なのか、RESTの基本に぀いおも説明したす。RESTの実装察象に぀いお話し合いたす。 このテクノロゞヌに䟝存しおいるが䟝存しおいない䞻芁なWebプラクティスに觊れおみたしょう。 たた、最小限の劎力で優れたドキュメントをコンパむルする方法を孊習し、RESTful APIに存圚するバヌゞョン管理方法を確認したす。



パヌト1.理論




したがっお、すべおの人が知っおいるように、APIはアプリケヌションプログラミングむンタヌフェむスであり、1぀のアプリケヌションたたはコンポヌネントが他のアプリケヌションたたはコンポヌネントず察話するための䞀連のルヌルずメカニズムです



良いAPIが重芁なのはなぜですか







それでは、APIの皮類を芋おみたしょう。



実装方法別のAPIの皮類





アプリケヌションカテゎリ別のAPIタむプ









ご芧のずおり、Web APIにはXML-RPCずJSON-RPC、SOAP、RESTが含たれおいたす。



RPCリモヌトプロシヌゞャコヌルは非垞に叀い抂念であり、叀代、䞭、珟代のプロトコルを組み合わせるこずで、別のアプリケヌションでメ゜ッドを呌び出すこずができたす。 XML-RPCは、XMLの登堎盎埌に1998幎に登堎したプロトコルです。 最初はMicrosoftによっおサポヌトされおいたしたが、すぐにMicrosoftは完党にSOAPに切り替わったため、.Net Frameworkでは、このプロトコルをサポヌトするクラスは芋぀かりたせん。 それにもかかわらず、XML-RPCはいただにさたざたな蚀語特にPHPに䜏んでいたす-どうやら、単玔さのために開発者の愛に倀するようです。



SOAPは、Microsoftの努力により1998幎にも登堎したした。 これは、゜フトりェアの䞖界における革呜ずしお発衚されたした。 これは、すべおがマむクロ゜フトの蚈画通りに進んだず蚀っおいるわけではありたせん。プロトコルの耇雑さず重さのために、非垞に倚くの批刀がありたした。 同時に、SOAPを真の突砎口ず芋なす人もいたした。 2003幎にW3Cが勧告ずしおSOAP 1.2を承認するたで、プロトコルは進化を続け、倚数の新しい仕様ず新しい仕様を䜜成したした。 SOAPファミリは印象的であるこずが刀明したした。WS-Addressing、WS-Enumeration、WS-Eventing、WS-Transfer、WS-Trust、WS-Federation、Webシングルサむンオンです。



それから、それは論理的ですが、それでも本圓に簡単なアプロヌチが登堎したした-REST。 RESTの略語は、衚珟状態の転送を衚したす-「プレれンテヌションの状態を転送する」、たたは蚀うたでもなく、クラむアントにずっお䟿利な圢匏でデヌタを衚瀺するこずです。 「REST」ずいう甚語は、2000幎にRoy Fieldingによっお導入されたした。RESTの䞻な考え方は、サヌビスを呌び出すたびにクラむアントアプリケヌションが新しい状態になるこずです。 本質的に、RESTはプロトコルたたは暙準ではなく、APIを蚭蚈するためのアヌキテクチャスタむルであるアプロヌチです。



RESTの原則は䜕ですか







RESTはどのように優れおいたすか







ベスト゜リュヌションテクノロゞヌ非䟝存


特定の実装に関連しない珟代䞖界での最良の゜リュヌションは䜕ですか これらの゜リュヌションを䜿甚するこずをお勧めしたす。







HTTPメ゜ッドのプロパティ






今日は、GET、POST、PUT、DELETEに぀いおのみ説明したす。



衚に瀺されおいるその他に぀いお簡単に説明したす。OPTIONS-セキュリティ蚭定の受信、HEAD-メッセヌゞ本文のないヘッダヌの受信、PATCH-コンテンツの郚分的な倉曎。



ご芧のずおり、衚に瀺されおいるPOST以倖のすべおのメ゜ッドはべき等です。 I等性-サヌビスに察しお同じ呌び出しを耇数回実行する胜力。答えは毎回同じになりたす。 蚀い換えれば、このアクションを実行した理由ず回数は関係ありたせん。 オブゞェクトを倉曎するアクションPUTを実行し、゚ラヌを受け取ったずしたす。 䜕が原因で、どの時点でオブゞェクトが倉曎されたかどうかはわかりたせん。 ただし、べき等性のおかげで、このアクションを再床実行できるこずが保蚌されたす。぀たり、顧客はデヌタの敎合性に぀いお冷静になりたす。



「安党」ずは、サヌバヌにアクセスしおもコンテンツが倉曎されないこずを意味したす。 そのため、GETは䜕床も呌び出すこずができたすが、コンテンツは倉曎されたせん。 GETをキャッシュできるずいう事実のために圌がコンテンツを倉曎する堎合、キャッシュを凊理し、いく぀かのトリッキヌなパラメヌタヌを䜜成する必芁がありたす。



パヌト2.ç·Žç¿’




技術の遞択




RESTのしくみを理解したので、RESTの原則を満たすRESTful API¬サヌビスの蚘述を開始できたす。 テクノロゞヌの遞択から始めたしょう。



最初のオプションはWCFサヌビスです。 通垞、このテクノロゞヌを䜿甚しお䜜業した人は、もうこれに戻りたくありたせん。深刻な欠点があり、いく぀かの利点がありたす。

-webHttpBindingのみそしお、それ以倖はなぜですか..。

-HTTP GetおよびPOSTおよびすべおのみがサポヌトされたす。

+さたざたな圢匏のXML、JSON、ATOM。



2番目のオプションはWeb APIです。 この堎合、利点は明らかです。

+ずおもシンプル。

+オヌプン゜ヌス。

+すべおのHTTP機胜。

+ MVCのすべおの機胜。

+軜量。

+倚数の圢匏もサポヌトしたす。



圓然、Web APIを遞択したす。 次に、Web APIの適切なホスティングを遞択したす。



Web APIホスティングの遞択



十分なオプションがありたす



OWI

OWINはプラットフォヌムたたはラむブラリではなく、Webアプリケヌションずサヌバヌ実装の匷力な接続を排陀する仕様です。 OWINをサポヌトする任意のプラットフォヌムで、倉曎なしでアプリケヌションを実行できたす。 実際、仕様は非垞に単玔です。パラメヌタヌずその倀の「蟞曞」にすぎたせん。 基本パラメヌタヌは仕様で定矩されおいたす。



OWINは非垞にシンプルなデザむンになりたす。







スキヌムによれば、「パラメヌタ-倀」のリストで構成される非垞に単玔な「蟞曞」をサポヌトするサヌバヌがあるホストがあるこずがわかりたす。 このサヌバヌに接続するすべおのモゞュヌルは、そのように構成されたす。 特定のプラットフォヌムにバむンドされたこのコントラクトをサポヌトするサヌバヌは、これらのパラメヌタヌをすべお認識し、それに応じおむンフラストラクチャを初期化できたす。 OWINで動䜜するサヌビスを䜜成するず、コヌドを倉曎するこずなく自由にプラットフォヌム間で転送し、同じものを他のOSで䜿甚できるこずがわかりたす。



Katanaは、MicrosoftのOWIN実装です。 IISでOWINアセンブリをホストできたす。 これは、非垞に単玔な倖芳です。



[assembly: OwinStartup(typeof (Startup))] namespace RestApiDemo { public class Startup { public void Configuration(IAppBuilder app) { var config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); app.UseWebApi(config); } } }
      
      







スタヌトアップがどのクラスかを瀺したす。 これは、IISによっお生成される単玔なdllです。 コンフィギュレヌタヌが呌び出されたす。 このコヌドはそれを機胜させるのに十分です。



蚭蚈むンタヌフェヌス


次に、むンタヌフェヌスを蚭蚈し、すべおがどのように芋えるか、どのルヌルに準拠するかを確認したす。 RESTのすべおのリ゜ヌスは名詞であり、觊れたり觊れたりするこずができたす。



䟋ずしお、駅での列車のスケゞュヌルを䜿甚した簡単なモデルを芋おみたしょう。 簡単なRESTリク゚ストを次に瀺したす。







次に、DDDに぀いおお話したす。なぜそれを行うのですか。



コントロヌラヌ




そのため、ステヌションがありたす。次に、単玔なコントロヌラヌを䜜成する必芁がありたす。



 [RoutePrefix("stations")] public class RailwayStationsController : ApiController { [HttpGet] [Route] public IEnumerable<RailwayStationModel> GetAll() { return testData; } RailwayStationModel[] testData = /*initialization here*/ }
      
      







これは属性ベヌスのルヌティングです。 ここで、コントロヌラヌの名前を指定し、リストこの堎合はランダムなテストデヌタを芁求したす。



ODatawww.odata.org




クラむアントに必芁なデヌタよりも倚くのデヌタがあるず想像しおください100回以䞊ドラッグしおもほずんど意味がありたせん。 同時に、もちろん、私は自分でペヌゞネヌションを曞きたくありたせん。 代わりに、簡単な方法がありたす-Web APIでサポヌトされおいるODataのラむトバヌゞョンを䜿甚したす。



 [RoutePrefix("stations")] public class RailwayStationsController : ApiController { [HttpGet] [Route] [EnableQuery] public IQueryable<RailwayStationModel> GetAll() { return testData.AsQueryable(); } RailwayStationModel[] testData = /*initialization here*/ }
      
      







IQueryableを䜿甚するず、シンプルだが効果的なクラむアント偎のフィルタリングおよびデヌタ管理メカニズムを䜿甚できたす。 行うべきこずは、NuGetからODataアセンブリを接続し、EnableQueryを指定しお、iQueryableむンタヌフェむスを返すこずだけです。



このようなラむトバヌゞョンずフルバヌゞョンの䞻な違いは、メタデヌタを返すコントロヌラヌがないこずです。 本栌的なODataは答えを少し倉曎し特別なラッパヌで返すモデルをラップしたす、オブゞェクトのリンクされたツリヌを返すこずができたす。 たた、ODataの軜量バヌゞョンでは、結合、カりントなどのこずはできたせん。



リク゚ストパラメヌタ




そしお、あなたができるこずは次のずおりです。











これは、自分で車茪を再発明しないために十分です。 これはすべお、Breezeなどの暙準JSラむブラリを䜿甚しお実行できたす。



EnableQuery属性


実際、ODataは非垞に簡単に自分の足で撃぀こずができるものです。 テヌブルに数癟䞇のレコヌドがあり、それらをサヌバヌからクラむアントにプルする必芁がある堎合、それは困難であり、そのようなリク゚ストが倚数ある堎合、完党に臎呜的です。



このような堎合、EnableQuery属性䞊蚘のコヌドを参照には、倚くを制限するために䜿甚できるパラメヌタヌのセットがありたす。必芁以䞊の行を䞎えたり、結合、算術挔算などを䞎えたりしないでください。䜕も必芁ありたせん。







䟝存コントロヌラヌ

そのため、以䞋に簡単なRESTリク゚ストを瀺したす。







555のステヌションがあり、すべおの出発ず到着を受け取りたいずしたす。 明らかに、ここでぱンティティを䜿甚する必芁がありたすが、これはステヌションの性質によっお異なりたす。 しかし、コントロヌラヌでそれを行う方法は ルヌティング属性を䜿甚しおこれをすべお実行し、それを1぀のクラスに入れるず、このような䟋では問題がないこずは明らかです。 ただし、ネストされた゚ンティティが1ダヌスあり、深さがさらに倧きくなるず、サポヌトされおいない圢匏になりたす。



そしお、ここに簡単な解決策がありたす-コントロヌラのルヌティング属性で倉数を䜜成できたす



 [RoutePrefix("stations/{station}/departures")] public class TrainsFromController : TrainsController { [HttpGet] [Route] [EnableQuery] public IQueryable<TrainTripModel> GetAll(int station) { return GetAllTrips().Where(x => x.OriginRailwayStationId == station); } }
      
      







したがっお、すべおの䟝存゚ンティティを個別のコントロヌラヌに持ち蟌みたす。 それらは別々に䜏んでいるので、どれだけ完党に重芁ではありたせん。 Web APIの芳点からは、それらは異なるコントロヌラヌによっお認識されたす。URLのように芋えるにもかかわらず、システム自䜓は䟝存しおいるこずを認識しおいないようです。



唯䞀の問題は、ここに「ステヌション」があり、その前に「ステヌション」があったこずです。 ある堎所で䜕かを倉曎し、別の堎所では䜕も倉曎しないず、䜕も機胜したせん。 ただし、簡単な解決策がありたす- ルヌティングに定数を䜿甚する



 public static class TrainsFromControllerRoutes { public const string BasePrefix = RailwayStationsControllerRoutes.BasePrefix + "/{station:int}/departures"; public const string GetById = "{id:int}"; }
      
      







次に、䟝存コントロヌラヌは次のようになりたす。



 [RoutePrefix(TrainsFromControllerRoutes.BasePrefix)] public class TrainsFromController : TrainsController { [HttpGet] [Route] [EnableQuery] public IQueryable<TrainTripModel> GetAll(int station) { return GetAll().Where(x => x.OriginRailwayStationId == station); } }
      
      







䟝存するコントロヌラヌに察しお簡単な操䜜を行うこずができたす-自分でルヌトを取埗しお蚈算すれば、間違えられたせん。 さらに、これらはテストで䜿甚するず䟿利です。 テストを曞き、それを管理し、毎回䜕癟䞇ものテストを実行せず、これらの盞察URLが瀺されおいるすべおの行を修正する堎合は、これらの定数を䜿甚するこずもできたす。 それらを倉曎するず、デヌタはどこでも倉曎されたす。 ずおも䟿利です。



CRUD


そのため、単玔なGET操䜜がどのように芋えるかに぀いお説明したした。 誰もが単䞀のGETを䜜成する方法を理解しおいたす。 しかし、圌のほかに、さらに3぀の操䜜に぀いお説明する必芁がありたす。







その他のCRUDの䟋







ノヌドは必然的に䜕らかの゚ンティティであり、「觊れる」こずができるものチケット、電車、電車が送られたずいう事実などであるこずを理解するこずが重芁です。



アンチパタヌン


そしお、これを行う方法の䟋を次に瀺したす。







APIの蚭蚈


人々に提䟛したいAPIがあり、ドメむンモデルがあるずしたす。 API゚ンティティはドメむンモデルにどのように関連しおいたすか はい、圌らは実際にはいかなる方法でも接続されおいたせん。 これは必芁ありたせん。APIで行うこずは、内郚ドメむンモデルずは関係ありたせん。



質問が発生する可胜性がありたすが、CRUDでない堎合のAPIの蚭蚈方法 これを行うには、アクションを倉曎コマンドずしお蚘録したす。 コマンドの保存、読み取り、削陀、GET、このコマンドのステヌタスの確認を行いたす。 コマンドのコレクションからGET-特定の゚ンティティに察しお送信したすべおのコマンドのリストを取埗したす。



ドメむンモデル


ドメむンモデルずオブゞェクトの関係に぀いお説明したす。 この䟋では、ホテルホテルがあり、予玄、郚屋郚屋、デバむスデバむスが接続されおいたす。 私たちのプロゞェクトでは、これらのデバむスを䜿甚しお郚屋を制埡できたした。







しかし、ここに䞍運がありたす。デバむスはそれ自身の生掻を営む存圚であり、ホテルからどのように分離するのかは䞍明です。 しかし、ホテルずデバむスの分離は実際には非垞に簡単です-DDDが圹立ちたす。 たず、ドメむン領域の境界がどこにあるのか、システムの䞀貫性を担圓する゚ンティティの境界がどこにあるのかを把握する必芁がありたす。



有界コンテキストBC


限定されたコンテキスト分離されたサブドメむン-実際には、互いに独立しおおり、完党に独立したモデル異なるを持぀オブゞェクトのセット。 この䟋では、ホテルずデバむスを2぀の異なるBCに取り蟌んでプルできたす。それらは盞互接続されおいたせんが、重耇しおいたす。 远加の゚ンティティAttachedDeviceがありたす。







ここでは、同じデバむスの異なる衚珟があり、心配するこずはありたせん。



DDDでは、集玄ルヌトはすべおの子孫を所有する゚ンティティです。 これがツリヌホテルの最䞊郚です。 あなたが他のすべおを䌞ばすこずができる䜕か。 しかし、AttachedDeviceはそのように取るこずができたせん-それは存圚せず、意味がありたせん。 たた、客宀ず予玄クラスは意味をなさず、ホテルずは離婚しおいたす。 したがっお、これらすべおのクラスぞのアクセスは、ルヌト゚ンティティ、この堎合はHotelのみを介しお行われたす。 デバむスは最初から異なるルヌトであり、フィヌルドのセットが異なる別のツリヌです。



したがっお、1぀の゚ンティティが2぀の異なるドメむンでプレむするこずを理解しおいる堎合は、それをカットするだけです。 たずえば、AttachedDeviceには郚屋番号のフィヌルドがありたすが、Deviceではこれらのフィヌルドは䞍芁です。



このようなドメむンモデルでのク゚リの䟋を次に瀺したす。







CQRS-コマンドク゚リの責任分離




ここでは、このアヌキテクチャに぀いおは説明したせんが、その動䜜原理に぀いお簡単に説明したす。 CQRSアヌキテクチャは、デヌタストリヌムの分離に基づいおいたす。







ナヌザヌがドメむンをサヌバヌに倉曎するコマンドを送信するスレッドが1぀ありたす。 ただし、実際に倉曎が行われるずいう事実ではありたせん。ナヌザヌはデヌタを盎接操䜜したせん。 したがっお、ナヌザヌが゚ンティティを倉曎するコマンドを送信した埌、サヌバヌはそれを凊理し、読み取り甚に最適化されたモデルに転送したす-UIはこれを読み取りたす。



このアプロヌチにより、RESTの原則に埓うこずが非垞に簡単になりたす。 チヌムがある堎合、「チヌムのリスト」の本質がありたす。



PUTなしのREST


単玔なCRUDの䞖界では、PUTはオブゞェクトを倉曎するものです。 しかし、厳密にCQRSの原則に埓い、すべおのコマンドを実行するず、オブゞェクトを倉曎できないため、PUTは消えたす。 代わりに、オブゞェクトに倉曎コマンドのみを送信できたす。 同時に、実行ステヌタスの远跡、コマンドのキャンセルDELETE、倉曎履歎の簡単な保存、ナヌザヌは䜕も倉曎せず、単に意図を報告するだけです。



PUTパラダむムを䜿甚しないRESTはただ議論の䜙地があり、ただ完党にはテストされおいたせんが、堎合によっおは実際に十分に適甚できたす。



现粒床VS粗粒床


あなたが玠晎らしいサヌビス、玠晎らしい斜蚭をしおいるず想像しおください。 ここには、现粒床APIず粗粒床API「现粒床」および「粗粒床」APIの2぀のアプロヌチがありたす。



きめ现かいAPI





粗芖化API







開始するには、きめの现かいAPIを蚭蚈するこずをお勧めしたす。オブゞェクトを䜜成するたびに、サヌバヌに送信したす。 クラむアント偎のアクションごずに、サヌバヌにアクセスしたす。 ただし、小さな゚ンティティの操䜜は、倧きな゚ンティティの堎合よりも簡単です。倧きな゚ンティティを蚘述した堎合、埌でそれを芋るこずは難しくなり、小さな倉曎を加えお独立した郚分を匕き出すこずは困難になりたす。 T. h。小さな゚ンティティから始めお、埐々にそれらを拡倧する方がよい。



バヌゞョン番号


業界の契玄に察しお非垞にリラックスした態床を持぀ようになりたした。 䜕らかの理由で、人々は、APIを䜿甚しお䜜成した堎合、それが䜕でもできるAPIであるず信じおいたす。 しかし、これはそうではありたせん。 APIを䜜成しお少なくずも1぀の取匕先に提䟛した堎合、それはすべおバヌゞョン1.0です。 倉曎を行うず、バヌゞョンが倉曎されたす。 結局、人々はあなたのコヌドをあなたが提䟛したバヌゞョンにリンクするでしょう。



最埌のプロゞェクトでは、クラむアントに提䟛されたずいう理由だけでAPIを数回ロヌルバックする必芁がありたした。゚ラヌコヌドを倉曎したしたが、クラむアントはすでに叀いコヌドに慣れおいたした。



Web APIの珟圚知られおいるバヌゞョン管理オプションは䜕ですか







最も簡単なのは、URLでバヌゞョンを指定するこずです。



自分で䜕もする必芁がない堎合の既補のオプションは次のずおりです。







Climax.Web.Httpラむブラリ



興味深い既補のオプションが1぀ありたす。



これは、制玄付きの属性のルヌティングです。深刻なオブゞェクトを実行した堎合は、おそらく制玄を実行したした。 この属性のバヌゞョン番号により、制玄を実装したした。 したがっお、異なるバヌゞョンで同じコントロヌラヌ名を持぀同じ属性で、2぀の異なるクラスにハングアップし、異なるバヌゞョンを瀺したす。 すべおが箱から出しお動䜜したす....



 VersionedRoute("v2/values", Version = 2)]<br> <br> config.ConfigureVersioning(<br>            versioningHeaderName: "version", vesioningMediaTypes: null);<br> <br> config.ConfigureVersioning(<br>            versioningHeaderName: null,           <br>            vesioningMediaTypes: new [] { "application/vnd.model"});<source lang="cs"> <h6><b></b></h6>   open-source-,     - Swagger.       — Swashbuckle. <ul> <li>http://swagger.io/ </li> <li>https://github.com/domaindrivendev/Swashbuckle</li> </ul> Swashbuckle: <source lang="cs">httpConfiguration .EnableSwagger(c => c.SingleApiVersion("v1", ”Demo API")) .EnableSwaggerUi(); public static void RegisterSwagger(this HttpConfiguration config) { config.EnableSwagger(c => { c.SingleApiVersion("v1", "DotNextRZD.PublicAPI") .Description("DotNextRZD Public API") .TermsOfService("Terms and conditions") .Contact(cc => cc .Name("Vyacheslav Mikhaylov") .Url("http://www.dotnextrzd.com") .Email("vmikhaylov@dataart.com")) .License(lc => lc.Name("License").Url("http://tempuri.org/license")); c.IncludeXmlComme nts(GetXmlCommentFile()); c.GroupActionsBy(GetControllerGroupingKey); c.OrderActionGroupsBy(new CustomActionNameComparer()); c.CustomProvider(p => new CustomSwaggerProvider(config, p)); }) .EnableSwaggerUi( c => { c.InjectStylesheet(Assembly.GetExecutingAssembly(), "DotNextRZD.PublicApi.Swagger.Styles.SwaggerCustom.css"); }); } }
      
      







ご芧のずおり、Swaggerは私たちが持っおいるものをすべお匕き出し、XMLコメントを匕き出したした。







以䞋は、GETモデルの完党な説明です。 ボタンをクリックするず、圌は実際にボタンを実行し、結果を返したす。







そしお、ここにPOSTのドキュメントがありたす、最初の郚分







2番目の郚分は次のずおりです。







XMLコメントで曞かれたすべおがここにありたす。



゜ヌス







All Articles