[Server][FEATURE] Handle request from QgsServer with a QgsProject

With this commit, it's posssible to handle a request from a QgsProject without writing it to the disk.

```python
server = QgsServer()
project = QgsProject()
vlayer = QgsVectorLayer("/path/to/shapefile/file.shp", "layer_name_you_like", "ogr")
project.addMapLayer(vlayer)

query_string = 'https://www.qgis.org/?SERVICE=WMS&VERSION=1.3&REQUEST=GetCapabilities'
request = QgsBufferServerRequest(query_string, QgsServerRequest.GetMethod, {}, data)
response = QgsBufferServerResponse()
server.handleRequest(request, response, project)
```
This commit is contained in:
rldhont 2017-10-19 15:22:57 +02:00
parent 13a00aae30
commit 7b455d8700
5 changed files with 70 additions and 11 deletions

View File

@ -35,7 +35,7 @@ class QgsServer
.. versionadded:: 2.14
%End
void handleRequest( QgsServerRequest &request, QgsServerResponse &response );
void handleRequest( QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project = 0 );
%Docstring
Handles the request.
The query string is normally read from environment
@ -44,6 +44,9 @@ class QgsServer
\param request a QgsServerRequest holding request parameters
\param response a QgsServerResponse for handling response I/O)
\param project a QgsProject or None, if it is None the project
is created from the MAP param specified in request or from
the QGIS_PROJECT_FILE setting
%End

View File

@ -300,8 +300,7 @@ void QgsServer::putenv( const QString &var, const QString &val )
* @param queryString
* @return response headers and body
*/
void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &response )
void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project )
{
QgsMessageLog::MessageLevel logLevel = QgsServerLogger::instance()->logLevel();
QTime time; //used for measuring request time if loglevel < 1
@ -346,16 +345,23 @@ void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &res
printRequestParameters( parameterMap, logLevel );
//Config file path
QString configFilePath = configPath( *sConfigFilePath, parameterMap );
// load the project if needed and not empty
const QgsProject *project = mConfigCache->project( configFilePath );
if ( ! project )
{
throw QgsServerException( QStringLiteral( "Project file error" ) );
}
QString configFilePath = configPath( *sConfigFilePath, parameterMap );
sServerInterface->setConfigFilePath( configFilePath );
// load the project if needed and not empty
project = mConfigCache->project( configFilePath );
if ( ! project )
{
throw QgsServerException( QStringLiteral( "Project file error" ) );
}
sServerInterface->setConfigFilePath( configFilePath );
}
else
{
sServerInterface->setConfigFilePath( project->fileName() );
}
//Service parameter
QString serviceString = parameterMap.value( QStringLiteral( "SERVICE" ) );

View File

@ -75,8 +75,11 @@ class SERVER_EXPORT QgsServer
*
* \param request a QgsServerRequest holding request parameters
* \param response a QgsServerResponse for handling response I/O)
* \param project a QgsProject or nullptr, if it is nullptr the project
* is created from the MAP param specified in request or from
* the QGIS_PROJECT_FILE setting
*/
void handleRequest( QgsServerRequest &request, QgsServerResponse &response );
void handleRequest( QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project = nullptr );
//! Returns a pointer to the server interface

View File

@ -214,6 +214,17 @@ class QgsServerTestBase(unittest.TestCase):
headers.append(("%s: %s" % (k, rh[k])).encode('utf-8'))
return b"\n".join(headers) + b"\n\n", bytes(response.body())
def _execute_request_project(self, qs, project, requestMethod=QgsServerRequest.GetMethod, data=None):
request = QgsBufferServerRequest(qs, requestMethod, {}, data)
response = QgsBufferServerResponse()
self.server.handleRequest(request, response, project)
headers = []
rh = response.headers()
rk = sorted(rh.keys())
for k in rk:
headers.append(("%s: %s" % (k, rh[k])).encode('utf-8'))
return b"\n".join(headers) + b"\n\n", bytes(response.body())
class TestQgsServerTestBase(unittest.TestCase):
@ -285,6 +296,16 @@ class TestQgsServer(QgsServerTestBase):
self.assertEqual(response.headers(), {'Content-Length': '54', 'Content-Type': 'text/xml; charset=utf-8'})
self.assertEqual(response.statusCode(), 500)
def test_requestHandlerProject(self):
"""Test request handler with none project"""
headers = {'header-key-1': 'header-value-1', 'header-key-2': 'header-value-2'}
request = QgsBufferServerRequest('http://somesite.com/somepath', QgsServerRequest.GetMethod, headers)
response = QgsBufferServerResponse()
self.server.handleRequest(request, response, None)
self.assertEqual(bytes(response.body()), b'<ServerException>Project file error</ServerException>\n')
self.assertEqual(response.headers(), {'Content-Length': '54', 'Content-Type': 'text/xml; charset=utf-8'})
self.assertEqual(response.statusCode(), 500)
def test_api(self):
"""Using an empty query string (returns an XML exception)
we are going to test if headers and body are returned correctly"""

View File

@ -32,6 +32,7 @@ from qgis.PyQt.QtCore import QSize
import osgeo.gdal # NOQA
from test_qgsserver import QgsServerTestBase
from qgis.core import QgsProject
# Strip path and content length because path may vary
RE_STRIP_UNCHECKABLE = b'MAP=[^"]+|Content-Length: \d+'
@ -196,6 +197,31 @@ class TestQgsServerWMS(QgsServerTestBase):
'',
'getschemaextension')
def wms_request_compare_project(self, request, extra=None, reference_file=None):
projectPath = self.testdata_path + "test_project.qgs"
assert os.path.exists(projectPath), "Project file not found: " + projectPath
project = QgsProject()
project.read(projectPath)
query_string = 'https://www.qgis.org/?SERVICE=WMS&VERSION=1.3&REQUEST=%s' % (request)
if extra is not None:
query_string += extra
header, body = self._execute_request_project(query_string, project)
response = header + body
reference_path = self.testdata_path + (request.lower() if not reference_file else reference_file) + '.txt'
self.store_reference(reference_path, response)
f = open(reference_path, 'rb')
expected = f.read()
f.close()
response = re.sub(RE_STRIP_UNCHECKABLE, b'*****', response)
expected = re.sub(RE_STRIP_UNCHECKABLE, b'*****', expected)
self.assertXMLEqual(response, expected, msg="request %s failed.\nQuery: %s\nExpected file: %s\nResponse:\n%s" % (query_string, request, reference_path, response.decode('utf-8')))
def test_wms_getcapabilities_project(self):
self.wms_request_compare_project('GetCapabilities')
def wms_inspire_request_compare(self, request):
"""WMS INSPIRE tests"""
project = self.testdata_path + "test_project_inspire.qgs"