3.1.5. Запросы в формате XML (SOAP)
3. Запросы из внешних систем 3.1. Запросы к данным 3.1.5. Запросы в формате XML (SOAP)
Рассмотрим обработку запросов в формате XML (SOAP) на основе базового примера. Для отправки запросов мы используем простые скрипты на языке Python. Для приёма и обработки запросов нам понадобится кластер TDG, настроенный ранее.
3.1.5.1. Адаптация конфигурации из базового примера
Для повторения примеров из данного раздела нам потребуется внести определенные исправления в конфигурацию системы, загруженную ранее.
Разархивируйте архив с конфигурацией системы в отдельную директорию.
Откройте файл config.yml
и измените его так, чтобы он содержал следующий текст:
types: __file: model.avsc functions: router: {__file: router.lua} classifier: {__file: classificator. lua} select_user_books: {__file: select_user_books.lua} process_user: {__file: process_user.lua} process_book: {__file: process_book.lua} process_sub: {__file: process_sub.lua} pipelines: router: - router classifier: - classifier select_user_books: - select_user_books process_user: - process_user process_book: - process_book process_sub: - process_sub connector: input: - name: soap type: soap wsdl: {__file: example.wsdl} handlers: - function: Connect pipeline: router routing: - key: input_key output: to_input_processor output: - name: to_input_processor type: input_processor input_processor: classifiers: - name: classifier pipeline: classifier routing: - key: process_user pipeline: process_user - key: process_book pipeline: process_book - key: process_sub pipeline: process_sub storage: - key: add_user type: User - key: add_book type: Book - key: add_subscription type: Subscription services: select_user_books: doc: "select_user_books" function: select_user_books return_type: string args: user_id: long
Обратите внимание на появившийся раздел input_processor
/routing
и связанные
с ним описания новых конвейеров обработки данных (pipeline) и функций. При этом
модель данных не изменилась. В разделе connector
появилось описание входа с
типом SOAP, функцией Connect
(не являющейся функцией TDG, но описываемой
в XML/SOAP) и связанным с ней конвейером (уже известным нам по примеру с JSON — router
).
Примечание
В секции wsdl:
обязательно указывается файл спецификации веб-сервиса с
расширением .wsdl
. В нашем примере это example.wsdl
. Данный файл должен
обязательно быть включен в состав архива с конфигурацией системы. В том случае,
если WSDL не используется, данный файл может быть пустым.
Теперь откройте файл classificator.lua
и отредактируйте его так, чтобы он имел
следующее содержимое:
#!/usr/bin/env tarantool local param = ... if (param.obj[1].tag == "username" or param.obj[2].tag == "username") then param.routing_key = "process_user" return param end if (param.obj[1].tag == "book_name" or param. obj[2].tag == "book_name") then param.routing_key = "process_book" return param end if ((param.obj[1].tag == "user_id" and param.obj[2] == "book_id") or (param.obj[2].tag == "user_id" and param.obj[1].tag == "book_id")) then param.routing_key = "process_sub" return param end param.routing_key = "unknown_type" return param
Создайте файлы process_user.lua
, process_book.lua
и process_sub.lua
.
Содержимое этих файлов — скрипт обработки данных, который должен формировать из
получаемого TDG информационного объекта объект, соответствующий описанному
в модели данных.
Далее приведен пример содержимого файла process_user.lua
.
Остальные файлы выполняются по аналогии.
#!/usr/bin/env tarantool local param = ... local id = param.obj[1][1] local username = param.obj[2][1] local data = {id = tonumber(id), username = username} local ret = {obj = data, priority = 1, routing_key = 'add_user'} return ret
Сформированный вышеуказанным скриптом объект должен содержать поля id
и username
,
так как он получит routing_key
для добавления объекта типа User
в хранилище.
Важно
Для сохранения TDG ожидает объект в формате, представленном выше,
где obj
включает в себя весь информационный объект в виде перечисленных
через запятую пар key = value
, где для каждого обязательного поля объекта
в модели данных задан одноимённый ключ (key) с непустым значением.
Закончив с подготовкой файлов, упакуйте их в zip-архив и загрузите его согласно инструкции.
3.1.5.2. Описание процесса обработки запроса
Логика обработки поступающего запроса изложена в файле конфигурации config.yml
и состоит в следующем:
Согласно разделу
connector
/input
файла конфигурацииconfig.yml
, SOAP (XML) запросы передаются на обработку конвейеруrouter
, который состоит из функцииrouter
;Функция
равным строкеrouter
ссылается на файлrouter.lua
, который упаковывает поступивший объект с ключомrouting_key
input_key
;Согласно разделу
connector
/routing
файла конфигурацииconfig. yml
, все объекты с ключомinput_key
передаютсяto_input_processor
;В секции
output
для разделаconnector
указана единственная записьto_input_processor
, которая переадресует запрос в разделinput_processor
для обработки на одноименной роли;В разделе
input_processor
все запросы попадают в секциюclassifiers
, где в нашем случае указан один единственный объект, вызывающий конвейер обработки объектов (pipeline)classifier
;Конвейер
classifier
вызывает одноименную функцию, которая описана в файлеclassificator.lua
. Как можно понять из названия, данная функция занимается классификацией поступающей информации. Логика ее работы следующая:при наличии тэга
username
у поступившего объекта — ему присваиваетсяrouting_key
=process_user
, то есть объект направляется для формирования объекта типа пользователь;при наличии тэга
book_name
у поступившего объекта — ему присваиваетсяrouting_key
=process_book
, то есть объект направляется для формирования объекта типа книга;при наличии тэгов
user_id
иbook_id
у поступившего объекта — ему присваиваетсяrouting_key
=process_sub
, то есть объект направляется для формирования объекта типа подписка;во всех остальных случаях объекту присваивается
, то есть объект не распознан. Такой объект обычно попадает в ремонтную очередь, но можно настроить и иное поведение;routing_key
=unknown_type
В разделе
input_processor
в секцииrouting
имеются указания по обработке объектов со следующимиrouting_key
:process_user
,process_book
иprocess_sub
при помощи одноименных функций. Каждая из этих функций формирует объект в нужном для сохранения формате и присваивает ему соответствующийrouting_key
;В секции
storage
описано сохранение данных в TDG.при значении
routing_key
равномadd_user
объект сохраняется какUser
;при значении
routing_key
равномadd_book
объект сохраняется какBook
;при значении
routing_key
равномadd_subscription
объект сохраняется какSubscription
.
Обратите внимание, что вся лишняя информация, не относящаяся к типу объекта, описанному в модели данных, не будет сохранена.
Из всего файла конфигурации системы остался не рассмотренным участок, отвечающий
за сервисы. В данном примере там описан простой сервис, вызывающий функцию select_user_books
с аргументом user_id
и возвращающий строковую переменную.
Логика работы этой функции такова — переданное значение user_id
используется
для поиска в объекте Subscription
всех книг, записанных за данным пользователем.
Затем выводятся все найденные book_id
.
3.1.5.3. Подготовка запроса в формате SOAP (XML)
Создайте файл request.py
со скриптом на языке Python для отправки простейшего
запроса с полями объекта типа User
(из нашей модели данных:
это поля id
и username
), который будет выглядеть следующим образом:
import requests data = """<soap:Envelope xmlns:soap="http://schemas. xmlsoap.org/soap/envelope/"> <soap:Body> <NS2:Connect xmlns:NS2="http://172.19.0.2:8080/soap"> <id>1</id> <username>John Smith</username> </NS2:Connect> </soap:Body> </soap:Envelope>""" header = {'Authorization' : 'Bearer c0b26a60-aebe-4899-9ab6-4458627ac61e'} r = requests.post(url = "http://172.19.0.2:8080/soap", data = data, headers = header)
Передаваемый запрос содержит заголовок для авторизации и тело в виде
SOAP объекта с обязательными полями для объекта модели данных типа
.
Примечание
Используйте в качестве значения для параметра Authorization: Bearer
токен приложений,
сгенерированный ранее.
Для успешного добавления записи имеющегося в модели данных типа объекта в запросе должны содержаться все обязательные поля для данного типа объекта.
Процедура отправки запроса и ожидаемые результаты описаны ранее в пункте Отправка запросов.
Found what you were looking for?
Feedback
Различия REST и SOAP / Хабр
val6852 000Z» title=»2020-01-10, 10:04″>10 января 2020 в 10:04
API *
Перевод
Tutorial
Автор оригинала: Ranga Karanam
Эта вторая статья в серии постов о разработке REST API:
- Введение в REST API — RESTful веб-сервисы
- Различия REST и SOAP
- Разработка REST API — что такое Contract First (контракт в первую очередь)?
- Разработка REST API — что такое Code First (код в первую очередь)?
- REST API — Что такое HATEOAS?
- Рекомендации по REST API — примеры проектирования веб-сервисов на Java и Spring
В этой статье рассматриваются некоторые аспекты основных различий между REST и SOAP.
Упс… на самом деле, сравнивать их немного похоже на сравнение яблок с апельсинами, поскольку SOAP — это формат протокола, основанный на XML, тогда как REST — это архитектурный подход.
Вы изучите
- Что такое REST?
- Что такое SOAP?
- Чем отличаются REST и SOAP?
REST и SOAP
REST и SOAP на самом деле не сопоставимы. REST — это архитектурный стиль. SOAP — это формат обмена сообщениями. Давайте сравним популярные реализации стилей REST и SOAP.
- Пример реализации RESTful: JSON через HTTP
- Пример реализации SOAP: XML поверх SOAP через HTTP
На верхнем уровне SOAP ограничивает структуры ваших сообщений, тогда как REST — это архитектурный подход, ориентированный на использование HTTP в качестве транспортного протокола.
- Специфика SOAP — это формат обмена данными. С SOAP это всегда SOAP-XML, который представляет собой XML, включающий:
— Envelope (конверт) – корневой элемент, который определяет сообщение и пространство имен, использованное в документе,
— Header (заголовок) – содержит атрибуты сообщения, например: информация о безопасности или о сетевой маршрутизации,
— Body (тело) – содержит сообщение, которым обмениваются приложения,
— Fault – необязательный элемент, который предоставляет информацию об ошибках, которые произошли при обработке сообщений. И запрос, и ответ должны соответствовать структуре SOAP. - Специфика REST — использование HTTP в качестве транспортного протокола. Он подразумевает наилучшее использование функций, предоставляемых HTTP — методы запросов, заголовки запросов, ответы, заголовки ответов и т. д.
Формат обмена сообщениями
- В SOAP вы используете формат SOAP XML для запросов и ответов.
- В REST такого фиксированного формата нет. Вы можете обмениваться сообщениями на основе XML, JSON или любого другого удобного формата. JSON является самым популярным среди используемых форматов.
Определения услуг
- SOAP использует WSDL (Web Services Description Language) — язык описания веб-сервисов и доступа к ним, основанный на языке XML.
- REST не имеет стандартного языка определения сервиса. Несмотря на то, что WADL был одним из первых предложенных стандартов, он не очень популярен. Более популярно использование Swagger или Open API.
Транспорт
SOAP не накладывает никаких ограничений на тип транспортного протокола. Вы можете использовать либо Web протокол HTTP, либо MQ.
REST подразумевает наилучшее использование транспортного протокола HTTP
Простота реализации
RESTFful веб-сервисы, как правило, гораздо проще реализовать, чем веб-сервисы на основе SOAP.
- REST обычно использует JSON, который легче анализировать и обрабатывать. В дополнение к этому, REST не требует наличия определения службы для предоставления веб-службы.
- Однако в случае SOAP вам необходимо определить свой сервис с использованием WSDL, и при обработке и анализе сообщений SOAP-XML возникают большие накладные расходы.
По этому вопросу также имеется авторское видео.
Резюме
В этой статье мы подробно рассмотрели различия между REST и SOAP.
Дополнительное чтение
5 Courses to Learn RESTful Web Services With Java and Spring in 2019
10 API Testing Tips for Beginners (SOAP and REST)
Теги:
- rest
- api
- restful
- web-services
Хабы:
- API
Всего голосов 6: ↑4 и ↓2 +2
Просмотры481K
Комментарии 7
@val6852
Пользователь
Комментарии Комментарии 7
XML Soap
❮ Предыдущий Далее ❯
- SOAP означает S imple O bject A ccess P ротокол
- SOAP — это протокол связи приложений
- SOAP — это формат для отправки и получения сообщений
- SOAP не зависит от платформы
- SOAP основан на XML
- SOAP является рекомендацией W3C
Почему мыло?
Важно, чтобы веб-приложения могли обмениваться данными через Интернет.
Лучший способ связи между приложениями — через HTTP, потому что HTTP поддерживается всеми интернет-браузерами и серверы. SOAP был создан для этого.
SOAP обеспечивает способ связи между приложениями, работающими на разных операционные системы, с различными технологиями и программированием языки.
Строительные блоки SOAP
Сообщение SOAP представляет собой обычный XML-документ, содержащий следующие элементы:
- Элемент Envelope, который идентифицирует документ XML как сообщение SOAP
- Элемент заголовка, содержащий информацию заголовка
- Элемент Body, содержащий информацию о вызове и ответе
- Элемент Fault, содержащий ошибки и информацию о состоянии
Все вышеперечисленные элементы объявлены в пространстве имен по умолчанию для конверта SOAP:
http://www.w3.org/2003/05/soap-envelope/
, а пространство имен по умолчанию для кодирования SOAP и типов данных:
http://www. w3.org/2003/05/soap-encoding
Правила синтаксиса
Вот несколько важных правил синтаксиса:
- Сообщение SOAP ДОЛЖНО быть закодировано с использованием XML
- Сообщение SOAP ДОЛЖНО использовать пространство имен SOAP Envelope
- Сообщение SOAP НЕ должно содержать ссылку на DTD
- Сообщение SOAP НЕ должно содержать инструкции по обработке XML
Скелет SOAP-сообщения
soap:encodingStyle=»http://www.w3.org/2003/05/soap-encoding»>
…
…
<мыло:ошибка>
…
Элемент конверта SOAP
Необходимый элемент конверта SOAP является корневым элементом сообщения SOAP. Этот элемент определяет документ XML как сообщение SOAP.
Пример
soap:encodingStyle=»http:/ /www.w3.org/2003/05/soap-encoding»>
…
Информация о сообщении идет сюда
…
мыло:Конверт>
Пространство имен xmlns:soap
Обратите внимание на пространство имен xmlns:soap в приведенном выше примере. Он всегда должен иметь значение: «http://www.w3.org/2003/05/soap-envelope/».
Пространство имен определяет конверт как конверт SOAP.
Если используется другое пространство имен, приложение выдает ошибку и отбрасывает сообщение.
Атрибут encodingStyle
Атрибут encodingStyle используется для определения типов данных, используемых в документ. Этот атрибут может появиться в любом элементе SOAP и применяется к содержимому элемента и всем дочерним элементам.
Сообщение SOAP не имеет кодировки по умолчанию.
Синтаксис
soap:encodingStyle=» URI »
Пример
soap:encodingStyle=»http:/ /www.w3.org/2003/05/soap-encoding»>
…
Информация о сообщении идет сюда
…
мыло:Конверт>
Элемент заголовка SOAP
Необязательный элемент заголовка SOAP содержит специфичную для приложения информацию (например, аутентификацию, оплату и т. д.) о сообщении SOAP.
Если присутствует элемент Header, он должен быть первым дочерним элементом элемента Envelope.
Примечание: Все непосредственные дочерние элементы элемента Header должны быть квалифицированы пространством имен.
soap:encodingStyle=»http:/ /www. w3.org/2003/05/soap-encoding»>
…
…
Пример выше содержит заголовок с элементом «Trans», «mustUnderstand». со значением 1 и значением 234.
SOAP определяет три атрибута в пространстве имен по умолчанию. Эти атрибуты: mustUnderstand, актор и encodingStyle.
Атрибуты, определенные в заголовке SOAP, определяют, как получатель должен обрабатывать сообщение SOAP.
Атрибут mustUnderstand
Атрибут SOAP mustUnderstand может использоваться для указания того, является ли запись заголовка обязательной или необязательной для обработки получателем.
Если вы добавите mustUnderstand=»1″ к дочернему элементу элемента Header, это означает, что получатель, обрабатывающий заголовок, должен распознать этот элемент. Если получатель не распознает элемент, он потерпит неудачу при обработке заголовка.
Синтаксис
soap:mustUnderstand=»0|1″
Пример
soap:encodingStyle=»http:/ /www.w3.org/2003/05/soap-encoding»>
…
…
Актер Атрибут
Сообщение SOAP может передаваться от отправителя к получателю, передавая различные конечные точки на пути сообщения. Однако не все части SOAP-сообщения могут быть предназначены для конечного использования. конечной точки, вместо этого он может быть предназначен для одной или нескольких конечных точек на пути сообщения.
Атрибут субъекта SOAP используется для адресации элемента Header определенной конечной точке.
Синтаксис
soap:actor=» URI »
Пример
0″?>
soap:encodingStyle=»http://www.w3.org/2003/05 /мыло-кодирование»>
<мыло:Заголовок>
…
…
Атрибут encodingStyle
Атрибут encodingStyle используется для определения типов данных, используемых в документ. Этот атрибут может появиться в любом элементе SOAP, и он будет применяться к этому элементу. содержимое элемента и все дочерние элементы.
Сообщение SOAP не имеет кодировки по умолчанию.
Синтаксис
soap:encodingStyle=» URI »
Элемент SOAP Body
Требуемый элемент SOAP Body содержит фактическое сообщение SOAP, предназначенное для конечной конечной точки сообщения.
Непосредственные дочерние элементы элемента SOAP Body могут быть уточнены пространством имен.
Пример
soap:encodingStyle=»http://www.w3.org/2003/05/soap-encoding»>
В приведенном выше примере запрашивается цена на яблоки. Обратите внимание, что m:GetPrice и вышеприведенные элементы Item относятся к конкретному приложению. Они не являются частью пространства имен SOAP.
Ответ SOAP может выглядеть примерно так:
soap:encodingStyle=»http:/ /www.w3.org/2003/05/soap-encoding»>
Элемент ошибки SOAP
Необязательный элемент ошибки SOAP используется для индикации ошибки Сообщения.
Элемент SOAP Fault содержит ошибки и информация о состоянии для сообщения SOAP.
Если присутствует элемент Fault, он должен отображаться как дочерний элемент элемента Тело. Элемент Fault может появиться в сообщении SOAP только один раз.
Элемент SOAP Fault имеет следующие подэлементы:
Подэлемент | Описание |
---|---|
<код неисправности> | Код для идентификации неисправности |
<строка ошибки> | Понятное объяснение неисправности |
<нарушитель> | Информация о том, кто вызвал неисправность |
<деталь> | Содержит информацию об ошибке конкретного приложения, связанную с Элемент кузова |
Коды ошибок SOAP
Значения кода неисправности, определенные ниже, должны использоваться в элементе кода неисправности, когда описание неисправностей:
Ошибка | Описание |
---|---|
Несоответствие версии | Обнаружено недопустимое пространство имен для элемента конверта SOAP |
Должен понимать | Непосредственный дочерний элемент элемента Header с атрибутом mustUnderstand, установленным на «1», был не понял |
Клиент | Сообщение было неправильно сформировано или содержало неверную информацию |
Сервер | Возникла проблема с сервером, поэтому сообщение не могло быть отправлено |
Протокол HTTP
HTTP обменивается данными через TCP/IP. HTTP-клиент подключается к HTTP-серверу с помощью TCP. После установления соединения клиент может отправить сообщение HTTP-запроса на сервер:
POST /item HTTP/1.1
Host: 189.123.255.239
Content-Type: text/plain
Content-Length: 200
Затем сервер обрабатывает запрос и отправляет ответ HTTP обратно клиенту. Ответ содержит код состояния, указывающий на статус запроса:
200 OK
Content-Type: text/plain
Content-Length: 200
В приведенном выше примере сервер вернул код состояния 200. Это стандартный код успеха для HTTP.
Если бы сервер не смог декодировать запрос, он мог бы вернуть примерно следующее:
400 Bad Request
Content-Length: 0
SOAP Binding
как они обмениваются. Этот пробел заполняется тем, что называется «привязками SOAP». МЫЛО привязки — это механизмы, которые позволяют эффективно обмениваться сообщениями SOAP. с помощью транспортного протокола.
Большинство реализаций SOAP обеспечивают привязки для общих транспортных протоколов, например HTTP или SMTP.
HTTP является синхронным и широко используется. HTTP-запрос SOAP указывает как минимум два заголовка HTTP: Content-Type и Content-Length.
SMTP является асинхронным и используется в крайнем случае или в особых случаях.
Java-реализации SOAP обычно предоставляют специальную привязку для JMS (система обмена сообщениями Java).
Content-Type
Заголовок Content-Type для запроса и ответа SOAP определяет тип MIME для сообщения и кодировка символов (необязательно), используемая для тела XML запроса или ответа.
Синтаксис
Content-Type: MIMEType; charset=character-encoding
Пример
POST /item HTTP/1.1
Content-Type: application/soap+xml; charset=utf-8
Content-Length
Заголовок Content-Length для запроса и ответа SOAP указывает количество байтов в теле запроса или ответа.
Синтаксис
Content-Length: байты
Пример
POST /item HTTP/1.1
Content-Type: application/soap+xml; кодировка=utf-8
Content-Length: 250
Пример SOAP
В приведенном ниже примере на сервер отправляется запрос GetStockPrice. Запрос имеет параметр StockName, и параметр Price, который будет возвращен в ответе. Пространство имен для функции определено в «http://www.example.org/stock».
SOAP-запрос:
POST /InStock HTTP/1.1
Хост: www.example.org
Тип содержимого: application/soap+xml; charset=utf-8
Длина содержимого: nnn
soap:encodingStyle=»http: //www.w3.org/2003/05/soap-encoding»>
Ответ SOAP:
HTTP/1.1 200 ОК
Content-Type: application/soap+xml; charset=utf-8
Content-Length: nnn
soap:encodingStyle=»http://www. w3.org/2003/05/soap-encoding»>
мыло:конверт>
❮ Предыдущий Далее ❯
Сообщения запроса и ответа SOAP | Documentation
Существует несколько панелей на выбор при работе с сообщениями SOAP Request и Response. Давайте посмотрим на оба.
Сообщения запроса
XML — стандартное текстовое представление базового XML-сообщения, щелкните правой кнопкой мыши в редакторе, чтобы открыть всплывающее меню с соответствующими действиями:
Выберите Подтвердить , чтобы проверить текущее сообщение по базовой схеме и отобразить список ошибок проверки внизу, если они обнаружены:
Raw — отображает фактические байты последнего отправленного сообщения, включая заголовки HTTP, вложения MIME и т. д.:
Используйте эту панель для проверки результатов раскрытия свойств, фильтров и т. д. Содержимое здесь должно быть таким же, как и в журнале HTTP в нижней части главного окна SoapUI:
Отслеживание производительности тестирования по мере масштабирования тестирования API
Сравните: все функции SoapUI Pro
SoapUI с открытым исходным кодом
- Поддержка тестирования SOAP и REST API.
- Простое переключение между несколькими средами.
- Подробная история тестов и отчеты о сравнении тестов.
SoapUI Pro
- Поддержка тестирования API SOAP, REST и GraphQL.
- Простое переключение между несколькими средами.
- Подробная история тестов и отчеты о сравнении тестов.
Редактор структуры — показывает древовидное представление базового XML-сообщения:
В столбце справа показан тип схемы соответствующего значения.
Здесь вы можете редактировать значения существующих элементов/атрибутов, но не можете добавлять или удалять существующие узлы в дереве.
Форма — отображает удобную для пользователя форму ввода для базового запроса, значительно упрощая ввод содержимого, чем в редакторе XML:
Опция View Type позволяет удалять ненужные элементы или элементы, не содержащие никаких данных. Это может быть полезно при ручном тестировании, если используются только определенные поля. В зависимости от типа поля ReadyAPI отображает различные редакторы, в том числе специальные редакторы для дат, времени, массивов, списков и так далее. Например, ниже скриншот редактора даты:
Примечание: Хотя редактор поддерживает достаточно сложные XML-схемы, он не поддерживает все возможные конструкции XML-схем.
Вставка данных
Все редактируемые поля имеют контекстное меню со стандартными действиями редактора и действием Получить данные , которое автоматически вставляет расширение свойства для выбранного свойства. Например, если вы хотите использовать свойство Password в пароле , вы можете щелкнуть правой кнопкой мыши в соответствующем поле редактора формы и выбрать Получить данные :
Затем выбрать нужное свойство в следующем диалоговом окне Получить данные :
Здесь мы выбираем свойство Пароль на шаге проверки свойств. После нажатия кнопки «Добавить» вы увидите следующее:
Ответные сообщения
Ответное сообщение имеет следующие панели:
XML — показывает XML-содержимое ответного сообщения:
Raw — показывает необработанные байты ответного сообщения:
Редактор структуры — показывает ответное сообщение в виде дерева только для чтения:
Обзорный редактор — показывает удобный рендеринг ответа:
URL-адреса в ответном сообщении кликабельны.