Automatische Dokumentation Perfect Server

  • Tutorial
Bild
Das letzte Mal haben wir gesagt, dass Perfect eine implementierte API nicht automatisch dokumentiert. Es ist möglich, dass die Entwickler in der nächsten Implementierung dieses ärgerliche Versäumnis beheben. Aber nichts hindert uns daran, uns selbst darum zu kümmern. Glücklicherweise müssen Sie ziemlich viel Code hinzufügen.

Wir haben bereits einige Stubs, mit denen Sie die vom Server unterstützten Befehle anzeigen können. Versuchen wir, die Möglichkeiten dieses Ansatzes zu erweitern.

Schritt 1 : Führen Sie den zuvor erstellten Perfect-Server aus und geben Sie den Befehl / cars ein, um JSON abzurufen. Dieser JSON wird nach jsonschema.net/# kopiert.und bilden Sie daraus ein Diagramm, das wir dem Projekt als cars.json-Datei hinzufügen. Vergessen Sie nicht, zu Xcode -> Projekt -> Erstellungsphase zu gehen und die erstellte Datei auf die gleiche Weise wie bei index.html zur Liste "Dateien kopieren" hinzuzufügen
cars.json

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "address": {
      "type": "object",
      "properties": {
        "streetAddress": {
          "type": "string"
        },
        "city": {
          "type": "string"
        }
      },
      "required": [
        "streetAddress",
        "city"
      ]
    },
    "phoneNumber": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string"
          },
          "code": {
            "type": "integer"
          }
        },
        "required": [
          "location",
          "code"
        ]
      }
    }
  },
  "required": [
    "address",
    "phoneNumber"
  ]
}


Dies ist nicht unbedingt erforderlich, aber es ist immer besser, die Möglichkeit zu geben, das JSON-Antwortschema abzurufen. Client-Anwendungsentwickler werden dankbar sein.

Schritt 2 : IRestHelp-Schnittstelle hinzufügen
IRestHelp.swift

import Foundation
protocol IRestHelp
{
    var details:String  {get}
    var params :String  {get}
    var schema :String  {get}
}



Schritt 3 : Fügen Sie die RestApi-Klasse hinzu
RestApi.swift

import PerfectLib
class RestApi
{
    var prefix:String?
    var commands:[String]? = nil
    var handler:RequestHandler?
    init(prefix:String?=nil, commands:[String]? = nil, handler:RequestHandler?=nil)
    {
        self.prefix   = prefix
        self.commands = commands
        self.handler  = handler
    }
}


Wofür es gebraucht wird - es wird weiter deutlich.

Schritt 4 : Fügen Sie die RestApiReg-Klasse hinzu
RestApiReg.swift

import Foundation
import PerfectLib
class RestApiReg
{
    typealias APIList = [RestApi]
    // MARK: - Public Properties
    private var commandList = APIList()
    // MARK: - Private Properties
    private var globalRegistered = false
    // MARK: - Singletone Implementation
    private init() {
    }
    private class var sharedInstance: RestApiReg
    {
        struct Static {
            static var instance: RestApiReg?
            static var token: dispatch_once_t = 0
        }
        dispatch_once(&Static.token) {
            Static.instance = RestApiReg()
        }
        return Static.instance!
    }
    // MARK: - Methods of class
    class func registration(list:APIList)
    {
       self.sharedInstance.commandList = list
       self.sharedInstance.linkAll()
    }
    class func add(command:RestApi)
    {
        self.sharedInstance.commandList += [command]
        self.sharedInstance.add(command)
    }
    class var list: APIList {
        return self.sharedInstance.commandList
    }
    // MARK: - Private methods
    private func linkAll()
    {
        Routing.Handler.registerGlobally()
        self.globalRegistered = true
        for api in self.commandList {
            self.add(api)
        }
    }
    private func add(api:RestApi)
    {
        if !self.globalRegistered {
           Routing.Handler.registerGlobally()
        }
        if let handler = api.handler
        {
            let prefix = api.prefix == nil ? "*" : api.prefix!
            if let commands = api.commands {
                Routing.Routes[prefix, commands] = { (_:WebResponse) in handler }
            } else {
                Routing.Routes[prefix] = { (_:WebResponse) in handler }
            }
        }
    }
}


Ich konnte mir keinen besseren Namen für diese Klasse ausdenken. Die Klasse vermittelt die Registrierung neuer Server-APIs.

Schritt 5 : Ersetzen Sie die HelpHandler-Klasse durch den folgenden Code:
HelpHandler.swift

import Foundation
import PerfectLib
class HelpHandler:RequestHandler, IRestHelp
{
    var details = "Show server comands list"
    var params  = ""
    var schema  = ""
    func handleRequest(request: WebRequest, response: WebResponse)
    {
        let list = self.createList()
        let html = ContentPage(title:"HELP", body:list).page(request.documentRoot)
        response.appendBodyString("\(html)")
        response.requestCompletedCallback()
    }
    private func createList() -> String
    {
        let list = RestApiReg.list
        var code = ""
        let allPrefixes = list.map { (api) -> String in
            api.prefix != nil ? api.prefix! : "/*"
        }
        let groups = Set(allPrefixes).sort()
        for group in groups
        {
            let commandsApi = self.commandsByGroup(group, list:list)
            code           += self.titleOfGroup(group)
            code           += self.tableWithCommnads(commandsApi)
        }
        return code
    }
    private func commandsByGroup(group:String, list:RestApiReg.APIList) -> [String:RestApi]
    {
        var dict = [String:RestApi]()
        let commandsOfGroup = list.filter({ (api) -> Bool in
            api.prefix == group
        })
        for api in commandsOfGroup {
            if let commands = api.commands {
                for cmd in commands {
                    dict[cmd] = api
                }
            } else {
                dict[""] = api
            }
        }
        return dict
    }
    private func titleOfGroup(group:String) -> String {
        return "
\(group):
"
    }
    private func tableWithCommnads(commands:[String:RestApi]) -> String {
        let sortedList = commands.keys.sort()
        var table = ""
        table += ""
        for name in sortedList
        {
            let cmd = commands[name]!
            table += ""
            table += ""
            if let help = cmd.handler as? IRestHelp
            {
                table += ""
                table += ""
                table += help.schema.characters.count > 0 ? "" : ""
            } else
            {
                table += ""
                table += ""
                table += ""
            }
             table += ""
        }
        table += "
\(name)\(help.details)\(help.params)/\(help.schema)
" return table } }



Schritt 6 : Fügen Sie die Implementierung des IRestHelp-Protokolls zum Handler jedes Befehls hinzu, der über eine automatische Dokumentation verfügen soll. Dieser Schritt ist optional. Diejenigen Teams, die das Protokoll nicht unterstützen, haben leere Werte in den entsprechenden Feldern. Der Befehlshandler / list (Klasse CarsJson) sieht für mich beispielsweise so aus:
CarsJson.swift

import Foundation
import PerfectLib
class CarsJson:RequestHandler, IRestHelp
{
    var details = "Show complexly JSON object"
    var params  = "{}"
    var schema  = "cars.json"
    func handleRequest(request: WebRequest, response: WebResponse)
    {
        let car1:[JSONKey: AnyObject] = ["Wheel":4, "Color":"Black"]
        let car2:[JSONKey: AnyObject] = ["Wheel":3, "Color":["mixColor":0xf2f2f2]]
        let cars                      = [car1, car2]
        let restResponse              = RESTResponse(data:cars)
        response.appendBodyBytes(restResponse.array)
        response.requestCompletedCallback()
    }
}



Schritt 7 : Ersetzen Sie die PerfectServerModuleInit () -Methode durch den neuen Code:
PerfectServerModuleInit ()

public func PerfectServerModuleInit()
{
    RestApiReg.add(RestApi(handler: StaticFileHandler()))
    RestApiReg.add(RestApi(prefix: "GET",  commands: ["/dynamic"],        handler: StaticPageHandler(staticPage: "index.mustache")))
    RestApiReg.add(RestApi(prefix: "GET",  commands: ["/index", "/list"], handler: StaticPageHandler(staticPage: "index.html")))
    RestApiReg.add(RestApi(prefix: "GET",  commands: ["/hello"],          handler: HelloHandler()))
    RestApiReg.add(RestApi(prefix: "GET",  commands: ["/help"],           handler: HelpHandler()))
    RestApiReg.add(RestApi(prefix: "GET",  commands: ["/cars", "/car"],   handler: CarsJson()))
    RestApiReg.add(RestApi(prefix: "POST", commands: ["/list"],           handler: CarsJson()))
}



Starten Sie!

Die ursprüngliche Seite ist dieselbe geblieben.
Wir versuchen, / help über die Browser-Befehlszeile einzugeben:
Bild
Wir sehen, dass alle Befehle in alphabetischer Reihenfolge in einer Tabelle angeordnet sind und Hyperlinks haben. Nach dem Aufrufen der Hilfeseite müssen Sie nicht jeden Befehl in die Befehlszeile des Browsers eingeben, um sie auszuführen. Und in der äußersten rechten Spalte befindet sich ein Link zur Schaltung zur Validierung dieses Befehls.
In Zukunft können wir selbst das Validierungsschema verwenden, um die Richtigkeit der von uns erstellten Antwort zu überprüfen, bevor sie an die Clientanwendung weitergeleitet wird. Die Clientanwendung kann möglicherweise Validierungsschemata direkt vom Server herunterladen. Mit der Validierung wird somit ein doppelter Gewinn erzielt.

Der Tisch ist natürlich ungeschickt. Die Verwendung von CSS kann das ästhetische Erscheinungsbild erheblich verbessern. Aber für die Arbeit ist das in der Regel genug.
Anfänglich bestand der Wunsch, bei der Anforderung / Hilfe eine XML-Datei mit einem Schema anzuzeigen, das die Daten in einer ähnlichen Tabelle anordnet. Das Verbessern des Erscheinungsbilds von HTML macht jedoch weitaus mehr Spaß als das Verwenden aller Arten von XML-Zuordnungen.

PS Wie bekannt wurde, arbeiten die Entwickler von Perfect intensiv daran, das umfangreiche Erbe von NextStep (Objective-C) loszuwerden, damit der Server auf einem * nix-System ausgeführt werden kann. Daher gelten einige der bekannten Arbeitsmethoden im NS-Namespace als nicht koscher.

Jetzt auch beliebt: