QGIS API Documentation 3.43.0-Master (0cdc48caa8d)
qgsserverogcapihandler.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsserverogcapihandler.cpp - QgsServerOgcApiHandler
3
4 ---------------------
5 begin : 10.7.2019
6 copyright : (C) 2019 by Alessandro Pasotti
7 email : elpaso at itopen dot it
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17#include <QDateTime>
18#include <QFileInfo>
19#include <QDir>
20#include <QDebug>
21
22#include "qgsmessagelog.h"
23#include "qgsproject.h"
24#include "qgsjsonutils.h"
25#include "qgsvectorlayer.h"
26
28#include "qgsserverapiutils.h"
29#include "qgsserverresponse.h"
30#include "qgsserverinterface.h"
31
32#include "nlohmann/json.hpp"
33#include "inja/inja.hpp"
34
35using namespace nlohmann;
36using namespace inja;
37
38
39QVariantMap QgsServerOgcApiHandler::values( const QgsServerApiContext &context ) const
40{
41 QVariantMap result;
42 const auto constParameters { parameters( context ) };
43 for ( const auto &p : constParameters )
44 {
45 // value() calls the validators and throws an exception if validation fails
46 result[p.name()] = p.value( context );
47 }
48 const auto sanitizedPath { QgsServerOgcApi::sanitizeUrl( context.handlerPath() ).path() };
49 const auto match { path().match( sanitizedPath ) };
50 if ( match.hasMatch() )
51 {
52 const auto constNamed { path().namedCaptureGroups() };
53 // Get named path parameters
54 for ( const auto &name : constNamed )
55 {
56 if ( !name.isEmpty() )
57 result[name] = QUrlQuery( match.captured( name ) ).toString();
58 }
59 }
60 return result;
61}
62
64{
65 //qDebug() << "handler destroyed";
66}
67
69{
70 const auto constContentTypes( contentTypes() );
71 return constContentTypes.size() > 0 ? constContentTypes.first() : QgsServerOgcApi::ContentType::JSON;
72}
73
74QList<QgsServerOgcApi::ContentType> QgsServerOgcApiHandler::contentTypes() const
75{
76 return mContentTypes;
77}
78
80{
81 Q_UNUSED( context )
82 throw QgsServerApiNotImplementedException( QStringLiteral( "Subclasses must implement handleRequest" ) );
83}
84
85QString QgsServerOgcApiHandler::contentTypeForAccept( const QString &accept ) const
86{
87 const auto constContentTypes( QgsServerOgcApi::contentTypeMimes() );
88 for ( auto it = constContentTypes.constBegin();
89 it != constContentTypes.constEnd(); ++it )
90 {
91 const auto constValues = it.value();
92 for ( const auto &value : constValues )
93 {
94 if ( accept.contains( value, Qt::CaseSensitivity::CaseInsensitive ) )
95 {
96 return value;
97 }
98 }
99 }
100 // Log level info because this is not completely unexpected
101 QgsMessageLog::logMessage( QStringLiteral( "Content type for accept %1 not found!" ).arg( accept ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
102
103 return QString();
104}
105
106void QgsServerOgcApiHandler::write( json &data, const QgsServerApiContext &context, const json &htmlMetadata ) const
107{
108 const QgsServerOgcApi::ContentType contentType { contentTypeFromRequest( context.request() ) };
109 switch ( contentType )
110 {
112 data["handler"] = schema( context );
113 if ( !htmlMetadata.is_null() )
114 {
115 data["metadata"] = htmlMetadata;
116 }
117 htmlDump( data, context );
118 break;
122 jsonDump( data, context, QgsServerOgcApi::contentTypeMimes().value( contentType ).first() );
123 break;
125 // Not handled yet
126 break;
127 }
128}
129
130void QgsServerOgcApiHandler::write( QVariant &data, const QgsServerApiContext &context, const QVariantMap &htmlMetadata ) const
131{
132 json j = QgsJsonUtils::jsonFromVariant( data );
133 json jm = QgsJsonUtils::jsonFromVariant( htmlMetadata );
134 QgsServerOgcApiHandler::write( j, context, jm );
135}
136
137std::string QgsServerOgcApiHandler::href( const QgsServerApiContext &context, const QString &extraPath, const QString &extension ) const
138{
139 QUrl url { context.request()->url() };
140 QString urlBasePath { context.matchedPath() };
141 const auto match { path().match( QgsServerOgcApi::sanitizeUrl( context.handlerPath() ).path() ) };
142 if ( match.captured().count() > 0 )
143 {
144 url.setPath( urlBasePath + match.captured( 0 ) );
145 }
146 else
147 {
148 url.setPath( urlBasePath );
149 }
150
151 // Remove any existing extension
152 const auto suffixLength { QFileInfo( url.path() ).suffix().length() };
153 if ( suffixLength > 0 )
154 {
155 auto path { url.path() };
156 path.truncate( path.length() - ( suffixLength + 1 ) );
157 url.setPath( path );
158 }
159
160 // Add extra path
161 url.setPath( url.path() + extraPath );
162
163 // (re-)add extension
164 // JSON is the default anyway so we don't need to add it
165 if ( !extension.isEmpty() )
166 {
167 // Remove trailing slashes if any.
168 QString path { url.path() };
169 while ( path.endsWith( '/' ) )
170 {
171 path.chop( 1 );
172 }
173 url.setPath( path + '.' + extension );
174 }
175 return QgsServerOgcApi::sanitizeUrl( url ).toString( QUrl::FullyEncoded ).toStdString();
176}
177
178void QgsServerOgcApiHandler::jsonDump( json &data, const QgsServerApiContext &context, const QString &contentType ) const
179{
180 // Do not append timestamp to openapi
181 if ( !QgsServerOgcApi::contentTypeMimes().value( QgsServerOgcApi::ContentType::OPENAPI3 ).contains( contentType, Qt::CaseSensitivity::CaseInsensitive ) )
182 {
183 QDateTime time { QDateTime::currentDateTime() };
184 time.setTimeSpec( Qt::TimeSpec::UTC );
185 data["timeStamp"] = time.toString( Qt::DateFormat::ISODate ).toStdString();
186 }
187 context.response()->setStatusCode( 200 );
188 context.response()->setHeader( QStringLiteral( "Content-Type" ), contentType );
189#ifdef QGISDEBUG
190 context.response()->write( data.dump( 2 ) );
191#else
192 context.response()->write( data.dump() );
193#endif
194}
195
197{
198 Q_UNUSED( context )
199 return nullptr;
200}
201
202json QgsServerOgcApiHandler::link( const QgsServerApiContext &context, const QgsServerOgcApi::Rel &linkType, const QgsServerOgcApi::ContentType contentType, const std::string &title ) const
203{
204 json l {
205 { "href", href( context, "/", QgsServerOgcApi::contentTypeToExtension( contentType ) )
206 },
208 { "type", QgsServerOgcApi::mimeType( contentType ) },
209 { "title", title != "" ? title : linkTitle() },
210 };
211 return l;
212}
213
215{
216 const QgsServerOgcApi::ContentType currentCt { contentTypeFromRequest( context.request() ) };
217 json links = json::array();
218 const QList<QgsServerOgcApi::ContentType> constCts { contentTypes() };
219 for ( const auto &ct : constCts )
220 {
221 links.push_back( link( context, ( ct == currentCt ? QgsServerOgcApi::Rel::self : QgsServerOgcApi::Rel::alternate ), ct, linkTitle() + " as " + QgsServerOgcApi::contentTypeToStdString( ct ) ) );
222 }
223 return links;
224}
225
227{
228 if ( !context.project() )
229 {
230 throw QgsServerApiImproperlyConfiguredException( QStringLiteral( "Project is invalid or undefined" ) );
231 }
232 // Check collectionId
233 const QRegularExpressionMatch match { path().match( context.request()->url().path() ) };
234 if ( !match.hasMatch() )
235 {
236 throw QgsServerApiNotFoundError( QStringLiteral( "Collection was not found" ) );
237 }
238 const QString collectionId { match.captured( QStringLiteral( "collectionId" ) ) };
239 // May throw if not found
240 return layerFromCollectionId( context, collectionId );
241}
242
243const QString QgsServerOgcApiHandler::staticPath( const QgsServerApiContext &context ) const
244{
245 // resources/server/api + /static
246 return context.serverInterface()->serverSettings()->apiResourcesDirectory() + QStringLiteral( "/ogc/static" );
247}
248
250{
251 // resources/server/api + /ogc/templates/ + operationId + .html
252 QString path { context.serverInterface()->serverSettings()->apiResourcesDirectory() };
253 path += QLatin1String( "/ogc/templates" );
254 path += context.apiRootPath();
255 path += '/';
256 path += QString::fromStdString( operationId() );
257 path += QLatin1String( ".html" );
258 return path;
259}
260
261
262void QgsServerOgcApiHandler::htmlDump( const json &data, const QgsServerApiContext &context ) const
263{
264 context.response()->setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/html" ) );
265 auto path { templatePath( context ) };
266 if ( !QFile::exists( path ) )
267 {
268 QgsMessageLog::logMessage( QStringLiteral( "Template not found error: %1" ).arg( path ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
269 throw QgsServerApiBadRequestException( QStringLiteral( "Template not found: %1" ).arg( QFileInfo( path ).fileName() ) );
270 }
271
272 QFile f( path );
273 if ( !f.open( QFile::ReadOnly | QFile::Text ) )
274 {
275 QgsMessageLog::logMessage( QStringLiteral( "Could not open template file: %1" ).arg( path ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
276 throw QgsServerApiInternalServerError( QStringLiteral( "Could not open template file: %1" ).arg( QFileInfo( path ).fileName() ) );
277 }
278
279 try
280 {
281 // Get the template directory and the file name
282 QFileInfo pathInfo { path };
283 Environment env { QString( pathInfo.dir().path() + QDir::separator() ).toStdString() };
284
285 // For template debugging:
286 env.add_callback( "json_dump", 0, [=]( Arguments & ) {
287 return data.dump();
288 } );
289
290 // Path manipulation: appends a directory path to the current url
291 env.add_callback( "path_append", 1, [=]( Arguments &args ) {
292 auto url { context.request()->url() };
293 QFileInfo fi { url.path() };
294 auto suffix { fi.suffix() };
295 auto fName { fi.filePath() };
296 if ( !suffix.isEmpty() )
297 {
298 fName.chop( suffix.length() + 1 );
299 }
300 // Chop any ending slashes
301 while ( fName.endsWith( '/' ) )
302 {
303 fName.chop( 1 );
304 }
305 fName += '/' + QString::fromStdString( args.at( 0 )->get<std::string>() );
306 if ( !suffix.isEmpty() )
307 {
308 fName += '.' + suffix;
309 }
310 fi.setFile( fName );
311 url.setPath( fi.filePath() );
312 return url.toString().toStdString();
313 } );
314
315 // Path manipulation: removes the specified number of directory components from the current url path
316 env.add_callback( "path_chomp", 1, [=]( Arguments &args ) {
317 QUrl url { QString::fromStdString( args.at( 0 )->get<std::string>() ) };
318 QFileInfo fi { url.path() };
319 auto suffix { fi.suffix() };
320 auto fName { fi.filePath() };
321 fName.chop( suffix.length() + 1 );
322 // Chomp last segment
323 const thread_local QRegularExpression segmentRx( R"raw(\/[^/]+$)raw" );
324 fName = fName.replace( segmentRx, QString() );
325 if ( !suffix.isEmpty() )
326 {
327 fName += '.' + suffix;
328 }
329 fi.setFile( fName );
330 url.setPath( fi.filePath() );
331 return url.toString().toStdString();
332 } );
333
334 // Returns filtered links from a link list
335 // links_filter( <links>, <key>, <value> )
336 env.add_callback( "links_filter", 3, [=]( Arguments &args ) {
337 json links = args.at( 0 )->get<json>();
338 if ( !links.is_array() )
339 {
340 links = json::array();
341 }
342 std::string key { args.at( 1 )->get<std::string>() };
343 std::string value { args.at( 2 )->get<std::string>() };
344 json result = json::array();
345 for ( const auto &l : links )
346 {
347 if ( l[key] == value )
348 {
349 result.push_back( l );
350 }
351 }
352 return result;
353 } );
354
355 // Returns a short name from content types
356 env.add_callback( "content_type_name", 1, [=]( Arguments &args ) {
357 const QgsServerOgcApi::ContentType ct { QgsServerOgcApi::contentTypeFromExtension( args.at( 0 )->get<std::string>() ) };
359 } );
360
361 // Replace newlines with <br>
362 env.add_callback( "nl2br", 1, [=]( Arguments &args ) {
363 QString text { QString::fromStdString( args.at( 0 )->get<std::string>() ) };
364 return text.replace( '\n', QLatin1String( "<br>" ) ).toStdString();
365 } );
366
367
368 // Returns a list of parameter component data from components -> parameters by ref name
369 // parameter( <ref object> )
370 env.add_callback( "component_parameter", 1, [=]( Arguments &args ) {
371 json ret = json::array();
372 json ref = args.at( 0 )->get<json>();
373 if ( !ref.is_object() )
374 {
375 return ret;
376 }
377 try
378 {
379 QString name = QString::fromStdString( ref["$ref"] );
380 name = name.split( '/' ).last();
381 ret.push_back( data["components"]["parameters"][name.toStdString()] );
382 }
383 catch ( std::exception & )
384 {
385 // Do nothing
386 }
387 return ret;
388 } );
389
390
391 // Static: returns the full URL to the specified static <path>
392 env.add_callback( "static", 1, [=]( Arguments &args ) {
393 auto asset( args.at( 0 )->get<std::string>() );
394 QString matchedPath { context.matchedPath() };
395 // If its the root path '/' strip it!
396 if ( matchedPath == '/' )
397 {
398 matchedPath.clear();
399 }
400 return matchedPath.toStdString() + "/static/" + asset;
401 } );
402
403
404 // Returns true if a string begins with the provided string prefix, false otherwise
405 env.add_callback( "starts_with", 2, []( Arguments &args ) {
406 return string_view::starts_with( args.at( 0 )->get<std::string_view>(), args.at( 1 )->get<std::string_view>() );
407 } );
408
409 // Returns "null" string if object is null else string object representation
410 env.add_callback( "if_nullptr_null_str", 1, []( Arguments &args ) {
411 json jsonValue = args.at( 0 )->get<json>();
412 std::string out;
413 switch ( jsonValue.type() )
414 {
415 // avoid escaping string value
416 case json::value_t::string:
417 out = jsonValue.get<std::string>();
418 break;
419
420 case json::value_t::array:
421 case json::value_t::object:
422 if ( jsonValue.is_null() )
423 {
424 out = "null";
425 }
426 else
427 {
428 out = jsonValue.dump();
429 }
430
431 break;
432
433 // use dump() for all other value types
434 default:
435 out = jsonValue.dump();
436 }
437 return out;
438 } );
439
440 context.response()->write( env.render_file( pathInfo.fileName().toStdString(), data ) );
441 }
442 catch ( std::exception &e )
443 {
444 QgsMessageLog::logMessage( QStringLiteral( "Error parsing template file: %1 - %2" ).arg( path, e.what() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
445 throw QgsServerApiInternalServerError( QStringLiteral( "Error parsing template file: %1" ).arg( e.what() ) );
446 }
447}
448
450{
451 // Fallback to default
453 bool found { false };
454 // First file extension ...
455 const QString extension { QFileInfo( request->url().path() ).suffix().toUpper() };
456 if ( !extension.isEmpty() )
457 {
458 static QMetaEnum metaEnum { QMetaEnum::fromType<QgsServerOgcApi::ContentType>() };
459 bool ok { false };
460 const int ct { metaEnum.keyToValue( extension.toLocal8Bit().constData(), &ok ) };
461 if ( ok )
462 {
463 result = static_cast<QgsServerOgcApi::ContentType>( ct );
464 found = true;
465 }
466 else
467 {
468 QgsMessageLog::logMessage( QStringLiteral( "The client requested an unsupported extension: %1" ).arg( extension ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
469 }
470 }
471 // ... then "Accept"
472 const QString accept { request->header( QStringLiteral( "Accept" ) ) };
473 if ( !found && !accept.isEmpty() )
474 {
475 const QString ctFromAccept { contentTypeForAccept( accept ) };
476 if ( !ctFromAccept.isEmpty() )
477 {
478 const auto constContentTypes( QgsServerOgcApi::contentTypeMimes() );
479 auto it = constContentTypes.constBegin();
480 while ( !found && it != constContentTypes.constEnd() )
481 {
482 int idx = it.value().indexOf( ctFromAccept );
483 if ( idx >= 0 )
484 {
485 found = true;
486 result = it.key();
487 }
488 it++;
489 }
490 }
491 else
492 {
493 QgsMessageLog::logMessage( QStringLiteral( "The client requested an unsupported content type in Accept header: %1" ).arg( accept ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
494 }
495 }
496 // Validation: check if the requested content type (or an alias) is supported by the handler
497 if ( !contentTypes().contains( result ) )
498 {
499 // Check aliases
500 bool found { false };
501 if ( QgsServerOgcApi::contentTypeAliases().contains( result ) )
502 {
503 const QList<QgsServerOgcApi::ContentType> constCt { contentTypes() };
504 for ( const auto &ct : constCt )
505 {
506 if ( QgsServerOgcApi::contentTypeAliases()[result].contains( ct ) )
507 {
508 result = ct;
509 found = true;
510 break;
511 }
512 }
513 }
514
515 if ( !found )
516 {
517 QgsMessageLog::logMessage( QStringLiteral( "Unsupported Content-Type: %1" ).arg( QgsServerOgcApi::contentTypeToString( result ) ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
518 throw QgsServerApiBadRequestException( QStringLiteral( "Unsupported Content-Type: %1" ).arg( QgsServerOgcApi::contentTypeToString( result ) ) );
519 }
520 }
521 return result;
522}
523
524QString QgsServerOgcApiHandler::parentLink( const QUrl &url, int levels )
525{
526 QString path { url.path() };
527 const QFileInfo fi { path };
528 const QString suffix { fi.suffix() };
529 if ( !suffix.isEmpty() )
530 {
531 path.chop( suffix.length() + 1 );
532 }
533 while ( path.endsWith( '/' ) )
534 {
535 path.chop( 1 );
536 }
537 const thread_local QRegularExpression re( R"raw(\/[^/]+$)raw" );
538 for ( int i = 0; i < levels; i++ )
539 {
540 path = path.replace( re, QString() );
541 }
542 QUrl result( url );
543 QUrlQuery query( result );
544 QList<QPair<QString, QString>> qi;
545 const auto constItems { query.queryItems() };
546 for ( const auto &i : constItems )
547 {
548 if ( i.first.compare( QStringLiteral( "MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
549 {
550 qi.push_back( i );
551 }
552 }
553 // Make sure the parent link ends with a slash
554 if ( !path.endsWith( '/' ) )
555 {
556 path.append( '/' );
557 }
558 QUrlQuery resultQuery;
559 resultQuery.setQueryItems( qi );
560 result.setQuery( resultQuery );
561 result.setPath( path );
562 return result.toString();
563}
564
566{
567 const auto mapLayers { context.project()->mapLayersByShortName<QgsVectorLayer *>( collectionId ) };
568 if ( mapLayers.count() != 1 )
569 {
570 throw QgsServerApiNotFoundError( QStringLiteral( "Collection with given id (%1) was not found or multiple matches were found" ).arg( collectionId ) );
571 }
572 return mapLayers.first();
573}
574
576{
577 static json defRes = {
578 { "description", "An error occurred." },
579 { "content", { { "application/json", { { "schema", { { "$ref", "#/components/schemas/exception" } } } } }, { "text/html", { { "schema", { { "type", "string" } } } } } }
580 }
581 };
582 return defRes;
583}
584
589
590void QgsServerOgcApiHandler::setContentTypesInt( const QList<int> &contentTypes )
591{
592 mContentTypes.clear();
593 for ( const int &i : std::as_const( contentTypes ) )
594 {
595 mContentTypes.push_back( static_cast<QgsServerOgcApi::ContentType>( i ) );
596 }
597}
598
599void QgsServerOgcApiHandler::setContentTypes( const QList<QgsServerOgcApi::ContentType> &contentTypes )
600{
601 mContentTypes = contentTypes;
602}
@ Warning
Warning message.
Definition qgis.h:156
@ Critical
Critical/error message.
Definition qgis.h:157
@ Info
Information message.
Definition qgis.h:155
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
QList< QgsMapLayer * > mapLayersByShortName(const QString &shortName) const
Retrieves a list of matching registered layers by layer shortName.
Bad request error API exception.
Encapsulates the resources for a particular client request.
const QgsProject * project() const
Returns the (possibly NULL) project.
QgsServerResponse * response() const
Returns the server response object.
QString handlerPath() const
Returns the handler component of the URL path, i.e.
const QgsServerRequest * request() const
Returns the server request object.
QgsServerInterface * serverInterface() const
Returns the server interface.
QString apiRootPath() const
Returns the API root path.
const QString matchedPath() const
Returns the initial part of the incoming request URL path that matches the API root path.
Raised when a configuration error on the server prevents to serve the request, which would be valid o...
Internal server error API exception.
Not found error API exception.
Raised when the client requested a method that is not yet implemented.
virtual QgsServerSettings * serverSettings()=0
Returns the server settings.
std::string href(const QgsServerApiContext &context, const QString &extraPath=QString(), const QString &extension=QString()) const
Returns an URL to self, to be used for links to the current resources and as a base for constructing ...
virtual const QString templatePath(const QgsServerApiContext &context) const
Returns the HTML template path for the handler in the given context.
virtual const QString staticPath(const QgsServerApiContext &context) const
Returns the absolute path to the base directory where static resources for this handler are stored in...
virtual QVariantMap values(const QgsServerApiContext &context) const
Analyzes the incoming request context and returns the validated parameter map, throws QgsServerApiBad...
json jsonTags() const
Returns tags as JSON.
void htmlDump(const json &data, const QgsServerApiContext &context) const
Writes data as HTML to the response stream in context using a template.
virtual QgsServerOgcApi::ContentType defaultContentType() const
Returns the default response content type in case the client did not specifically ask for any particu...
QgsServerOgcApi::ContentType contentTypeFromRequest(const QgsServerRequest *request) const
Returns the content type from the request.
virtual QgsServerOgcApi::Rel linkType() const =0
Main role for the resource link.
virtual json schema(const QgsServerApiContext &context) const
Returns handler information from the context for the OPENAPI description (id, description and other m...
virtual QStringList tags() const
Tags.
void setContentTypes(const QList< QgsServerOgcApi::ContentType > &contentTypes)
Set the content types to contentTypes.
void write(json &data, const QgsServerApiContext &context, const json &htmlMetadata=nullptr) const
Writes data to the context response stream, content-type is calculated from the context request,...
QList< QgsServerOgcApi::ContentType > contentTypes() const
Returns the list of content types this handler can serve, default to JSON and HTML.
virtual std::string linkTitle() const =0
Title for the handler link.
virtual QList< QgsServerQueryStringParameter > parameters(const QgsServerApiContext &context) const
Returns a list of query string parameters.
virtual std::string operationId() const =0
Returns the operation id for template file names and other internal references.
static json defaultResponse()
Returns the defaultResponse as JSON.
void jsonDump(json &data, const QgsServerApiContext &context, const QString &contentType=QStringLiteral("application/json")) const
Writes data to the context response stream as JSON (indented if debug is active), an optional content...
QgsVectorLayer * layerFromContext(const QgsServerApiContext &context) const
Returns a vector layer instance from the "collectionId" parameter of the path in the given context,...
QString contentTypeForAccept(const QString &accept) const
Looks for the first ContentType match in the accept header and returns its mime type,...
virtual void handleRequest(const QgsServerApiContext &context) const
Handles the request within its context.
json link(const QgsServerApiContext &context, const QgsServerOgcApi::Rel &linkType=QgsServerOgcApi::Rel::self, const QgsServerOgcApi::ContentType contentType=QgsServerOgcApi::ContentType::JSON, const std::string &title="") const
Builds and returns a link to the resource.
json links(const QgsServerApiContext &context) const
Returns all the links for the given request context.
static QString parentLink(const QUrl &url, int levels=1)
Returns a link to the parent page up to levels in the HTML hierarchy from the given url,...
virtual QRegularExpression path() const =0
URL pattern for this handler, named capture group are automatically extracted and returned by values(...
void setContentTypesInt(const QList< int > &contentTypes)
Set the content types to contentTypes.
static QgsVectorLayer * layerFromCollectionId(const QgsServerApiContext &context, const QString &collectionId)
Returns a vector layer from the collectionId in the given context.
static QUrl sanitizeUrl(const QUrl &url)
Returns a sanitized url with extra slashes removed and the path URL component that always starts with...
static QString contentTypeToExtension(const QgsServerOgcApi::ContentType &ct)
Returns the file extension for a ct (Content-Type).
static const QMap< QgsServerOgcApi::ContentType, QStringList > contentTypeMimes()
Returns a map of contentType => list of mime types.
ContentType
Media types used for content negotiation, insert more specific first.
@ OPENAPI3
"application/openapi+json;version=3.0"
static QString contentTypeToString(const QgsServerOgcApi::ContentType &ct)
Returns the string representation of a ct (Content-Type) attribute.
Rel
Rel link types.
@ alternate
Refers to a substitute for this context.
@ self
Conveys an identifier for the link’s context.
static std::string contentTypeToStdString(const QgsServerOgcApi::ContentType &ct)
Returns the string representation of a ct (Content-Type) attribute.
static std::string mimeType(const QgsServerOgcApi::ContentType &contentType)
Returns the mime-type for the contentType or an empty string if not found.
static std::string relToString(const QgsServerOgcApi::Rel &rel)
Returns the string representation of rel attribute.
static const QHash< QgsServerOgcApi::ContentType, QList< QgsServerOgcApi::ContentType > > contentTypeAliases()
Returns contentType specializations (e.g.
static QgsServerOgcApi::ContentType contentTypeFromExtension(const std::string &extension)
Returns the Content-Type value corresponding to extension.
Defines requests passed to QgsService classes.
virtual QString header(const QString &name) const
Returns the header value.
QUrl url() const
Returns the request URL as seen by QGIS server.
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
virtual void setStatusCode(int code)=0
Set the http status code.
QString apiResourcesDirectory() const
Returns the server-wide base directory where HTML templates and static assets (e.g.
Represents a vector layer which manages a vector based dataset.