Defining Web API Services and Clients with NSwag
One of the things that I liked about WSDL/SOAP services is that a WSDL contract for a Web Service could be used to generate a client proxy that was guaranteed to work with your Web Service. With a WSDL contract, it was easy for client-side developers to write code against the proxy that would handle the complexity of calling your services (it was also easy for Web Service developers to create test clients to exercise their services).
This meant that, when it came time to add business logic to either clients or services, developers on both sides could work in parallel. Both teams could be confident that the shared WSDL document would ensure there would be no API issues when the consumer was brought together with the service (there could, of course, be lots of other issues).
In a previous column I showed how adding support for the OpenAPI specifaction (through Swagger's Swashbuckle NuGet package) provides an easy way to generate documentation for your ASP.NET Web API project (and throws in a user interface that supports testing your Web API methods, just for fun). The thing is, as useful as that functionality is, it's not the most important part of OpenAPI.
What matters is the OpenAPI specification you can create to describe your service. It's that specification that drives the documentation and testing UI I talked about in the previous column. That specification also gives me what I used to get from WSDL: Automatic generation of client-side code that's guaranteed to work with my service.
However, in this column, rather than continue with Swashbuckle and the other Swagger tools, I'm going to look at part of the NSwag toolset. In part I'm doing this to illustrate one of the attractive aspects of the OpenAPI specification: The variety of tools that support it (Visual Studio has no built-in support, though).
And, just to continue the contrast with my previous column, in this column I'm going to work with an ASP.NET Core Web API project that handles Customer objects, rather than the ASP.NET 4.5 Web API project I used in the previous column.
Generating the Service's Specification
Using NSwag requires two components: NSwagStudio and the NSwag.AspNetCore NuGet package which generates the OpenAPI specification for your service. To enable generating that specification, after adding the NuGet package to your application, you need to open your project's Startup file, find the Configure method that accepts IApplication and IHostingEnvironment parameters, and add to the method code like this:
app.UseSwaggerUi(typeof(Startup).GetTypeInfo().Assembly, settings =>
You'll also need to add a couple of using directives to support this code, but IntelliSense will walk you through that.
After these changes, your service will have two new endpoints. With my service, running from within Visual Studio, the endpoint localhost:61399/swagger took me to the default UI page generated by my minimal NSwag implementation. There wasn't much on that page but what was there was very useful: the second endpoint that allows me to retrieve the OpenAPI specification describing my ASP.NET Core Web API service (http://localhost:61399/swagger/v1/swagger.json, if you're curious).
With that URL in hand, I could now start up NSwagStudio and generate a variety of clients for my service.
To generate a client, once you start NSwagStudio, just select the Documents tab on the left side of the NSwagStudio window and, in the resulting screen, select the Swagger Specification tab. On that tab, paste the URL to your service's specification into the Swagger Specification URL textbox and click the Create Local Copy button. That causes NSwagStudio to fetch the specification and save it locally (once NSwagStudio has its own copy of the specification, you can shut down your Web API service).
Now, to generate client-side code to call your service, you just need to check off what kind of code you want (TypeScript or C#) and click the Generate Code button. You might want to also take a look at the Settings tab on the code generation window which lets you tweak the output of the code generation process (I used it to control the namespace for the C# code I generated).
Altogether, it probably takes less than 10 minutes from the time you add the NSwag NuGet package to your project to having working client-side code.
To see if the code works, I created a C# WPF application, added a class called CustomerProxy to it, copied the code out of NSwagStudio's code-generation window, and pasted the C# generated code into my class (NSwagStudio is perfectly willing to put that code into files, but it seemed just as easy to me to copy and paste it). After adding the NuGet packages for NewtonSoft.Json and a reference to System.Runtime.Serialization to my WPF project, my C# client was compiling.
If nothing else, this seems like the slickest way to ensure that the Customer objects used by my client match the definition of the Customer objects in my service. But, in addition, the code to call my service was now almost trivially simple. All I needed was this (and it worked the first time!):
CustomerProxy cp = new CustomerProxy();
ObservableCollection<Customer> allCusts = await cp.GetAllAsync();
Futures and Opportunities
It's probably possible, from the OpenAPI specification document, to generate a skeleton Web service. In addition to generating client-side code NSwagStudio will also generate a C# version of your service, targeting ASP.NET 4.5.
That suggests the specification could also be used to communicate design decisions from architects/designers to service developers. However, the specification syntax is sufficiently complex that writing one would be ... challenging. Even if someone were to create an editor that simplified creating an OpenAPI specification, I suspect it would still be easier just to write the appropriate ASP.NET controllers and let NSwag (or Swashbuckle) generate the specification for you.
It's a shame that JSON Schema (which describes messages) and the OpenAPI (which describes services) don't work together (while they say they do, they don't really). There is a proposal for including schemas (including JSON Schema) in OpenAPI that may fix this but, right now, it is just a proposal.
But, quite frankly, if I have to generate the JSON Schemas I use for testing and message validation in a separate process, it's not the end of the world. More importantly, if I can ship to my customers the TypeScript or C# code guaranteed to call my service then that's pretty cool.
Besides, if my clients think I wrote that client-side code myself ... well, there's nothing wrong with that, is there?
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.