Оригинал статьи: X-Road: Message Protocol for REST
Автор: Владимир Недорослев
Все REST сервисы должны быть описаны в соответствии со стандартом OPENAPI3. Пример правильно описанного сервиса смотрите в приложении.
Используется протокол HTTP версии 1.1.
Участник/Подсистема отправителя указываются в заголовке HTTP. Используемый сервис указывается в самом URL в виде параметров.
{http-request-method} /{protocol-version}/{serviceId}[/path][?query-parameters]
[X-Road instance]/[member class]/[member code]/[subsystem code]/[service code]
.В каждом запросе должен присутствовать следующий заголовок HTTP:
X-Road-Client: {client}
{client} — информация о том, кто обращается к сервису. Состоит из следующих частей:
[X-Road instance]/[member class]/[member code]/[subsystem code]
[Subsystem code] является не обязательным.
Например:
X-Road-Client: central-server/GOV/70000004/e-services
При ответе Сервер Безопасности устанавливает следующие заголовки HTTP:
Например:
X-Road-Client: central-server/GOV/70000004/e-services X-Road-Service: central-server/GOV/70000002/gns-service/GnsTransportByPin/v1 X-Road-Id: fa2e18a5-c2cb-4d09-b994-f57727f7c3fb X-Road-Request-Hash: 4c519cf0-0e5e-4ccf-b72b-8ed6fe289e6e X-Road-Request-Id: f92591a3-6bf0-49b1-987b-0dd78c034cc3
Если Вы обращаетесь к сервису, то рекомендуется включать заголовок Accept, позволяющий указать в каком формате Вы хотите получить ответ. Например:
Accept: application/xml
Если заголовок Accept не указан явно, то Сервер Безопасности самостоятельно ставит значение:
application/json
Пример запроса:
GET /r1/INSTANCE/CLASS2/MEMBER2/SUBSYSTEM2/BARSERVICE/v1/bar/zyggy?some_parameter=1&another_parameter=test
Чтобы отправить запрос к определенному Серверу Безопасности, необходимо включить в запрос следующий заголовок HTTP:
X-Road-Security-Server: [X-Road instance]/[member class]/[member code]/[server code]
В случае возникновении ошибки во время обработки запроса или же на стороне X-Road, в ответ включается следующий заголовок:
X-Road-Error: [error type]
Заголовок будет содержать только тип ошибки. Детальная информация, как код ответа HTTP, тело сообщения об ошибки и т.д. будет находиться в теле ответа.
Все возникающие ошибки между клиентом и Сервером Безопасности можно поделить на 4 типа:
Для того чтобы различить, на чьей стороне возникла ошибка при коде статуса 500, необходимо обращать внимание на заголовок X-Road-Error: Если заголовок X-Road-Error отсутствует, то ошибка возникла на стороне сервиса, иначе – со стороны X-Road. В таком случае, дополнительная информация отображается в поле type в теле ответа. Например:
Server.ServerProxy.ServiceFailed
означает, что Сервер Безопасности не получил ответ от сервиса.Server.ServerProxy.DatabaseError
означает, что на Сервере Безопасности возникла внутренняя ошибка.Server.ClientProxy.OutdatedGlobalConf
указывает на проблему со стороны обратившегося Сервера Безопасности.
1. Всё сработало на стороне Сервера Безопасности, но сервис вернул ошибку.
HTTP status code: 405
Response body:
"timestamp":"2019-03-21T09:45:19.904Z", "status":405, "error":"Method Not Allowed", "message":"Request method 'PUT' not supported", "path":"/v3/pet/findByStatus"
HTTP headers:
Content-Type: application/json;charset=utf-8 Date: Thu, 21 Mar 2019 09:45:19 GMT x-road-id: 5ea48ae9-15c1-465a-be15-9b6ef2c7ef4a x-road-client: DEV/COM/222/TESTCLIENT x-road-service: DEV/COM/222/TESTSERVICE/petstore x-road-request-id: f92591a3-6bf0-49b1-987b-0dd78c034cc3 x-road-request-hash: yFOLGuJ0zmLhZSgwp3ooSBQbR9ejSvTc6p6FvBmcSEB2tDD6bxpjiv8sHORxqz4MMgEADH7IcARNprLfEwudNw== Content-Length: 159
2. Всё сработало на стороне Сервера Безопасности, но на стороне сервиса возникли технические неполадки
HTTP status code: 500
Response body:
{ "type":"Server.ServerProxy.NetworkError", "message":"Connect to 10.139.178.1:8080 [/10.139.178.1] failed: Connection timed out (Connection timed out)", "detail":"9bc95b6e-2f1d-4a41-a7e6-11eda7d734d5" }
HTTP headers:
Date: Thu, 21 Mar 2019 11:42:03 GMT Content-Type: application/json;charset=utf-8 X-Road-Error: Server.ServerProxy.NetworkError Content-Length: 199
3. Клиент послал запрос, не соответствующий протоколу сообщений X-Road для REST
HTTP status code: 400 Response body:
{ "type": "Client.BadRequest", "message": "Error parsing the client's REST request. Please that the request format corresponds to the X-Road Message Protocol for REST (r1).", "detail": "018cbcae-537e-421b-b6f6-2608dc97bd90" }
HTTP headers:
Date: Thu, 21 Mar 2019 11:45:12 GMT Content-Type: application/json;charset=utf-8 X-Road-Error: Client.BadRequest Content-Length: 167
4. На стороне сервера безопасности возникли технические неполадки
HTTP status code: 500
Response body:
{ "type":"Server.ServerProxy.DatabaseError", "message":"Error accessing database (serverconf)",с "detail":"3c4d0f08-440f-417f-b935-bc801e103d51" }
HTTP headers:
Date: Thu, 21 Mar 2019 11:57:11 GMT Content-Type: application/json;charset=utf-8 X-Road-Error: Server.ServerProxy.DatabaseError Content-Length: 141''
Сервис магазина питомцев описан по стандарту OPENAPI3. Полное описание сервиса смотрите в Приложении 1.
Пример предполагает, что serviceID привязан к https://petstore.niis.org/.
Обратите внимание: каждый запрос должен содержать аттрибут {protocol-version} (см.
Формат запроса).
Например: r1
Сервис - /pets/{petId} Метод - GET. Описание - Находит питомца по ID. Параметры - PetId – ID питомца.
Вызов сервиса через X-Road:
curl -X GET "https://{securityserver}/r1/{serviceId}/v2/pets/1124" -H "accept: application/json" -H "X-Road-Client: {client}"
Ответ:
{ "id": 1124, "name": "Siddu", "photoUrls": [], "tags": [], "status": "Offline" }
Код статуса: 200
Заголовки:
Content-Type: application/json;charset=utf-8 Date: Thu, 21 Mar 2019 12:36:39 GMT x-road-id: 29f4d011-ef17-4f2f-9bb1-0452ce17d3f5 x-road-client: DEV/COM/222/TESTCLIENT x-road-service: DEV/COM/222/TESTSERVICE/petstore x-road-request-id: f92591a3-6bf0-49b1-987b-0dd78c034cc3 x-road-request-hash: Xvx9V2U5c5RhDUiXpVLtW7L8vTd5cM2IOBU2n9efEk7/m3ECKyGAp7yTpJpTWpo6HcmwSaGO+cinxMVKjxJTOQ== Content-Length: 1148
Сервис - /pets/{petId} Метод - PUT Описание - Обновляет питомца по ID. Параметры - body – объект Pet, который будет обновлён.
Вызов сервиса через X-Road:
curl -X PUT "https://{securityserver}/r1/{serviceId}/v2/pets/5657082955040009" -H "accept: application/json" -H "Content-Type: application/json" -H "X-Road-Client: {client}" -d '{ "id": 0, "category": { "id": 0, "name": "string" }, "name": "doggie", "photoUrls": [ "string" ], "tags": [ { "id": 0, "name": "string" } ], "status": "available"}'
Ответ:
{ "id": 5657082955040009, "category": { "id": 0, "name": "string" }, "name": "doggie", "photoUrls": [ "string" ], "tags": [ { "id": 0, "name": "string" } ], "status": "available" }
Код статуса: 200
Заголовки:
Date: Thu, 21 Mar 2019 12:43:33 GMT x-road-id: acdb2c7a-c705-41c2-b595-4cd62e78316e x-road-client: DEV/COM/222/TESTCLIENT x-road-service: DEV/COM/222/TESTSERVICE/petstore x-road-request-id: f92591a3-6bf0-49b1-987b-0dd78c034cc3 x-road-request-hash: MOEfTqBjdqYiX3db9hxJ6JvHvCpYqfA6t0Uhdv6g2I29fMY8ld4CbN8tslj6mUQPXoRaUdPm7NdZeAYTg6zi+A== Content-Length: 0
Сервис - /pets/{petId} Метод - POST Описание - Добавляет нового питомца. Параметры - body – объект Pet, который будет добавлен.
Вызов сервиса через X-Road:
curl -X POST "https://{securityserver}/r1/{serviceId}/v2/pets" -H "accept: application/json" -H "Content-Type: application/json" -H "X-Road-Client: {client}" -d '{ "id": 0, "category": { "id": 0, "name": "string" }, "name": "doggie", "photoUrls": [ "string" ], "tags": [ { "id": 0, "name": "string" } ], "status": "available"}'
Ответ:
{ "id": 5657082955040122, "category": { "id": 0, "name": "string" }, "name": "doggie", "photoUrls": [ "string" ], "tags": [ { "id": 0, "name": "string" } ], "status": "available" }
Код статуса: 200
Заголовки:
Date: Thu, 21 Mar 2019 12:49:38 GMT x-road-id: dcaaa3a2-a158-41e1-8775-309848052358 x-road-client: DEV/COM/222/TESTCLIENT x-road-service: DEV/COM/222/TESTSERVICE/petstore x-road-request-id: f92591a3-6bf0-49b1-987b-0dd78c034cc3 x-road-request-hash: VCNZdwTxl7m3XC6Mpfw1H6qJUtBcm3Y6tfCvg5b3W/fb2RRXsLF9wftR3u6ElclE+RFaiAN/OkSz02fAYbNKaw== Content-Length: 0
Сервис - /pets/{petId}/images Метод - POST Описание - Загружает изображение питомца. Параметры: • PetID – Id питомца. • AdditionalMetaData – дополнительная информация. • File – загружаемый файл.
Вызов сервиса через X-Road:
curl -X POST "https://{securityserver}/r1/{serviceId}/v2/pets/1124/images" -H "accept: application/json" -H "Content-Type: multipart/form-data" -H "X-Road-client: {client}" -F "file=@A-fluffy-cat-looking-funny-surprised-or-concerned.jpg;type=image/jpeg"
Ответ:
{ "code":200, "type":null, "message":"additionalMetadata: null\nFile uploaded to ./file, 170025 bytes" }
Код статуса: 200
Заголовки:
Content-Type: application/json;charset=utf-8 Date: Thu, 21 Mar 2019 13:02:29 GMT x-road-id: 86e081a6-ec16-4b8d-b729-963f9659a80c x-road-client: DEV/COM/222/TESTCLIENT x-road-service: DEV/COM/222/TESTSERVICE/petstore x-road-request-id: f92591a3-6bf0-49b1-987b-0dd78c034cc3 x-road-request-hash: EycIkZAz4WMvbKgnBvd0wUcN4A4w0RZMvugD36ZJ2PpwwGZuMGfxCoO4C0ZC3c4LBGF0rh61vunL3ssZV6TB3Q== Content-Length: 100
Сервис - /pets/{petId} Метод - DELETE. Описание - Удаляет питомца. Параметры - PetId – Id питомца.
Вызов сервиса через X-Road:
curl -X DELETE "https://{securityserver}/r1/{serviceId}/v2/pets/1124" -H "accept: application/json" -H "X-Road-Client: {client}"
Ответ: <пусто>
Код статуса: 200
Заголовки:
Date: Thu, 21 Mar 2019 12:49:38 GMT x-road-id: 6209d61b-6ab5-4443-a09a-b8d2a7c491b2 x-road-client: DEV/COM/222/TESTCLIENT x-road-service: DEV/COM/222/TESTSERVICE/petstore x-road-request-id: f92591a3-6bf0-49b1-987b-0dd78c034cc3 x-road-request-hash: lQBoldcyuI3BerjHfkleRQ45AyYoFlF7zXSN6yH/RwvTNWEcsTQM18EfqMxYfdkyGGB26oxAjAWv/AcfmZF7og== Content-Length: 0
openapi: 3.0.0 info: description: >- This is a sample server Petstore server. version: 1.0.0 title: Petstore contact: email: info@niis.org license: name: Apache 2.0 url: 'http://www.apache.org/licenses/LICENSE-2.0.html' tags: - name: pet description: Everything about your Pets externalDocs: description: Find out more url: 'https://niis.org' - name: store description: Access to Petstore orders - name: user description: Operations about user externalDocs: description: Find out more about our store url: 'https://niis.org' paths: /pets: get: tags: - pet summary: Get pets from store description: Search pets operationId: getPets parameters: - name: term in: query description: search term required: false schema: type: string responses: '200': description: successful operation content: application/xml: schema: type: array items: $ref: '#/components/schemas/Pet' application/json: schema: type: array items: $ref: '#/components/schemas/Pet' '400': description: Invalid ID supplied '404': description: Pet not found security: - api_key: [] post: tags: - pet summary: Add a new pet to the store description: '' operationId: addPet responses: '201': description: pet created '405': description: Invalid input security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: $ref: '#/components/requestBodies/Pet' '/pets/{petId}': get: tags: - pet summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' '400': description: Invalid ID supplied '404': description: Pet not found security: - api_key: [] put: tags: - pet summary: Update an existing pet description: '' operationId: updatePet parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: '200': description: Pet updated '400': description: Invalid ID supplied '404': description: Pet not found '405': description: Validation exception security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: $ref: '#/components/requestBodies/Pet' delete: tags: - pet summary: Deletes a pet description: '' operationId: deletePet parameters: - name: api_key in: header required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: '200': description: Pet deleted '400': description: Invalid ID supplied '404': description: Pet not found security: - petstore_auth: - 'write:pets' - 'read:pets' '/pets/{petId}/images': post: tags: - pet summary: Uploads an image description: '' operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: content: multipart/form-data: schema: type: object properties: additionalMetadata: description: Additional data to pass to server type: string file: description: file to upload type: string format: binary /store/inventories: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: '200': description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] /store/orders: post: tags: - store summary: Place an order for a pet description: '' operationId: placeOrder responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Order' application/json: schema: $ref: '#/components/schemas/Order' '400': description: Invalid Order requestBody: content: application/json: schema: $ref: '#/components/schemas/Order' description: order placed for purchasing the pet required: true '/store/orders/{orderId}': get: tags: - store summary: Find purchase order by ID description: >- For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of pet that needs to be fetched required: true schema: type: integer format: int64 minimum: 1 maximum: 10 responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Order' application/json: schema: $ref: '#/components/schemas/Order' '400': description: Invalid ID supplied '404': description: Order not found delete: tags: - store summary: Delete purchase order by ID description: >- For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: integer format: int64 minimum: 1 responses: '200': description: Purchase order deleted '400': description: Invalid ID supplied '404': description: Order not found /users: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser responses: default: description: successful operation requestBody: content: application/json: schema: $ref: '#/components/schemas/User' description: Created user object required: true /users/login: get: tags: - user summary: Logs user into the system description: '' operationId: loginUser parameters: - name: username in: query description: The user name for login required: true schema: type: string - name: password in: query description: The password for login in clear text required: true schema: type: string responses: '200': description: successful operation headers: X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time content: application/xml: schema: type: string application/json: schema: type: string '400': description: Invalid username/password supplied /users/logout: get: tags: - user summary: Logs out current logged in user session description: '' operationId: logoutUser responses: default: description: successful operation '/users/{username}': get: tags: - user summary: Get user by user name description: '' operationId: getUserByName parameters: - name: username in: path description: 'The name that needs to be fetched. Use user1 for testing. ' required: true schema: type: string responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/User' application/json: schema: $ref: '#/components/schemas/User' '400': description: Invalid username supplied '404': description: User not found put: tags: - user summary: Updated user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be updated required: true schema: type: string responses: '200': description: User updated '400': description: Invalid user supplied '404': description: User not found requestBody: content: application/json: schema: $ref: '#/components/schemas/User' description: Updated user object required: true delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: '200': description: User deleted '400': description: Invalid username supplied '404': description: User not found externalDocs: description: Find out more url: 'https://niis.org' servers: - url: 'https://petstore.niis.org/v2' - url: 'http://petstore.niis.org/v2' components: requestBodies: UserArray: content: application/json: schema: type: array items: $ref: '#/components/schemas/User' description: List of user object required: true Pet: content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' description: Pet object that needs to be added to the store required: true securitySchemes: petstore_auth: type: oauth2 flows: implicit: authorizationUrl: 'http://petstore.niis.org/oauth/dialog' scopes: 'write:pets': modify pets in your account 'read:pets': read your pets api_key: type: apiKey name: api_key in: header schemas: Order: type: object properties: id: type: integer format: int64 petId: type: integer format: int64 quantity: type: integer format: int32 shipDate: type: string format: date-time status: type: string description: Order Status enum: - placed - approved - delivered complete: type: boolean default: false xml: name: Order Category: type: object properties: id: type: integer format: int64 name: type: string xml: name: Category User: type: object properties: id: type: integer format: int64 username: type: string firstName: type: string lastName: type: string email: type: string password: type: string phone: type: string userStatus: type: integer format: int32 description: User Status xml: name: User Tag: type: object properties: id: type: integer format: int64 name: type: string xml: name: Tag Pet: type: object required: - name - photoUrls properties: id: type: integer format: int64 category: $ref: '#/components/schemas/Category' name: type: string example: doggie photoUrls: type: array xml: name: photoUrl wrapped: true items: type: string tags: type: array xml: name: tag wrapped: true items: $ref: '#/components/schemas/Tag' status: type: string description: pet status in the store enum: - available - pending - sold xml: name: Pet ApiResponse: type: object properties: code: type: integer format: int32 type: type: string message: type: string