Ontwikkel je een API? Voeg dan Swagger toe.

Ingediend door Dirk Hornstra op 18-sep-2019 21:55

Een tijdje geleden heeft mijn collega Jeldert Pol een demo gegeven hoe hij bij een API-project Swagger toegevoegd heeft. Door deze module toe te voegen krijg je een interface/pagina's bij je project waar je kunt zien welke functies beschikbaar zijn, wat de waardes zijn die je mee moet sturen en wat je aan data terug kunt verwachten. Ideaal omdat we als "back-enders" de API bouwen en de "front-enders" deze gebruiken. Dus nu kan iedereen zien hoe het gebruikt kan worden en hoef je dus niet naast het ontwikkelen ook nog eens uitleg te geven hoe het werkt. 

Mijn "hobby"-project

Voor een eigen project ben ik nu bezig een API (voor eigen gebruik) te maken. Want ik bezoek regelmatig concerten. Maar ik hou niet altijd bij wanneer een artiest in Nederland optreedt. Vaak kom je er dan (te) laat achter, waardoor de kaartjes met een "redelijke" prijs al verkocht zijn en je voor een kaartje een platinum of andere versie met een hoger prijskaartje moet betalen. Voorbeeld? Andrea Bocelli komt naar Nederland, ik spotte het bericht ergens op Facebook, paar dagen later naar de site en toen kon ik (met moeite) nog een normaal kaartje vinden (84 euro inclusief servicekosten).

Ik heb al eens in kaart gebracht op welke sites ik de agenda's en evenementen kan vinden, mijn neef Jelmer heeft toen voor me de xpath-configuraties uitgewerkt en vervolgens heb ik er niets meer mee gedaan. Te druk met andere dingen bezig, studie, etc. Ik vind dat het tijd wordt om er nu wat mee te gaan doen, dus heb een project opgezet waarbij de sites gescraped worden en het als een RSS-feed opgeleverd wordt. Die ga ik dan naar een andere site sturen waar ik de evenementen ga tonen.

Voor het bovenstaande, als je niet weet wat xpath is, stel dat er in een deel van een pagina dit staat:


<div class="row">
<div class="eventdate">12 oktober 2019</div>
<div class="eventtitle">optreden Moai Wark</div>
</div>
<div class="row">
<div class="eventdate">5 november 2019</div>
<div class="eventtitle">optreden 2Unlimited</div>
</div>
 

Met een stukje xPath kun je door alle evenementen lopen en zo je eigen feed opbouwen:

var items = navigator.Select("//div[contains(@class,\"row\")]");
while (items.MoveNext())
{
// ...
}

Swagger-inrichting

Maar goed, in een latere post ga ik dat project nog wel verder toelichten. Even terug naar Swagger. Je moet een aantal handelingen uitvoeren om dat werkend te krijgen. Mijn project is een C#-project, dit zijn dus de stappen die daarvoor nodig zijn. Als ik Google op Swagger en PHP krijg ik meteen een hit op Github, dus als je een ander type project hebt zul je het vast ook werkend kunnen krijgen.

Mijn project is een .NET Framework-project, geen .NET Core. Mocht ik daar nog mee bezig gaan en dit gebruiken, dan zal ik dit in een eigen blog-item toevoegen.

Allereerst de officiĆ«le site, dat is: https://swagger.io/

Ik heb een nieuw API project gemaakt. Klik in de Solution Explorer met de rechtermuisknop op het project en kies dan voor Manage Nuget Packages... Ik heb de volgende Nuget Packages toegevoegd:
 

  • NSwag.Annotations van Rico Suter
  • NSwag.AspNet.Owin van Rico Suter
  • NSwag.Core van Rico Suter
  • NSwag.Generation van Rico Suter
  • NSwag.Generation.WebApi van Rico Suter
  • NSwag.SwaggerGeneration van Rico Suter
  • NSwag.SwaggerGeneration.WebApi van Rico Suter


Dat zijn de pakketten. Vervolgens moet je in je code een paar zaken instellen;
Je hebt als het goed is een Global.asax (en Global.asax.cs) bestand in je project. Zo niet: dan toevoegen. Hier moet je onderstaande code in plaatsen:


    public class Global: System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            RouteTable.Routes.MapOwinPath("swagger", app =>
            {
                app.UseSwaggerUi3(typeof(Global).Assembly, settings => {
                    settings.MiddlewareBasePath = "/swagger";
                    settings.DocumentPath = "/swagger/v3/swagger.json";
                    settings.GeneratorSettings.DefaultUrlTemplate = "api/{controller}/{action}/{id}";
                    settings.PostProcess = (document) =>
                    {
                        document.Info.Version = "v3";
                        document.Info.Title = "Mijn API";
                        document.Info.Description = "Korte omschrijving van de API";
                        document.Info.Contact = new NSwag.OpenApiContact
                        {
                            Name = "mijn naam",
                            Url = "url van mijn website",
                            Email = "mijn e-mailadres"
                        };
                    };
                });
            });
// .....
    }

Ik heb hierboven even Global vetgedrukt, omdat in mijn geval de class een andere naam had (WebApi). Dan moet je het ook op de andere plaats die naam geven (WebApi dus).

Ik dacht dat ik er was, maar toen ik de URL /swagger in de browser aanroep werd de pagina niet gevonden. Je moet het volgende in de web.config toevoegen om het werkend te krijgen:


  <system.webServer>
    <handlers>
      <add name="NSwag" path="swagger" verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<!-- overige handlers -->
    </handlers>
<!-- overige zaken -->
  </system.webServer>

En ik zou nog bijna vergeten dat ik een "owin automatic start-up foutmelding" op mijn scherm kreeg. Als je onderstaande in je appsettings toevoegt is dat ook verleden tijd:


  <appSettings>
    <add key="owin:AutomaticAppStartup" value="false" />
<!-- andere settings -->
  </appSettings>

Hiermee zou je een werkende versie moeten hebben. In mijn geval is het een eigen API waar ik zelf toegang tot wil hebben, maar anderen er niet bij hoeven kunnen komen. Hiervoor heb ik in de web.config de volgende ip-restrictie toegevoegd:


  <system.webServer>
<!-- overige zaken -->
      <rewrite>
          <rules>
              <rule name="swagger ipblock" stopProcessing="true">
                  <match url="swagger(.*)"/>
                  <conditions>
                      <add input="{REMOTE_ADDR}" pattern="mijn IP adres" negate="true" />
                  </conditions>
                  <action type="CustomResponse" statusCode="403"/>
              </rule>
          </rules>
      </rewrite>
  </system.webServer>