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