QGIS API Documentation 4.1.0-Master (376402f9aeb)
Loading...
Searching...
No Matches
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
18
19#include <nlohmann/json.hpp>
20
21#include "inja/inja.hpp"
22#include "qgsjsonutils.h"
23#include "qgsmessagelog.h"
24#include "qgsproject.h"
25#include "qgsserverapiutils.h"
26#include "qgsserverinterface.h"
27#include "qgsserverresponse.h"
28#include "qgsvectorlayer.h"
29
30#include <QDateTime>
31#include <QDebug>
32#include <QDir>
33#include <QFileInfo>
34#include <QString>
35
36using namespace Qt::StringLiterals;
37
38using namespace nlohmann;
39using namespace inja;
40
41
42QVariantMap QgsServerOgcApiHandler::values( const QgsServerApiContext &context ) const
43{
44 QVariantMap result;
45 const auto constParameters { parameters( context ) };
46 for ( const auto &p : constParameters )
47 {
48 // value() calls the validators and throws an exception if validation fails
49 result[p.name()] = p.value( context );
50 }
51 const auto sanitizedPath { QgsServerOgcApi::sanitizeUrl( context.handlerPath() ).path() };
52 const auto match { path().match( sanitizedPath ) };
53 if ( match.hasMatch() )
54 {
55 const auto constNamed { path().namedCaptureGroups() };
56 // Get named path parameters
57 for ( const auto &name : constNamed )
58 {
59 if ( !name.isEmpty() )
60 result[name] = QUrlQuery( match.captured( name ) ).toString();
61 }
62 }
63 return result;
64}
65
67{
68 //qDebug() << "handler destroyed";
69}
70
72{
73 const auto constContentTypes( contentTypes() );
74 return constContentTypes.size() > 0 ? constContentTypes.first() : QgsServerOgcApi::ContentType::JSON;
75}
76
77QList<QgsServerOgcApi::ContentType> QgsServerOgcApiHandler::contentTypes() const
78{
79 return mContentTypes;
80}
81
83{
84 Q_UNUSED( context )
85 throw QgsServerApiNotImplementedException( u"Subclasses must implement handleRequest"_s );
86}
87
88QString QgsServerOgcApiHandler::contentTypeForAccept( const QString &accept ) const
89{
90 const auto constContentTypes( QgsServerOgcApi::contentTypeMimes() );
91 for ( auto it = constContentTypes.constBegin(); it != constContentTypes.constEnd(); ++it )
92 {
93 const auto constValues = it.value();
94 for ( const auto &value : constValues )
95 {
96 if ( accept.contains( value, Qt::CaseSensitivity::CaseInsensitive ) )
97 {
98 return value;
99 }
100 }
101 }
102 // Log level info because this is not completely unexpected
103 QgsMessageLog::logMessage( u"Content type for accept %1 not found!"_s.arg( accept ), u"Server"_s, Qgis::MessageLevel::Info );
104
105 return QString();
106}
107
108void QgsServerOgcApiHandler::write( json &data, const QgsServerApiContext &context, const json &htmlMetadata ) const
109{
110 const QgsServerOgcApi::ContentType contentType { contentTypeFromRequest( context.request() ) };
111 switch ( contentType )
112 {
114 data["handler"] = schema( context );
115 if ( !htmlMetadata.is_null() )
116 {
117 data["metadata"] = htmlMetadata;
118 }
119 htmlDump( data, context );
120 break;
124 jsonDump( data, context, QgsServerOgcApi::contentTypeMimes().value( contentType ).first() );
125 break;
127 // Not handled yet
128 break;
129 }
130}
131
132void QgsServerOgcApiHandler::write( QVariant &data, const QgsServerApiContext &context, const QVariantMap &htmlMetadata ) const
133{
134 json j = QgsJsonUtils::jsonFromVariant( data );
135 json jm = QgsJsonUtils::jsonFromVariant( htmlMetadata );
136 QgsServerOgcApiHandler::write( j, context, jm );
137}
138
139std::string QgsServerOgcApiHandler::href( const QgsServerApiContext &context, const QString &extraPath, const QString &extension ) const
140{
141 QUrl url { context.request()->url() };
142 QString urlBasePath { context.matchedPath() };
143 const auto match { path().match( QgsServerOgcApi::sanitizeUrl( context.handlerPath() ).path() ) };
144 if ( match.captured().count() > 0 )
145 {
146 url.setPath( urlBasePath + match.captured( 0 ) );
147 }
148 else
149 {
150 url.setPath( urlBasePath );
151 }
152
153 // Remove any existing extension
154 const auto suffixLength { QFileInfo( url.path() ).suffix().length() };
155 if ( suffixLength > 0 )
156 {
157 auto path { url.path() };
158 path.truncate( path.length() - ( suffixLength + 1 ) );
159 url.setPath( path );
160 }
161
162 // Add extra path
163 url.setPath( url.path() + extraPath );
164
165 // (re-)add extension
166 // JSON is the default anyway so we don't need to add it
167 if ( !extension.isEmpty() )
168 {
169 // Remove trailing slashes if any.
170 QString path { url.path() };
171 while ( path.endsWith( '/' ) )
172 {
173 path.chop( 1 );
174 }
175 url.setPath( path + '.' + extension );
176 }
177 return QgsServerOgcApi::sanitizeUrl( url ).toString( QUrl::FullyEncoded ).toStdString();
178}
179
180void QgsServerOgcApiHandler::jsonDump( json &data, const QgsServerApiContext &context, const QString &contentType ) const
181{
182 // Do not append timestamp to openapi
183 if ( !QgsServerOgcApi::contentTypeMimes().value( QgsServerOgcApi::ContentType::OPENAPI3 ).contains( contentType, Qt::CaseSensitivity::CaseInsensitive ) )
184 {
185 QDateTime time { QDateTime::currentDateTime() };
186 time.setTimeSpec( Qt::TimeSpec::UTC );
187 data["timeStamp"] = time.toString( Qt::DateFormat::ISODate ).toStdString();
188 }
189 context.response()->setStatusCode( 200 );
190 context.response()->setHeader( u"Content-Type"_s, contentType );
191#ifdef QGISDEBUG
192 context.response()->write( data.dump( 2 ) );
193#else
194 context.response()->write( data.dump() );
195#endif
196}
197
199{
200 Q_UNUSED( context )
201 return nullptr;
202}
203
204json QgsServerOgcApiHandler::link( const QgsServerApiContext &context, const QgsServerOgcApi::Rel &linkType, const QgsServerOgcApi::ContentType contentType, const std::string &title ) const
205{
206 json l {
207 { "href", href( context, "/", QgsServerOgcApi::contentTypeToExtension( contentType ) ) },
209 { "type", QgsServerOgcApi::mimeType( contentType ) },
210 { "title", title != "" ? title : linkTitle() },
211 };
212 return l;
213}
214
216{
217 const QgsServerOgcApi::ContentType currentCt { contentTypeFromRequest( context.request() ) };
218 json links = json::array();
219 const QList<QgsServerOgcApi::ContentType> constCts { contentTypes() };
220 for ( const auto &ct : constCts )
221 {
222 links.push_back( link( context, ( ct == currentCt ? QgsServerOgcApi::Rel::self : QgsServerOgcApi::Rel::alternate ), ct, linkTitle() + " as " + QgsServerOgcApi::contentTypeToStdString( ct ) ) );
223 }
224 return links;
225}
226
228{
229 if ( !context.project() )
230 {
231 throw QgsServerApiImproperlyConfiguredException( u"Project is invalid or undefined"_s );
232 }
233 // Check collectionId
234 const QRegularExpressionMatch match { path().match( context.request()->url().path() ) };
235 if ( !match.hasMatch() )
236 {
237 throw QgsServerApiNotFoundError( u"Collection was not found"_s );
238 }
239 const QString collectionId { match.captured( u"collectionId"_s ) };
240 // May throw if not found
241 return layerFromCollectionId( context, collectionId );
242}
243
244const QString QgsServerOgcApiHandler::staticPath( const QgsServerApiContext &context ) const
245{
246 // resources/server/api + /static
247 return context.serverInterface()->serverSettings()->apiResourcesDirectory() + u"/ogc/static"_s;
248}
249
251{
252 // resources/server/api + /ogc/templates/ + apiRootPath() + / + operationId() + .html
253 QString path { context.serverInterface()->serverSettings()->apiResourcesDirectory() };
254 path += "/ogc/templates"_L1;
255 path += context.apiRootPath();
256 path += '/';
257 path += QString::fromStdString( operationId() );
258 path += ".html"_L1;
259 return path;
260}
261
262void QgsServerOgcApiHandler::htmlDump( const json &data, const QgsServerApiContext &context ) const
263{
264 context.response()->setHeader( u"Content-Type"_s, u"text/html"_s );
265 auto path { templatePath( context ) };
266 if ( !QFile::exists( path ) )
267 {
268 QgsMessageLog::logMessage( u"Template not found error: %1"_s.arg( path ), u"Server"_s, Qgis::MessageLevel::Critical );
269 throw QgsServerApiBadRequestException( u"Template not found: %1"_s.arg( QFileInfo( path ).fileName() ) );
270 }
271
272 QFile f( path );
273 if ( !f.open( QFile::ReadOnly | QFile::Text ) )
274 {
275 QgsMessageLog::logMessage( u"Could not open template file: %1"_s.arg( path ), u"Server"_s, Qgis::MessageLevel::Critical );
276 throw QgsServerApiInternalServerError( u"Could not open template file: %1"_s.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, [data]( Arguments & ) { return data.dump(); } );
287
288 // Path manipulation: appends a directory path to the current url
289 env.add_callback( "path_append", 1, [context]( Arguments &args ) {
290 auto url { context.request()->url() };
291 QFileInfo fi { url.path() };
292 auto suffix { fi.suffix() };
293 auto fName { fi.filePath() };
294 if ( !suffix.isEmpty() )
295 {
296 fName.chop( suffix.length() + 1 );
297 }
298 // Chop any ending slashes
299 while ( fName.endsWith( '/' ) )
300 {
301 fName.chop( 1 );
302 }
303 fName += '/' + QString::fromStdString( args.at( 0 )->get<std::string>() );
304 if ( !suffix.isEmpty() )
305 {
306 fName += '.' + suffix;
307 }
308 fi.setFile( fName );
309 url.setPath( fi.filePath() );
310 return url.toString().toStdString();
311 } );
312
313 // Path manipulation: removes the specified number of directory components from the current url path
314 env.add_callback( "path_chomp", 1, []( Arguments &args ) {
315 QUrl url { QString::fromStdString( args.at( 0 )->get<std::string>() ) };
316 QFileInfo fi { url.path() };
317 auto suffix { fi.suffix() };
318 auto fName { fi.filePath() };
319 fName.chop( suffix.length() + 1 );
320 // Chomp last segment
321 const thread_local QRegularExpression segmentRx( R"raw(\/[^/]+$)raw" );
322 fName = fName.replace( segmentRx, QString() );
323 if ( !suffix.isEmpty() )
324 {
325 fName += '.' + suffix;
326 }
327 fi.setFile( fName );
328 url.setPath( fi.filePath() );
329 return url.toString().toStdString();
330 } );
331
332 // Returns filtered links from a link list
333 // links_filter( <links>, <key>, <value> )
334 env.add_callback( "links_filter", 3, []( Arguments &args ) {
335 json links = args.at( 0 )->get<json>();
336 if ( !links.is_array() )
337 {
338 links = json::array();
339 }
340 std::string key { args.at( 1 )->get<std::string>() };
341 std::string value { args.at( 2 )->get<std::string>() };
342 json result = json::array();
343 for ( const auto &l : links )
344 {
345 if ( l[key] == value )
346 {
347 result.push_back( l );
348 }
349 }
350 return result;
351 } );
352
353 // Returns a short name from content types
354 env.add_callback( "content_type_name", 1, []( Arguments &args ) {
355 const QgsServerOgcApi::ContentType ct { QgsServerOgcApi::contentTypeFromExtension( args.at( 0 )->get<std::string>() ) };
357 } );
358
359 // Replace newlines with <br>
360 env.add_callback( "nl2br", 1, []( Arguments &args ) {
361 QString text { QString::fromStdString( args.at( 0 )->get<std::string>() ) };
362 return text.replace( '\n', "<br>"_L1 ).toStdString();
363 } );
364
365
366 // Returns a list of parameter component data from components -> parameters by ref name
367 // parameter( <ref object> )
368 env.add_callback( "component_parameter", 1, [data]( Arguments &args ) {
369 json ret = json::array();
370 json ref = args.at( 0 )->get<json>();
371 if ( !ref.is_object() )
372 {
373 return ret;
374 }
375 try
376 {
377 QString name = QString::fromStdString( ref["$ref"] );
378 name = name.split( '/' ).last();
379 ret.push_back( data["components"]["parameters"][name.toStdString()] );
380 }
381 catch ( std::exception & )
382 {
383 // Do nothing
384 }
385 return ret;
386 } );
387
388
389 // Static: returns the full URL to the specified static <path>
390 env.add_callback( "static", 1, [context]( Arguments &args ) {
391 auto asset( args.at( 0 )->get<std::string>() );
392 QString matchedPath { context.matchedPath() };
393 // If its the root path '/' strip it!
394 if ( matchedPath == '/' )
395 {
396 matchedPath.clear();
397 }
398 return matchedPath.toStdString() + "/static/" + asset;
399 } );
400
401
402 // Returns true if a string begins with the provided string prefix, false otherwise
403 env.add_callback( "starts_with", 2, []( Arguments &args ) { return string_view::starts_with( args.at( 0 )->get<std::string_view>(), args.at( 1 )->get<std::string_view>() ); } );
404
405 // Returns "null" string if object is null else string object representation
406 env.add_callback( "if_nullptr_null_str", 1, []( Arguments &args ) {
407 json jsonValue = args.at( 0 )->get<json>();
408 std::string out;
409 switch ( jsonValue.type() )
410 {
411 // avoid escaping string value
412 case json::value_t::string:
413 out = jsonValue.get<std::string>();
414 break;
415
416 case json::value_t::array:
417 case json::value_t::object:
418 if ( jsonValue.is_null() )
419 {
420 out = "null";
421 }
422 else
423 {
424 out = jsonValue.dump();
425 }
426
427 break;
428
429 // use dump() for all other value types
430 default:
431 out = jsonValue.dump();
432 }
433 return out;
434 } );
435
436 context.response()->write( env.render_file( pathInfo.fileName().toStdString(), data ) );
437 }
438 catch ( std::exception &e )
439 {
440 QgsMessageLog::logMessage( u"Error parsing template file: %1 - %2"_s.arg( path, e.what() ), u"Server"_s, Qgis::MessageLevel::Critical );
441 throw QgsServerApiInternalServerError( u"Error parsing template file: %1"_s.arg( e.what() ) );
442 }
443}
444
446{
447 // Fallback to default
449 bool found { false };
450 // First file extension ...
451 const QString extension { QFileInfo( request->url().path() ).suffix().toUpper() };
452 if ( !extension.isEmpty() )
453 {
454 static QMetaEnum metaEnum { QMetaEnum::fromType<QgsServerOgcApi::ContentType>() };
455 bool ok { false };
456 const int ct { metaEnum.keyToValue( extension.toLocal8Bit().constData(), &ok ) };
457 if ( ok )
458 {
459 result = static_cast<QgsServerOgcApi::ContentType>( ct );
460 found = true;
461 }
462 else
463 {
464 QgsMessageLog::logMessage( u"The client requested an unsupported extension: %1"_s.arg( extension ), u"Server"_s, Qgis::MessageLevel::Warning );
465 }
466 }
467 // ... then "Accept"
468 const QString accept { request->header( u"Accept"_s ) };
469 if ( !found && !accept.isEmpty() )
470 {
471 const QString ctFromAccept { contentTypeForAccept( accept ) };
472 if ( !ctFromAccept.isEmpty() )
473 {
474 const auto constContentTypes( QgsServerOgcApi::contentTypeMimes() );
475 auto it = constContentTypes.constBegin();
476 while ( !found && it != constContentTypes.constEnd() )
477 {
478 int idx = it.value().indexOf( ctFromAccept );
479 if ( idx >= 0 )
480 {
481 found = true;
482 result = it.key();
483 }
484 it++;
485 }
486 }
487 else
488 {
489 QgsMessageLog::logMessage( u"The client requested an unsupported content type in Accept header: %1"_s.arg( accept ), u"Server"_s, Qgis::MessageLevel::Warning );
490 }
491 }
492 // Validation: check if the requested content type (or an alias) is supported by the handler
493 if ( !contentTypes().contains( result ) )
494 {
495 // Check aliases
496 bool found { false };
497 if ( QgsServerOgcApi::contentTypeAliases().contains( result ) )
498 {
499 const QList<QgsServerOgcApi::ContentType> constCt { contentTypes() };
500 for ( const auto &ct : constCt )
501 {
502 if ( QgsServerOgcApi::contentTypeAliases()[result].contains( ct ) )
503 {
504 result = ct;
505 found = true;
506 break;
507 }
508 }
509 }
510
511 if ( !found )
512 {
513 QgsMessageLog::logMessage( u"Unsupported Content-Type: %1"_s.arg( QgsServerOgcApi::contentTypeToString( result ) ), u"Server"_s, Qgis::MessageLevel::Info );
514 throw QgsServerApiBadRequestException( u"Unsupported Content-Type: %1"_s.arg( QgsServerOgcApi::contentTypeToString( result ) ) );
515 }
516 }
517 return result;
518}
519
520QString QgsServerOgcApiHandler::parentLink( const QUrl &url, int levels )
521{
522 QString path { url.path() };
523 const QFileInfo fi { path };
524 const QString suffix { fi.suffix() };
525 if ( !suffix.isEmpty() )
526 {
527 path.chop( suffix.length() + 1 );
528 }
529 while ( path.endsWith( '/' ) )
530 {
531 path.chop( 1 );
532 }
533 const thread_local QRegularExpression re( R"raw(\/[^/]+$)raw" );
534 for ( int i = 0; i < levels; i++ )
535 {
536 path = path.replace( re, QString() );
537 }
538 QUrl result( url );
539 QUrlQuery query( result );
540 QList<QPair<QString, QString>> qi;
541 const auto constItems { query.queryItems() };
542 for ( const auto &i : constItems )
543 {
544 if ( i.first.compare( u"MAP"_s, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
545 {
546 qi.push_back( i );
547 }
548 }
549 // Make sure the parent link ends with a slash
550 if ( !path.endsWith( '/' ) )
551 {
552 path.append( '/' );
553 }
554 QUrlQuery resultQuery;
555 resultQuery.setQueryItems( qi );
556 result.setQuery( resultQuery );
557 result.setPath( path );
558 return result.toString();
559}
560
562{
563 const auto mapLayers { context.project()->mapLayersByShortName<QgsVectorLayer *>( collectionId ) };
564 if ( mapLayers.count() != 1 )
565 {
566 throw QgsServerApiNotFoundError( u"Collection with given id (%1) was not found or multiple matches were found"_s.arg( collectionId ) );
567 }
568 return mapLayers.first();
569}
570
572{
573 static json defRes
574 = { { "description", "An error occurred." }, { "content", { { "application/json", { { "schema", { { "$ref", "#/components/schemas/exception" } } } } }, { "text/html", { { "schema", { { "type", "string" } } } } } } } };
575 return defRes;
576}
577
582
584{
585 mContentTypes.clear();
586 for ( const int &i : std::as_const( contentTypes ) )
587 {
588 mContentTypes.push_back( static_cast<QgsServerOgcApi::ContentType>( i ) );
589 }
590}
591
592void QgsServerOgcApiHandler::setContentTypes( const QList<QgsServerOgcApi::ContentType> &contentTypes )
593{
594 mContentTypes = contentTypes;
595}
@ Warning
Warning message.
Definition qgis.h:162
@ Critical
Critical/error message.
Definition qgis.h:163
@ Info
Information message.
Definition qgis.h:161
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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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.
void jsonDump(json &data, const QgsServerApiContext &context, const QString &contentType=u"application/json"_s) const
Writes data to the context response stream as JSON (indented if debug is active), an optional content...
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.
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.