TypeScript、Swashbuckle、およびAutoRestを䜿甚した完党な入力に向けお

はじめに



この蚘事では、ASP.NET Web APIに基づくバック゚ンドずTypeScriptを䜿甚しお䜜成されたフロント゚ンドずの間で型付きメッセヌゞングを実装する方法に぀いお説明したす。 これは、倧芏暡プロゞェクトで䜜業する堎合に特に重芁であり、チヌムが分散しおいる堎合はさらに重芁です。 たずえば、バック゚ンド開発者ずフロント゚ンド開発者が異なるタむムゟヌンの異なる堎所で働いおいる堎合、垞に䜕かに連絡したり話し合ったりする機䌚はありたせん。 この堎合、倉曎の远跡は骚の折れる䜜業であり、倚くの埮劙な゚ラヌを䌎う可胜性がありたす。



この蚘事の著者にずっお、WPFずSilverlightによるフロント゚ンドの開発に携わった人にずっお、倧きな問題は静的型付けの欠劂でした。 「2」ず「2」を远加する代わりに、「2」ず「2を返す関数」を远加したか、jQueryラッパヌの代わりにDOMオブゞェクトを枡した回数。 JSLintなどの静的コヌドアナラむザヌの出珟により、問題は倚少緩和されたしたが、TypeScriptは、特にチヌム開発においお真のブレヌクスルヌずなりたした。









問題の本質



TypeScriptは静的型付けを可胜にする蚀語ですが、「錯芚」ず呌ばれるものもありたす habrahabr.ru/post/258957、habrahabr.ru/post/272055 。 䞍思議なこずに、批評家は䞀般的に安党でないシナリオずしおバック゚ンドで䜜業するこずを匷調しおいたす。

ただし、問題の本質は、以前および珟圚のTypeScriptでJavaScriptでフロント゚ンドアプリケヌションを蚘述する堎合、WCFにあったようなメタデヌタおよびクラむアントコヌドの自動生成を操䜜するためのツヌルが存圚しないずいう事実にありたす。



メタデヌタ



WPF + WCFの経隓に目を向けるず、この点ですべおが非垞に良奜です。 もちろん、䞀般的に蚀えば、デヌタは垞に型付けされおいない圢匏で送信されたすが、送信されるず、デヌタはほが最埌たで型付けされたたたで、反察偎に送信される盎前にのみ文字列たたはバむナリストリヌムにシリアル化されたす。 反察偎では、再び、圌らはタむプされたものにそれらを回す特定のクラむアントになりたす。 このようなクラむアントの手で曞かないで、耇数の゚ラヌをキャッチしようずしないために、メタデヌタが存圚したす。 .NETの䞖界では、90のケヌスで、それらを生成したり凊理したりする䜜業はたったく必芁ありたせん。 サヌビスを曞くだけで、適切な゚ンドポむントを远加するこずを忘れずに、自動生成されたメタデヌタを取埗したす。 たた、ワンクリックでクラむアントを生成し、その結果、入力されたメッセヌゞを亀換できたす。



JavaScript / TypeScriptで単䞀ペヌゞアプリケヌションを開発する堎合、WCFはWeb APIに眮き換えられたす。 か぀おは、Web APIのメタデヌタをすぐに生成する方法がなかった理由が倚少驚きでしたヘルプペヌゞのメタデヌタを考慮しない。 どうやらその答えは、Web APIからのデヌタの䞻な受信者はJavaScriptコヌドであり、入力は意味をなさないずいうこずです。 ただし、珟圚はJavaScriptではなくTypeScriptがあり、型付けされたデヌタを操䜜したいずいう芁望が再び関連しおいたす。



珟圚、非垞に人気のあるメタデヌタ圢匏はOpenAPI / Swaggerです。 圓然のこずながら、この圢匏でメタデヌタずドキュメントを生成する機䌚がありたす。



次に、型付き盞互䜜甚を敎理するプロセスを瀺したす。

぀たり、次の手順に埓いたす。



  1. Swashbuckleラむブラリを接続しお構成する
  2. ドキュメント/メタデヌタを生成する
  3. 生成されたファむルをバヌゞョン管理システムに保存するこずの利䟿性を怜蚌したす
  4. AutoRestを接続する
  5. クラむアントモデルを生成したす
  6. ビゞネスでテストしたす。




スワッシュバックル



github.com/domaindrivendev/Swashbuckle



たず、メタデヌタを生成したす。

そのため、Web APIがあり、その䞭に埓業員ずの䜜業を担圓するコントロヌラヌがあるずしたす。

/// <summary> /// Gets all employees. /// </summary> /// <remarks> /// Gets the list of all employees. /// </remarks> /// <returns> /// The list of employees. /// </returns> [Route("api/employees")] [HttpGet] public Employee[] GetEmployees() { return new[] { new Employee { Id = 1, Name = "John Doe" }, new Employee { Id = 2, Name = "Jane Doe" } }; }
      
      





ご芧のずおり、Employee型の型付きオブゞェクトの配列が返されたす。 プロゞェクトを開始するず、埓業員のリストをリク゚ストできたす。

http// localhost1234 / api / employees



ここでSwashbuckleラむブラリを有効にしたしょう。 NuGetには2぀のSwashbuckle.CoreおよびSwashbuckleパッケヌゞがありたす。 2぀の違いは、1぀目はカヌネルであり、マゞックを実行するすべおのコヌドが含たれおいるこずです。2぀目は、カヌネルを構成するブヌトストラップをむンストヌルする単なるアドオンです。



これはgithub.com/domaindrivendev/Swashbuckle#iis-hosted documentationに曞かれおいたす



Coreをむンストヌルし、独自の構成コヌドを蚘述するこずをお勧めしたす。 それを再利甚する方が䟿利です。



むンストヌルしたしょう

 PM> Install-Package Swashbuckle.Core
      
      





WebActivatorExに登録する

 [assembly: WebActivatorEx.PreApplicationStartMethod(typeof(FullyTypedExample.WebApi.SwaggerConfig), "RegisterGlobal")]
      
      





たた、構成コヌドを曞きたす

 /// <summary> /// Configures Swagger. /// </summary> /// <param name="config"> /// The Swagger configuration. /// </param> public static void ConfigureSwagger(SwaggerDocsConfig config) { config.SingleApiVersion("v1", "FullyTypedExample.WebApi"); config.IncludeXmlComments(GetXmlCommentsPathForControllers()); config.IncludeXmlComments(GetXmlCommentsPathForModels()); config.GroupActionsBy(apiDescription => apiDescription.ActionDescriptor.ControllerDescriptor.ControllerName); config.OrderActionGroupsBy(Comparer<string>.Default); config.PrettyPrint(); }
      
      





ここではすべおが非垞に簡単です。最初に、APIのバヌゞョンずタむトルを蚭定したす。 次に、コントロヌラヌずモデルのxmlコメントを有効にする必芁があるず蚀いたす。 swagger-document内のアクションの順序ずグルヌプ化を調敎したす。 PrettyPrintオプションに぀いおも蚀及したいず思いたす。 SwaggerドキュメントのJSONフォヌマットを有効にしたす。 このオプションは、将来バヌゞョン管理システムにドキュメントを保存し、差分ビュヌアヌを䜿甚しおその倉曎を簡単に衚瀺するために圹立ちたす。



これで、プロゞェクトを開始しおSwaggerむンタヌフェヌスを確認できたす。

http// localhost1234 / swagger







近くで、Swaggerドキュメント自䜓をJSON圢匏で芋るこずができたす。

http// localhost1234 / swagger / docs / v1



ここで、生成されたドキュメントをバヌゞョン管理システムに远加する必芁がありたす。 Swashbuckleは内郚でMicrosoft IApiExplorerを䜿甚しおいるため、swaggerファむルを生成するには、Web APIを実行する必芁がありたすこれに぀いおはこちらgithub.com/domaindrivendev/Swashbuckle/issues/559 。 ぀たり、ドキュメントを生成するたびに、Web APIを起動し、手動でswagger / docsをファむルにコピヌする必芁がありたす。 もちろん、もっず自動化されたものが欲しいです。



自己ホスト型アプリケヌションずしおWeb APIを起動し、非゚ンドポむントのswaggerリク゚ストを送信し、応答をファむルに曞き蟌むこずでこれを解決したした。 Swashbuckle構成コヌドを再利甚するず䟿利でした。 すべお次のようになりたす。

 /// <summary> /// Generate Swagger JSON document. /// </summary> /// <param name="filePath"> /// The file path where to write the generated document. /// </param> private static void GenerateSwaggerJson(string filePath) { // Start OWIN host using (TestServer server = TestServer.Create<WebApiHostStartup>()) { HttpResponseMessage response = server.CreateRequest("/swagger/docs/v1").GetAsync().Result; string result = response.Content.ReadAsStringAsync().Result; string path = Path.GetFullPath(filePath); File.WriteAllText(path, result); } }
      
      





すべおを実行しおみたしょう。

 nuget.exe restore "..\FullyTypedExample.sln" "C:\Program Files (x86)\MSBuild\12.0\bin\MSBuild.exe" "..\FullyTypedExample.WebApi.SelfHosted\FullyTypedExample.WebApi.SelfHosted.proj" /v:minimal "..\FullyTypedExample.WebApi.SelfHosted\bin\Debug\FullyTypedExample.WebApi.SelfHosted.exe" --swagger "swagger.json"
      
      





合蚈で、JSONファむルの圢匏のswaggerドキュメントを受け取り、バヌゞョン管理システムに入れたした。 分散チヌムのフロント゚ンド開発者は、゚ンドポむントの倉曎を簡単に远跡できるようになりたした。 それがどのように芋えるか芋おみたしょう。



埓業員を識別子で取埗するための新しいアクションを远加したずしたしょう。

 /// <summary> /// Gets employee by id. /// </summary> /// <param name="employeeId"> /// The employee id. /// </param> /// <remarks> /// Gets the employee by specified id. /// </remarks> /// <returns> /// The <see cref="Employee"/>. /// </returns> [Route("api/employees/{employeeId:int}")] public Employee GetEmployeeById(int employeeId) { return this.GetEmployees().SingleOrDefault(x => x.Id == employeeId); }
      
      





そしお、swagger.jsonを再生成したした。 䜕が倉わったのか芋おみたしょう





ご芧のずおり、このアクションに関するドキュメントが衚瀺されおおり、diffビュヌアヌを䜿甚しお簡単に確認できたす。 PrettyPrintオプションのおかげで、フォヌマットされおいお読みやすくなっおいたす。



AutoRest



これで、タスクの最初の郚分は完了したした。メタデヌタがありたす。 クラむアントパヌツ、぀たり クラむアント偎のデヌタの皮類サヌバヌから受信



Web APIをリク゚ストするためのコヌド自䜓を生成できるこずを蚀わなければなりたせん。それはもう少し耇雑で、コヌドゞェネレヌタヌを構成したり、独自のコヌドを蚘述したりするのに時間がかかる䜜業です。 たた、䜿甚するラむブラリjQuery、SuperAgent、たたは新しい実隓的なFetch API developer.mozilla.org/en/docs/Web/API/Fetch_APIなど ずアプロヌチPromise、Rxなどに倧きく䟝存したすクラむアントコヌド。



コヌド生成には、次のオプションがありたす。



1. Swagger Code Generator github.com/swagger-api/swagger-codegen

Javaで蚘述されたSwaggerチヌムの公匏ツヌルであり、適切なむンフラストラクチャが必芁です。 Dockerでも実行できたす。 確かに、JavaScriptの生成、特にTypeScriptは欠萜しおいたす。 ただし、たずえばJavaでコヌドを生成する必芁がある堎合は、これがあなたの遞択です。 圌は明らかな理由で私たちに合わなかった。



2. Swagger JSラむブラリ github.com/swagger-api/swagger-js

Swaggerチヌムの公匏ツヌルでもありたす。 すでに暖かい。 JSで蚘述され、それに応じおJSコヌドを生成したす。 npmたたはbowerを介しおむンストヌルされたす。 むンフラストラクチャは私たちに適しおいたすが、残念ながら、このタむプの䞖代は存圚したせん。



3. JSおよびTypescript Codegenに Swaggerする github.com/wcandillon/swagger-js-codegen

プロゞェクトは、このアプロヌチの開発を始めた少し埌に公開されたした。 おそらく近い将来、これが最適な゜リュヌションになるでしょう。



4. 自転車コヌドゞェネレヌタヌを䜜成したす。 党䜓ずしお、なぜですか しかし、たず最初に、AutoRestを詊しおみるこずにしたした。うたくいかない堎合、たたは機胜に合わない堎合は、ブラックゞャックを䜿っお独自に䜜成したす。



5. AutoRest github.com/Azure/autorest

最埌に、Azure MicrosoftチヌムのAutoRest。 珟圚のバヌゞョンは0.15.0であり、率盎に蚀っお、完党なリリヌスであるず芋なされるかどうかは明確ではありたせんが、前のものず同様、Preマヌクは芳察されたせん。 䞀般に、ここではすべおが簡単で、必芁な.d.tsファむルをむンストヌルしおすぐに生成したした。



それでは、このツヌルを䜿甚しお私たちの旅の最終段階を芋おいきたしょう。



AutoRestはNuGetを介しお接続したす。

 PM> Install-Package AutoRest
      
      





パッケヌゞは特定のプロゞェクトには配眮されず、゜リュヌションぞのリンクが远加されたす。

 <?xml version="1.0" encoding="utf-8"?> <packages> <package id="AutoRest" version="0.15.0" /> </packages>
      
      





パッケヌゞには、実際に生成を実行するコン゜ヌルアプリケヌションAutoRest.exeがありたす。 実行するには、次のスクリプトを䜿甚したす

 nuget.exe restore "..\FullyTypedExample.sln" "..\packages\AutoRest.0.15.0\tools\AutoRest.exe" -Input "swagger.json" -CodeGenerator NodeJS move "Generated\models\index.d.ts" "..\FullyTypedExample.HtmlApp\models.d.ts"
      
      





入力では、以前に生成されたswagger.jsonを送信し、出力では、models \ index.d.ts-モデルを含むファむルを取埗したす。 クラむアントプロゞェクトにコピヌしたす。



これで、TypeScriptには次のモデル蚘述がありたす。

 /** * @class * Initializes a new instance of the Employee class. * @constructor * Represents the employee. * @member {number} id Gets or sets the employee identifier. * * @member {string} name Gets or sets the employee name. * */ export interface Employee { id: number; name: string; }
      
      





実際に詊しおみたしょう。

 public makeRequest() { this.repository.getEmployees() .then((employees) => { // Generate html using tempalte string this.table.innerHTML = employees.reduce<string>((acc, x) => { return `${acc}<tr><td>${x.id}</td><td>${x.name}</td></tr>`; }, ''); }); }
      
      





ここでは、idおよびnameモデルフィヌルドを参照したす。 サヌバヌぞのリク゚ストの実装を意図的に䞋げたした。 既に述べたように、遞択したラむブラリずアプロヌチに䟝存する可胜性がありたす。



存圚しない幎霢フィヌルドにアクセスしようずするず、TSコヌドはコンパむルされたせん。 以前にアクセスしたフィヌルドがAPIで消えた堎合、コヌドは再床コンパむルされたせん。 新しいフィヌルドが远加されるず、同じ差分を䜿甚しおすぐに衚瀺されたす。 さらに、メタデヌタに基づいおJSDocドキュメントを自動的に受け取りたす。 䞀般に、静的型付けの魅力はすべお明癜です。



ResponseType



興味深いこずに、必芁に応じお、ドキュメント化のために、返されるものずは異なるタむプを指定できたす。 たずえば、これは、型指定されおいないDataSetで動䜜するレガシヌコヌドがある堎合に圹立ちたす。 コントロヌラヌからIHttpActionResultを返す堎合。 メ゜ッドの実装に圱響を䞎えるこずなく、ResponseType属性でそれらをマヌクし、特別な型を開発できたす



 /// <summary> /// Gets all departments. /// </summary> /// <remarks> /// Gets the list of all departments. /// </remarks> /// <returns> /// The list of departments. /// </returns> [Route("api/departments")] [HttpGet] [ResponseType(typeof(DepartmentsResponse))] public DataSet GetDepartments() { var dataTable = new DataTable("Departments"); dataTable.Columns.Add("Id", typeof(int)); dataTable.Columns.Add("Name", typeof(string)); dataTable.Rows.Add(1, "IT"); dataTable.Rows.Add(2, "Sales"); var dataSet = new DataSet(); dataSet.Tables.Add(dataTable); return dataSet; }
      
      





クラむアント偎の型付きモデルを取埗する

 /** * @class * Initializes a new instance of the Department class. * @constructor * Represents the department. * @member {number} id Gets or sets the department identifier. * * @member {string} name Gets or sets the department name. * */ export interface Department { id: number; name: string; }
      
      







問題



たず、models.d.tsファむルのサむズは時間ずずもに増加したす。 いく぀かのサブファむルに分割するこずはただ扱っおいたせんが、間違いなくこれを行う必芁がありたす。



たた、アンダヌスコアが䜿甚されおいる堎合など、非暙準衚蚘が䜿甚されおいる堎合、フィヌルド名の䞍正な生成に問題がある可胜性がありたす。 CコヌドのLAST_NAMEフィヌルドは、SwaggerのlagT_NAMEで生成され、TypeScrptのlasTNAMEずしお生成されたす。



 /// <summary> /// Gets or sets the last name. /// </summary> [Required] // ReSharper disable once InconsistentNaming public string LAST_NAME { get; set; }
      
      





 "lasT_NAME": { "description": "Gets or sets the last name.", "type": "string" }
      
      





 export interface Employee { id: number; name: string; firstName: string; lasTNAME: string; }
      
      





マむナヌな問題のほずんどは、構成を䜿甚しお簡単に解決でき、個別に蚀及する䟡倀はありたせん。



おわりに



このアプロヌチにより、型付きメッセヌゞ亀換を敎理するこずができたした。 同時に、圌はクラむアントモデルのタむピングを提䟛し、クラむアントコヌドずサヌバヌコヌドの䞍䞀臎の可胜性を枛らし、APIずモデルの倉曎の远跡を容易にしたした。 玠晎らしいボヌナスは、RESTクラむアントが組み蟌たれたAPIの䟿利な手動テストず、スキヌムに埓っおオンザフラむでペむロヌドを生成する機胜でした。 このアプロヌチを䜿甚するず、バック゚ンド開発者ずフロント゚ンド開発者の盞互䜜甚を改善するのにも圹立ちたした。



実甚的な䟋をここで芋るこずができたす。

github.com/EBTRussia/fully-typed-example



All Articles