Docs in progress for 'QGIS testing'. Visit https://docs.qgis.org/3.4 for QGIS 3.4 docs and translations.
警告
Despite our constant efforts, information beyond this line may not be updated for QGIS 3. Refer to https://qgis.org/pyqgis/master for the python API documentation or, give a hand to update the chapters you know about. Thanks.
python插件也可以在qgis服务器上运行(请参见 QGIS作为OGC数据服务器 ):
QgsServerInterface
)在服务器上运行的python插件可以改变现有核心服务的行为(wms, WFS 等等)。QgsServerFilter
)您可以更改输入参数、更改生成的输出,甚至提供新的服务。QgsAccessControlFilter
)您可以对每个请求应用一些访问限制。服务器python插件在fcgi应用程序启动时加载一次。他们注册了一个或多个 QgsServerFilter
(从这一点上,您可能会发现,快速查看 server plugins API docs )每个筛选器应至少实现三个回调中的一个:
requestReady()
responseComplete()
sendResponse()
所有筛选器都可以访问请求/响应对象( QgsRequestHandler
)并且可以操纵它的所有属性(输入/输出)并引发异常(同时以一种非常特殊的方式,我们将在下面看到)。
下面是一个伪代码,显示典型的服务器会话以及调用筛选器回调的时间:
QgsServerInterface
requestReady()
过滤器executeRequest()
可能打电话 sendResponse()
当流输出或将字节流输出和内容类型存储在请求处理程序中时,插件过滤器responseComplete()
过滤器sendResponse()
过滤器以下段落详细描述了可用的回调。
当请求就绪时调用此函数:传入的URL和数据已被分析,在进入核心服务(WMS、WFS等)开关之前,您可以在此处操作输入并执行以下操作:
您甚至可以通过更改来完全替代核心服务 SERVICE 参数,因此完全绕过核心服务(尽管这没有多大意义)。
每当输出发送到 FCGI stdout
(从那里到客户机),这通常是在核心服务完成其流程后和调用responseComplete hook之后完成的,但在少数情况下,XML可能会变得如此庞大,以至于需要流式XML实现(WFS GetFeature就是其中之一),在这种情况下, sendResponse()
在响应完成之前多次调用 responseComplete()
被称为。明显的后果是 sendResponse()
通常被称为一次,但可能被例外地称为多次,在这种情况下(仅在这种情况下),它也被称为之前 responseComplete()
.
sendResponse()
is the best place for direct manipulation of core service’s
output and while responseComplete()
is typically also an option,
sendResponse()
is the only viable option in case of streaming services.
当核心服务(如果命中)完成其进程并且请求准备发送到客户机时,调用一次。如上所述,这通常在前面调用 sendResponse()
除了可能调用的流服务(或其他插件过滤器) sendResponse()
早期的。
responseComplete()
is the ideal place to provide new services implementation
(WPS or custom services) and to perform direct manipulation of the output coming
from core services (for example to add a watermark upon a WMS image).
在这个主题上还需要做一些工作:当前的实现可以通过设置 QgsRequestHandler
属性到的实例 QgsMapServiceException
这样,主C++代码就可以捕获处理过的Python异常,而忽略未处理的异常(或者更好:记录它们)。
这种方法基本上是有效的,但它并不是非常“python”:更好的方法是从Python代码中提高异常,并将它们冒泡到C++循环中进行处理。
服务器插件是标准的qgis python插件,如中所述。 开发python插件 这只是提供了一个附加(或替代)接口:典型的QGIS桌面插件可以通过 QgisInterface
例如,服务器插件还可以访问 QgsServerInterface
.
为了告诉QGIS服务器插件具有服务器接口,需要一个特殊的元数据条目(in metadata.txt) ::
server=True
这里讨论的示例插件(还有许多示例过滤器)在Github上可用: QGIS HelloServer Example Plugin . 您还可以在https://github.com/elpaso/qgis3-server-vagrant/tree/master/resources/web/plugins或浏览 QGIS plugins repository .
下面是我们的示例服务器插件的目录结构
PYTHON_PLUGINS_PATH/
HelloServer/
__init__.py --> *required*
HelloServer.py --> *required*
metadata.txt --> *required*
这个文件是python的导入系统所必需的。此外,qgis服务器要求此文件包含 serverClassFactory()
函数,当插件在服务器启动时加载到qgis服务器时调用。它接收对的实例的引用 QgsServerInterface
必须返回插件类的实例。这就是示例插件 __init__.py
看起来像:
# -*- coding: utf-8 -*-
def serverClassFactory(serverIface):
from HelloServer import HelloServerServer
return HelloServerServer(serverIface)
这就是魔法发生的地方,这就是魔法的样子:(例如 HelloServer.py
)
服务器插件通常包含一个或多个回调,这些回调打包到称为qgsserverfilter的对象中。
各 QgsServerFilter
实现以下一个或多个回调:
requestReady()
responseComplete()
sendResponse()
下面的示例实现了打印*helloserver的最小过滤器!*万一 SERVICE 参数等于“hello”::
from qgis.server import *
from qgis.core import *
class HelloFilter(QgsServerFilter):
def __init__(self, serverIface):
super(HelloFilter, self).__init__(serverIface)
def responseComplete(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap()
if params.get('SERVICE', '').upper() == 'HELLO':
request.clearHeaders()
request.setHeader('Content-type', 'text/plain')
request.clearBody()
request.appendBody('HelloServer!')
必须将筛选器注册到 serverIface 如以下示例所示:
class HelloServerServer:
def __init__(self, serverIface):
# Save reference to the QGIS server interface
self.serverIface = serverIface
serverIface.registerFilter( HelloFilter, 100 )
的第二个参数 registerFilter()
允许设置优先级,该优先级定义具有相同名称的回调顺序(先调用较低优先级)。
通过使用这三个回调,插件可以以许多不同的方式操作服务器的输入和/或输出。在每个时刻,插件实例都可以访问 QgsRequestHandler
通过 QgsServerInterface
, the QgsRequestHandler
有很多方法可以用来在进入服务器的核心处理之前更改输入参数(通过使用 requestReady()
)或者在核心服务(通过使用 sendResponse()
)
以下示例涵盖了一些常见的用例:
该示例插件包含一个测试示例,该示例更改来自查询字符串的输入参数,在本示例中,一个新参数被注入(已解析) parameterMap
,然后核心服务(WMS等)可以看到该参数,在核心服务处理结束时,我们检查该参数是否仍然存在:
from qgis.server import *
from qgis.core import *
class ParamsFilter(QgsServerFilter):
def __init__(self, serverIface):
super(ParamsFilter, self).__init__(serverIface)
def requestReady(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap( )
request.setParameter('TEST_NEW_PARAM', 'ParamsFilter')
def responseComplete(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap( )
if params.get('TEST_NEW_PARAM') == 'ParamsFilter':
QgsMessageLog.logMessage("SUCCESS - ParamsFilter.responseComplete", 'plugin', QgsMessageLog.INFO)
else:
QgsMessageLog.logMessage("FAIL - ParamsFilter.responseComplete", 'plugin', QgsMessageLog.CRITICAL)
这是您在日志文件中看到的内容的提取:
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloServerServer - loading filter ParamsFilter
src/core/qgsmessagelog.cpp: 45: (logMessage) [1ms] 2014-12-12T12:39:29 Server[0] Server plugin HelloServer loaded!
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 Server[0] Server python plugins loaded
src/mapserver/qgsgetrequesthandler.cpp: 35: (parseInput) [0ms] query string is: SERVICE=HELLO&request=GetOutput
src/mapserver/qgshttprequesthandler.cpp: 547: (requestStringToParameterMap) [1ms] inserting pair SERVICE // HELLO into the parameter map
src/mapserver/qgshttprequesthandler.cpp: 547: (requestStringToParameterMap) [0ms] inserting pair REQUEST // GetOutput into the parameter map
src/mapserver/qgsserverfilter.cpp: 42: (requestReady) [0ms] QgsServerFilter plugin default requestReady called
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloFilter.requestReady
src/mapserver/qgis_map_serv.cpp: 235: (configPath) [0ms] Using default configuration file path: /home/xxx/apps/bin/admin.sld
src/mapserver/qgshttprequesthandler.cpp: 49: (setHttpResponse) [0ms] Checking byte array is ok to set...
src/mapserver/qgshttprequesthandler.cpp: 59: (setHttpResponse) [0ms] Byte array looks good, setting response...
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloFilter.responseComplete
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] SUCCESS - ParamsFilter.responseComplete
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] RemoteConsoleFilter.responseComplete
src/mapserver/qgshttprequesthandler.cpp: 158: (sendResponse) [0ms] Sending HTTP response
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloFilter.sendResponse
在突出显示的行中,“success”字符串表示插件通过了测试。
可以利用相同的技术来使用自定义服务而不是核心服务:例如,可以跳过 WFS 服务**请求或仅通过更改 **SERVICE 参数不同,核心服务将被跳过,然后您可以将自定义结果注入到输出中,并将其发送到客户机(下面将对此进行解释)。
水印过滤器示例显示了如何将WMS输出替换为通过在WMS核心服务生成的WMS图像顶部添加水印图像而获得的新图像:
import os
from qgis.server import *
from qgis.core import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import *
class WatermarkFilter(QgsServerFilter):
def __init__(self, serverIface):
super(WatermarkFilter, self).__init__(serverIface)
def responseComplete(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap( )
# Do some checks
if (request.parameter('SERVICE').upper() == 'WMS' \
and request.parameter('REQUEST').upper() == 'GETMAP' \
and not request.exceptionRaised() ):
QgsMessageLog.logMessage("WatermarkFilter.responseComplete: image ready {}".format(request.infoFormat()), 'plugin', QgsMessageLog.INFO)
# Get the image
img = QImage()
img.loadFromData(request.body())
# Adds the watermark
watermark = QImage(os.path.join(os.path.dirname(__file__), 'media/watermark.png'))
p = QPainter(img)
p.drawImage(QRect( 20, 20, 40, 40), watermark)
p.end()
ba = QByteArray()
buffer = QBuffer(ba)
buffer.open(QIODevice.WriteOnly)
img.save(buffer, "PNG")
# Set the body
request.clearBody()
request.appendBody(ba)
在这个例子中, SERVICE 检查参数值,如果传入请求是 WMS **getmap**并且之前执行的插件或核心服务(在本例中为WMS)没有设置任何异常,从输出缓冲区检索WMS生成的图像并添加水印图像。最后一步是清除输出缓冲区并将其替换为新生成的图像。请注意,在实际情况下,我们还应该检查请求的图像类型,而不是在任何情况下返回PNG。
下面是示例服务器插件的目录结构:
PYTHON_PLUGINS_PATH/
MyAccessControl/
__init__.py --> *required*
AccessControl.py --> *required*
metadata.txt --> *required*
这个文件是python的导入系统所必需的。对于所有QGIS服务器插件,此文件包含 serverClassFactory()
函数,当插件在服务器启动时加载到qgis服务器时调用。它接收对的实例的引用 QgsServerInterface
必须返回插件类的实例。这就是示例插件 __init__.py
看起来像:
# -*- coding: utf-8 -*-
def serverClassFactory(serverIface):
from MyAccessControl.AccessControl import AccessControl
return AccessControl(serverIface)
class AccessControl(QgsAccessControlFilter):
def __init__(self, server_iface):
super(QgsAccessControlFilter, self).__init__(server_iface)
def layerFilterExpression(self, layer):
""" Return an additional expression filter """
return super(QgsAccessControlFilter, self).layerFilterExpression(layer)
def layerFilterSubsetString(self, layer):
""" Return an additional subset string (typically SQL) filter """
return super(QgsAccessControlFilter, self).layerFilterSubsetString(layer)
def layerPermissions(self, layer):
""" Return the layer rights """
return super(QgsAccessControlFilter, self).layerPermissions(layer)
def authorizedLayerAttributes(self, layer, attributes):
""" Return the authorised layer attributes """
return super(QgsAccessControlFilter, self).authorizedLayerAttributes(layer, attributes)
def allowToEdit(self, layer, feature):
""" Are we authorise to modify the following geometry """
return super(QgsAccessControlFilter, self).allowToEdit(layer, feature)
def cacheKey(self):
return super(QgsAccessControlFilter, self).cacheKey()
这个例子为每个人提供了完全访问权。
插件的作用是知道谁登录了。
在所有这些方法中,我们都有一个参数层来定制每层的限制。
用于添加表达式以限制结果,例如:
def layerFilterExpression(self, layer):
return "$role = 'user'"
限制属性角色等于“用户”的功能。
与上一个相同,但使用 SubsetString
(在数据库中执行)
def layerFilterSubsetString(self, layer):
return "role = 'user'"
限制属性角色等于“用户”的功能。
限制对层的访问。
返回类型为的对象 QgsAccessControlFilter.LayerPermissions
,谁拥有这些属性:
canRead
to see him in the GetCapabilities
and have read access.canInsert
to be able to insert a new feature.canUpdate
to be able to update a feature.candelete
to be able to delete a feature.例子:
def layerPermissions(self, layer):
rights = QgsAccessControlFilter.LayerPermissions()
rights.canRead = True
rights.canRead = rights.canInsert = rights.canUpdate = rights.canDelete = False
return rights
限制所有内容的只读访问。
用于限制特定属性子集的可见性。
参数属性返回当前可见属性集。
例子:
def authorizedLayerAttributes(self, layer, attributes):
return [a for a in attributes if a != "role"]
隐藏“role”属性。