19. QGIS Server en Python

19.1. Introductie

Lees de QGIS Server Gebruikershandleiding om meer te leren over QGIS Server.

QGIS Server is drie verschillende dingen:

  1. QGIS Server bibliotheek: een bibliotheek die een API verschaft voor het maken van webservices voor OGC

  2. QGIS Server FCGI: een FCGI binaire toepassing qgis_maserv.fcgi die, samen met een webserver, een set services van OGC implementeert (WMS, WFS, WCS etc.) en API’s voor OGC (WFS3/OAPIF)

  3. QGIS Development Server: een binaire toepassing voor een ontwikkelingsserver qgis_mapserver een set services van OGC implementeert (WMS, WFS, WCS etc.) en API’s voor OGC (WFS3/OAPIF)

Dit hoofdstuk van het kookboek focust op het eerste onderwerp en door het gebruik van de API van QGIS Server uit te leggen, lat het zien hoe het mogelijk is Python te gebruiken om het gedrag van de server uit te breiden, te verbeteren of aan te passen of hoe de API van QGIS Server te gebruiken om QGIS server in een andere toepassing in te bedden.

Er zijn een aantal verschillende manieren waarmee u het gedrag van QGIS Server kunt wijzigen of de mogelijkheden ervan kan uitbreiden om nieuwe aangepaste services of API’s aan te bieden. Dit zijn de belangrijkste scenario’s die u zou kunnen tegenkomen:

  • INBEDDEN → API van QGIS Server gebruiken vanuit een andere toepassing

  • ZELFSTANDIG → QGIS Server uitvoeren als een zelfstandige service voor WSGI/HTTP

  • FILTERS → QGIS Server verbeteren/aanpassen met plug-ins voor filters

  • SERVICES → Een nieuwe SERVICE toevoegen

  • OGC APIs → Een nieuwe OGC API toevoegen

Ingebedde en zelfstandige toepassingen vereisen het gebruiken van de Python API voor QGIS Server direct vanuit een ander script of toepassing voor Python. De resterende opties zijn beter geschikt voor wanneer u aangepaste mogelijkheden wilt toevoegen aan een standaard QGIS Server binaire toepassing (FCGI of ontwikkelingsserver): in dit geval dient u een plug-in voor Python voor de toepassing van de server te schrijven en uw aangepaste filters, services of API’s te registreren.

19.2. Basisbeginselen Server API

De betrokken fundamentele klassen voor een typische toepassing van QGIS Server zijn:

De werkstroom voor een QGIS Server FCGI of ontwikkelingsserver kan als volgt worden samengevat:

1initialize the QgsApplication
2create the QgsServer
3the main server loop waits forever for client requests:
4    for each incoming request:
5        create a QgsServerRequest request
6        create a QgsServerResponse response
7        call QgsServer.handleRequest(request, response)
8            filter plugins may be executed
9        send the output to the client

Binnen de methode QgsServer.handleRequest(request, response) worden de callbacks voor de filterplug-ins aangeroepen en QgsServerRequest en QgsServerResponse worden voor de plug-ins beschikbaar gemaakt via de klasse QgsServerInterface.

Waarschuwing

Klassen voor QGIS server zijn niet thread safe, u zou altijd een model voor multiverwerking of containers moeten gebruiken bij het bouwen van schaalbare toepassingen, gebaseerd op de API van QGIS Server.

19.3. Zelfstandig of inbedden

Voor zelfstandige toepassingen voor de server of inbedden dient u de hierboven vermelde klassen voor de server direct te gebruiken, verpak ze in een implementatie voor een webserver die alle interacties met de cliënt voor het protocol HTTP beheert.

Een minimaal voorbeeld voor het gebruiken van de API voor QGIS Server API (zonde het gedeelte voor HTTP) volgt:

 1from qgis.core import QgsApplication
 2from qgis.server import *
 3app = QgsApplication([], False)
 4
 5# Create the server instance, it may be a single one that
 6# is reused on multiple requests
 7server = QgsServer()
 8
 9# Create the request by specifying the full URL and an optional body
10# (for example for POST requests)
11request = QgsBufferServerRequest(
12    'http://localhost:8081/?MAP=/qgis-server/projects/helloworld.qgs' +
13    '&SERVICE=WMS&REQUEST=GetCapabilities')
14
15# Create a response objects
16response = QgsBufferServerResponse()
17
18# Handle the request
19server.handleRequest(request, response)
20
21print(response.headers())
22print(response.body().data().decode('utf8'))
23
24app.exitQgis()

Hier staat een volledig voorbeeld van een zelfstandige toepassing, ontwikkeld voor het doorlopend testen van integraties in de opslagplaats voor de broncode van QGIS. Het laat een brede set van verschillende plug-ins voor filters en authenticatieschema’s zien (niet bedoeld voor productie, omdat zij alleen voor testdoeleinden werden ontwikkeld, maar nog steeds interessant zijn om van te leren): https://github.com/qgis/QGIS/blob/release-3_16/tests/src/python/qgis_wrapped_server.py

19.4. Server plug-ins

Server Python plug-ins worden geladen als de toepassing QGIS Server start en kunnen worden gebruikt om filters, services of API’s te registreren.

De structuur van een server plug-in lijkt zeer veel op zijn collega voor de desktop, een object QgsServerInterface wordt beschikbaar gemaakt voor de plug-ins en de plug-ins kunnen één of meer aangepaste filters, services of API’s registreren in het overeenkomende register door een van de methoden te gebruiken die worden blootgelegd door de interface van de server.

19.4.1. Server filter plug-ins

Filters zijn er in drie verschillende smaken en zij kunnen worden geïnstantieerd door een van de klassen hieronder te subklasseren en door de overeenkomende methode van QgsServerInterface aan te roepen:

Type filter

Basisklasse

registratie QgsServerInterface

I/O

QgsServerFilter

registerFilter()

Access Control

QgsAccessControlFilter

registerAccessControl()

Cache

QgsServerCacheFilter

registerServerCache()

19.4.1.1. I/O-filters

I/O-filters kunnen de in- en uitvoer van de server (het verzoek en het antwoord) van de bronservices (WMS, WFS etc.) aanpassen, wat het mogelijk maakt de werkstroom van de services te bewerken. Het is bijvoorbeeld mogelijk toegang tot de geselecteerde lagen te beperken, een XSL-stijlblad te injecteren in het antwoord van XML, om een watermerk toe te voegen aan een gemaakte afbeelding voor WMS, enzovoort.

Vanaf dit punt zou u het misschien nuttig kunnen vinden om eens snel te kijken naar de documentatie voor API server plug-ins.

Elk filter zou ten minste één van drie instructies moeten implementeren:

Alle filters hebben toegang tot het object voor het verzoek/antwoord (QgsRequestHandler) en kan al zijn eigenschappen bewerken (invoer/uitvoer) en exceptions opwerpen (hoewel op een bijzondere manier zoals we hieronder zullen zien).

Hier is de pseudocode die weergeeft hoe de server een gewoon verzoek afhandelt en wanneer de callbacks van het filter worden aangeroepen:

 1for each incoming request:
 2    create GET/POST request handler
 3    pass request to an instance of QgsServerInterface
 4    call requestReady filters
 5    if there is not a response:
 6        if SERVICE is WMS/WFS/WCS:
 7            create WMS/WFS/WCS service
 8            call service’s executeRequest
 9                possibly call sendResponse for each chunk of bytes
10                sent to the client by a streaming services (WFS)
11        call responseComplete
12        call sendResponse
13    request handler sends the response to the client

De volgende alinea’s beschrijven de beschikbare terugkoppelingen tot in detail.

19.4.1.1.1. requestReady

Dit wordt aangeroepen als het verzoek gereed is: inkomende URL en gegevens zijn geparset en vóór te schakelen naar de bronservices (WMS, WFS etc.), is dit het punt waar u de invoer kunt bewerken en acties kunt uitvoeren als:

  • authenticatie/autorisatie

  • doorverwijzingen

  • bepaalde parameters toevoegen/verwijderen (typenamen bijvoorbeeld)

  • exceptions opwerpen

U zou zelfs een bronservice volledig kunnen vervangen door de parameter SERVICE te wijzigen en op die manier de bronservice volledig omzeilen (niet dat dat echter enige zin zou hebben).

19.4.1.1.2. sendResponse

Deze wordt aangeroepen wanneer enige uitvoer wordt verzonden aan FCGI stdout (en van daaruit naar de cliënt). Dit wordt normaal gesproken gedaan nadat bronservices hun proces hebben voltooid en nadat hook responseComplete werd aangeroepen, maar in een klein aantal gevallen kan de XML zo groot worden dat een stromende XML implementatie nodig is (WFS GetFeature is één ervan). In dit geval, in plaats van een enkele aanroep van :meth:``sendResponse() <qgis.server.QgsServerFilter.sendResponse>`, zou de methode bij uitzondering meerdere keren aangeroepen kunnen worden, voordat het antwoord compleet is, en in dat geval (en alleen in dat geval) wordt het ook aangeroepen vóór responseComplete().

sendResponse() is de beste plaats voor het direct bewerken van de uitvoer van bronservices en hoewel responseComplete() gewoonlijk ook een optie is, is :meth:`sendResponse() <qgis.server.QgsServerFilter.sendResponse> de enige geldige optie in het geval van stromende services.

19.4.1.1.3. responseComplete

Dit wordt eenmaal aangeroepen wanneer de bronservices (indien aangesproken) hun proces voltooien en het verzoek gereed is om te worden verzonden naar de cliënt. Zoals hierboven besproken wordt dit normaal gesproken aangeroepen vóór sendResponse() met uitzondering van stromende services (of andere filters voor plug-ins) die sendResponse() eerder zouden hebben kunnen aangeroepen.

responseComplete() is de ideale plek om implementatie voor nieuwe services te verschaffen (WPS of aangepaste services) en om de uitvoer, komende vanaf bronservices, direct te bewerken (bijvoorbeeld om ene watermerk aan een afbeelding van WMS toe te voegen).

19.4.1.2. Uitzonderingen opwerpen vanuit een plug-in

Er moet nog steeds wat werk worden gedaan aan dit onderwerp: de huidige implementatie kan onderscheid maken tussen afgehandelde en niet afgehandelde uitzonderingen door een eigenschap QgsRequestHandler in te stellen voor een instance van QgsMapServiceException. Op deze manier kan de belangrijkste code in C++ afgehandelde uitzonderingen voor Python opvangen en niet afgehandelde uitzonderingen negeren (of beter nog: ze loggen).

Deze benadering werkt in de basis maar is nog niet erg “Pythonisch”: een betere benadering zou zijn om uitzonderingen op te werpen vanuit de code van Python en ze op zien borrelen in een lus van C++ om daar te worden afgehandeld.

19.4.1.3. Een plug-in voor de server schrijven

Een plug-in voor de server is een standaard plug-in in Python voor QGIS Python zoals beschreven in Python plug-ins ontwikkelen, dat eenvoudigweg een aanvullende (of alternatieve) interface verschaft: een typische plug-in voor QGIS Desktop heeft toegang tot de toepassing QGIS via de instantie QgisInterface, een plug-in voor de server heeft alleen toegang tot een QgsServerInterface wanneer het wordt uitgevoerd binnen de context van de toepassing voor de QGIS Server.

Een speciaal item voor metadata is nodig (in metadata.txt) om QGIS Server te vertellen dat een plug-in een interface voor de server heeft:

server=True

Belangrijk

Alleen plug-ins die de metadata server=True hebben ingesteld zullen worden geladen en uitgevoerd door QGIS Server.

De hier besproken voorbeeldplug-in (met nog veel meer) is beschikbaar op Github op https://github.com/elpaso/qgis3-server-vagrant/tree/master/resources/web/plugins. Een aantal plug-ins voor de server zijn ook gepubliceerd in de officiële QGIS opslagplaats voor plug-ins.

19.4.1.4. Plug-inbestanden

Hier is de mappenstructuur van onze voorbeeld-plug-in voor de server.

1PYTHON_PLUGINS_PATH/
2  HelloServer/
3    __init__.py    --> *required*
4    HelloServer.py  --> *required*
5    metadata.txt   --> *required*
19.4.1.4.1. __init__.py

Dit bestand wordt vereist door het systeem voor importeren van Python. Ook vereist QGIS Server dat dit bestand een functie classFactory() bevat, die wordt aangeroepen als de plug-in wordt geladen in QGIS Server. Het ontvangt een verwijzing naar de instantie van QgsServerInterface en moet een instantie teruggeven van de klasse van uw plug-in. Dit is hoe de voorbeeldplug-in __init__.py eruitziet:

def serverClassFactory(serverIface):
    from .HelloServer import HelloServerServer
    return HelloServerServer(serverIface)
19.4.1.4.2. HelloServer.py

Dit is waar de magie gebeurt en dit is hoe de magie eruit ziet: (bijv. HelloServer.py)

Een plug-in voor de server bestaat gewoonlijk uit één of meer callbacks, verpakt in instanties van een QgsServerFilter.

Elk QgsServerFilter implementeert één of meer van de volgende callbacks:

Het volgende voorbeeld implementeert een minimaal filter dat HelloServer! afdrukt in het geval dat de parameter SERVICE gelijk is aan “HELLO”:

 1class HelloFilter(QgsServerFilter):
 2
 3    def __init__(self, serverIface):
 4        super().__init__(serverIface)
 5
 6    def requestReady(self):
 7        QgsMessageLog.logMessage("HelloFilter.requestReady")
 8
 9    def sendResponse(self):
10        QgsMessageLog.logMessage("HelloFilter.sendResponse")
11
12    def responseComplete(self):
13        QgsMessageLog.logMessage("HelloFilter.responseComplete")
14        request = self.serverInterface().requestHandler()
15        params = request.parameterMap()
16        if params.get('SERVICE', '').upper() == 'HELLO':
17            request.clear()
18            request.setResponseHeader('Content-type', 'text/plain')
19            # Note that the content is of type "bytes"
20            request.appendBody(b'HelloServer!')

De filters moeten worden geregistreerd in de serverIface zoals in het volgende voorbeeld:

class HelloServerServer:
    def __init__(self, serverIface):
        serverIface.registerFilter(HelloFilter(serverIface), 100)

De tweede parameter van registerFilter() stelt een prioriteit in die de volgorde definieert voor de callbacks met dezelfde naam (de laagste prioriteit wordt het eerst uitgevoerd).

Door de drie callbacks te gebruiken, kunnen plug-ins de invoer en/of de uitvoer van de server op veel verschillende manieren manipuleren. Op elk moment heeft de instantie van de plug-in toegang tot de QgsRequestHandler via de QgsServerInterface, de QgsRequestHandler heeft veel methoden die kunnen worden gebruikt om de parameters voor de invoer te wijzigen vóór de bronverwerking door de server (door requestReady() te gebruiken) of nadat het verzoek is verwerkt door de bronservices (door sendResponse() te gebruiken).

De volgende voorbeelden behandelen enkele veel voorkomende gevallen van gebruik:

19.4.1.4.3. De invoer aanpassen

De voorbeeld plug-in bevat een testvoorbeeld dat parameters voor invoer wijzigt die afkomstig zijn uit de tekenreeks van de query, in dit voorbeeld wordt een nieuwe parameter ingevoerd in de (reeds geparste) parameterMap, deze parameter is dan zichtbaar voor bronservices (WMS etc.), aan het einde van de verwerking door bronservices controleren we of de parameter er nog steeds is:

 1class ParamsFilter(QgsServerFilter):
 2
 3    def __init__(self, serverIface):
 4        super(ParamsFilter, self).__init__(serverIface)
 5
 6    def requestReady(self):
 7        request = self.serverInterface().requestHandler()
 8        params = request.parameterMap( )
 9        request.setParameter('TEST_NEW_PARAM', 'ParamsFilter')
10
11    def responseComplete(self):
12        request = self.serverInterface().requestHandler()
13        params = request.parameterMap( )
14        if params.get('TEST_NEW_PARAM') == 'ParamsFilter':
15            QgsMessageLog.logMessage("SUCCESS - ParamsFilter.responseComplete")
16        else:
17            QgsMessageLog.logMessage("FAIL    - ParamsFilter.responseComplete")

Dit is een extract van wat u ziet in het logbestand:

1 src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloServerServer - loading filter ParamsFilter
2 src/core/qgsmessagelog.cpp: 45: (logMessage) [1ms] 2014-12-12T12:39:29 Server[0] Server plugin HelloServer loaded!
3 src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 Server[0] Server python plugins loaded
4 src/mapserver/qgshttprequesthandler.cpp: 547: (requestStringToParameterMap) [1ms] inserting pair SERVICE // HELLO into the parameter map
5 src/mapserver/qgsserverfilter.cpp: 42: (requestReady) [0ms] QgsServerFilter plugin default requestReady called
6 src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] SUCCESS - ParamsFilter.responseComplete

Op de geaccentueerde regel geeft de tekenreeks “SUCCESS” aan dat de plug-in voor de test is geslaagd.

Dezelfde techniek kan worden gebruikt om een aangepaste service te gebruiken in plaats van een bronservice: u zou bijvoorbeeld een verzoek WFS SERVICE kunnen overslaan of elk ander bronverzoek door slechts de parameter SERVICE naar iets anders te wijzigen en de bronservice zal worden overgeslagen. Dan kunt u uw aangepaste resultaten invoeren in de uitvoer en die naar de cliënt verzenden (dat is hieronder uitgelegd).

Tip

Als u echt een aangepaste service wilt implementeren wordt aanbevolen om QgsService te subklasseren en uw service te registreren in registerFilter() door de registerService(service) daarvan aan te roepen

19.4.1.4.4. De uitvoer aanpassen of vervangen

Het voorbeeld watermark filter laat zien hoe de uitvoer van WMS te vervangen door een nieuwe afbeelding die wordt verkregen door het toevoegen van een afbeelding van een watermerk bovenop de afbeelding van WMS die werd gegenereerd door de bronservice van WMS:

 1from qgis.server import *
 2from qgis.PyQt.QtCore import *
 3from qgis.PyQt.QtGui import *
 4
 5class WatermarkFilter(QgsServerFilter):
 6
 7    def __init__(self, serverIface):
 8        super().__init__(serverIface)
 9
10    def responseComplete(self):
11        request = self.serverInterface().requestHandler()
12        params = request.parameterMap( )
13        # Do some checks
14        if (params.get('SERVICE').upper() == 'WMS' \
15                and params.get('REQUEST').upper() == 'GETMAP' \
16                and not request.exceptionRaised() ):
17            QgsMessageLog.logMessage("WatermarkFilter.responseComplete: image ready %s" % request.parameter("FORMAT"))
18            # Get the image
19            img = QImage()
20            img.loadFromData(request.body())
21            # Adds the watermark
22            watermark = QImage(os.path.join(os.path.dirname(__file__), 'media/watermark.png'))
23            p = QPainter(img)
24            p.drawImage(QRect( 20, 20, 40, 40), watermark)
25            p.end()
26            ba = QByteArray()
27            buffer = QBuffer(ba)
28            buffer.open(QIODevice.WriteOnly)
29            img.save(buffer, "PNG" if "png" in request.parameter("FORMAT") else "JPG")
30            # Set the body
31            request.clearBody()
32            request.appendBody(ba)

In dit voorbeeld is de waarde van de parameter SERVICE gecontroleerd en als het inkomende verzoek een WMS GETMAP is en er geen uitzonderingen zijn ingesteld door een eerder uitgevoerde plug-in of door de bronservice (WMS in dit geval), wordt de door WMS gegenereerde afbeelding opgehaald uit de buffer voor de uitvoer en wordt de afbeelding van het watermerk toegevoegd. De laatste stap is om de buffer voor de uitvoer op te schonen en die te vervangen door de nieuw gegenereerde afbeelding. Onthoud dat, in een situatie in de echte wereld, we ook het type van de verzochte afbeelding zouden controleren in plaats van alleen PNG of JPG te ondersteunen.

19.4.1.5. Filters voor Access control

Filters voor Access control geven de ontwikkelaar fijnmazig beheer over tot welke lagen, objecten en attributen toegang kan worden verkregen, de volgende callbacks kunnen worden geïmplementeerd in een filter voor Access control:

19.4.1.5.1. Plug-inbestanden

Hier is de mappenstructuur van onze voorbeeld-plug-in:

1PYTHON_PLUGINS_PATH/
2  MyAccessControl/
3    __init__.py    --> *required*
4    AccessControl.py  --> *required*
5    metadata.txt   --> *required*
19.4.1.5.2. __init__.py

Dit bestand wordt vereist door het systeem voor importeren van Python. Net als voor alle plug-ins voor QGIS Server bevat dit bestand een functie classFactory() bevat, die wordt aangeroepen als de plug-in wordt geladen in QGIS Server bij het opstarten. Het ontvangt een verwijzing naar een instantie van QgsServerInterface en moet een instantie teruggeven van de klasse van uw plug-in. Dit is hoe de voorbeeldplug-in __init__.py er uit ziet:

def serverClassFactory(serverIface):
    from MyAccessControl.AccessControl import AccessControlServer
    return AccessControlServer(serverIface)
19.4.1.5.3. AccessControl.py
 1class AccessControlFilter(QgsAccessControlFilter):
 2
 3    def __init__(self, server_iface):
 4        super().__init__(server_iface)
 5
 6    def layerFilterExpression(self, layer):
 7        """ Return an additional expression filter """
 8        return super().layerFilterExpression(layer)
 9
10    def layerFilterSubsetString(self, layer):
11        """ Return an additional subset string (typically SQL) filter """
12        return super().layerFilterSubsetString(layer)
13
14    def layerPermissions(self, layer):
15        """ Return the layer rights """
16        return super().layerPermissions(layer)
17
18    def authorizedLayerAttributes(self, layer, attributes):
19        """ Return the authorised layer attributes """
20        return super().authorizedLayerAttributes(layer, attributes)
21
22    def allowToEdit(self, layer, feature):
23        """ Are we authorised to modify the following geometry """
24        return super().allowToEdit(layer, feature)
25
26    def cacheKey(self):
27        return super().cacheKey()
28
29class AccessControlServer:
30
31   def __init__(self, serverIface):
32      """ Register AccessControlFilter """
33      serverIface.registerAccessControl(AccessControlFilter(serverIface), 100)

Dit voorbeeld geeft een voorbeeld voor volledige toegang voor iedereen.

Het is de rol van de plug-in om te weten wie er is ingelogd.

Voor al deze methoden hebben de laag als argument om in staat te zien om de rechten per laag aan te passen.

19.4.1.5.4. layerFilterExpression

Gebruikt om een Expressie toe te voegen om de resultaten te beperken, bijv.:

def layerFilterExpression(self, layer):
    return "$role = 'user'"

Te beperken tot de mogelijkheid waar de rol attribuut gelijk is aan “user”.

19.4.1.5.5. layerFilterSubsetString

Hetzelfde als hiervoor maar dan door de SubsetString te gebruiken (uitgevoerd in de database)

def layerFilterSubsetString(self, layer):
    return "role = 'user'"

Te beperken tot de mogelijkheid waar de rol attribuut gelijk is aan “user”.

19.4.1.5.6. layerPermissions

Toegang beperken tot de laag.

Geef een object terug van het type LayerPermissions(), die de eigenschappen heeft:

  • canRead om het te zien in de GetCapabilities en rechten voor lezen hebben.

  • canInsert om een nieuw object te kunnen invoegen.

  • canUpdate om een object te kunnen bijwerken.

  • canDelete om een object te kunnen verwijderen.

Voorbeeld:

1def layerPermissions(self, layer):
2    rights = QgsAccessControlFilter.LayerPermissions()
3    rights.canRead = True
4    rights.canInsert = rights.canUpdate = rights.canDelete = False
5    return rights

Om alles te beperken tot toegang voor alleen-lezen.

19.4.1.5.7. authorizedLayerAttributes

Gebruikt om de zichtbaarheid van een specifieke subset van attributen te beperken.

Het argument attribute geeft de huidige set van zichtbare attributen terug.

Voorbeeld:

def authorizedLayerAttributes(self, layer, attributes):
    return [a for a in attributes if a != "role"]

Het attribuut ‘role’ verbergen.

19.4.1.5.8. allowToEdit

Dit wordt gebruikt om het bewerken van een subset van objecten te beperken.

Het wordt gebruikt in het protocol WFS-Transaction.

Voorbeeld:

def allowToEdit(self, layer, feature):
    return feature.attribute('role') == 'user'

Om het mogelijk te maken alleen objecten te bewerken die het attribuut role hebben met de waarde user.

19.4.1.5.9. cacheKey

QGIS server onderhoudt een cache van de capabilities, om dan een cache per rol te hebben kunt u de rol teruggeven met deze methode. Of geef None terug om de cache volledig uit te schakelen.

19.4.2. Aangepaste services

In QGIS Server zijn bronservices, zoals WMS, WFS en WCS, geïmplementeerd als subklassen van QgsService.

U kunt, om een nieuwe service te implementeren die zal worden uitgevoerd als de tekenreeks voor de query voor de parameter SERVICE overeenkomt met de naam voor de service, uw eigen QgsService implementeren en uw service registreren in serviceRegistry() door de registerService(service) daarvan aan te roepen

Hier is een voorbeeld van een aangepaste service, ganaamd CUSTOM:

 1from qgis.server import QgsService
 2from qgis.core import QgsMessageLog
 3
 4class CustomServiceService(QgsService):
 5
 6    def __init__(self):
 7        QgsService.__init__(self)
 8
 9    def name(self):
10        return "CUSTOM"
11
12    def version(self):
13        return "1.0.0"
14
15    def executeRequest(self, request, response, project):
16        response.setStatusCode(200)
17        QgsMessageLog.logMessage('Custom service executeRequest')
18        response.write("Custom service executeRequest")
19
20
21class CustomService():
22
23    def __init__(self, serverIface):
24        serverIface.serviceRegistry().registerService(CustomServiceService())

19.4.3. Aangepaste API’s

In QGIS Server worden bron-API’S van OGC, zoals OAPIF, (alias WFS3) geïmplementeerd als collecties van subklassen van QgsServerOgcApiHandler, die zijn geregistreerd bij een instantie van QgsServerOgcApi (of zijn ouderklasse QgsServerApi).

U kunt, om een nieuwe API te implementeren die zal worden uitgevoerd als het pad voor de URL overeenkomt met een bepaalde URL, uw eigen instanties QgsServerOgcApiHandler implementeren, ze toevoegen aan een QgsServerOgcApi en de API registreren in het serviceRegistry() door de registerApi(api) daarvan aan te roepen.

Hier is een voorbeeld van een aangepaste API die zal worden uitgevoerd als de URL /customapi bevat:

 1import json
 2import os
 3
 4from qgis.PyQt.QtCore import QBuffer, QIODevice, QTextStream, QRegularExpression
 5from qgis.server import (
 6    QgsServiceRegistry,
 7    QgsService,
 8    QgsServerFilter,
 9    QgsServerOgcApi,
10    QgsServerQueryStringParameter,
11    QgsServerOgcApiHandler,
12)
13
14from qgis.core import (
15    QgsMessageLog,
16    QgsJsonExporter,
17    QgsCircle,
18    QgsFeature,
19    QgsPoint,
20    QgsGeometry,
21)
22
23
24class CustomApiHandler(QgsServerOgcApiHandler):
25
26    def __init__(self):
27        super(CustomApiHandler, self).__init__()
28        self.setContentTypes([QgsServerOgcApi.HTML, QgsServerOgcApi.JSON])
29
30    def path(self):
31        return QRegularExpression("/customapi")
32
33    def operationId(self):
34        return "CustomApiXYCircle"
35
36    def summary(self):
37        return "Creates a circle around a point"
38
39    def description(self):
40        return "Creates a circle around a point"
41
42    def linkTitle(self):
43        return "Custom Api XY Circle"
44
45    def linkType(self):
46        return QgsServerOgcApi.data
47
48    def handleRequest(self, context):
49        """Simple Circle"""
50
51        values = self.values(context)
52        x = values['x']
53        y = values['y']
54        r = values['r']
55        f = QgsFeature()
56        f.setAttributes([x, y, r])
57        f.setGeometry(QgsCircle(QgsPoint(x, y), r).toCircularString())
58        exporter = QgsJsonExporter()
59        self.write(json.loads(exporter.exportFeature(f)), context)
60
61    def templatePath(self, context):
62        # The template path is used to serve HTML content
63        return os.path.join(os.path.dirname(__file__), 'circle.html')
64
65    def parameters(self, context):
66        return [QgsServerQueryStringParameter('x', True, QgsServerQueryStringParameter.Type.Double, 'X coordinate'),
67                QgsServerQueryStringParameter(
68                    'y', True, QgsServerQueryStringParameter.Type.Double, 'Y coordinate'),
69                QgsServerQueryStringParameter('r', True, QgsServerQueryStringParameter.Type.Double, 'radius')]
70
71
72class CustomApi():
73
74    def __init__(self, serverIface):
75        api = QgsServerOgcApi(serverIface, '/customapi',
76                            'custom api', 'a custom api', '1.1')
77        handler = CustomApiHandler()
78        api.registerHandler(handler)
79        serverIface.serviceRegistry().registerApi(api)