Правильный JSON API или JSON RPC

    Что же такое JSON API ?


    Наверняка многие в курсе.

    JSON — Текстовый формат обмена данными JSON
    API — Программный интерфейс приложения API

    Ключевые слова здесь: интерфейс обмена данными.

    A, что же тогда JSON-RPC?



    JSON — мы уже в курсе.

    RPC — удаленный вызов процедур RPC

    Приходим к выводу, что JSON-RPC это: удаленный обмен данными.

    Наверняка этот обмен данными будет происходить с неким интерфейсом, т.е. с API.

    И в чем проблема?! Спросите Вы. А в том, что некоторые программисты разрабатывая JSON API, т.е интерфейс, забывают про JSON-RPC.И начинается очередное изобретение велосипеда. Frontend программист говорит: «я передам тебе такой то json», а Backend программист отвечает: «а я тебе верну такой то json». И все бы ничего, но было бы хорошо вспомнить о том, что умные люди уже давно разработали стандарты, вернее протоколы обмена данными. И не какие то сверх сложные, а очень даже простые: JSON-RPC

    Вероятно многие, если не сказать, что почти все, знают и даже пользуются этими протоколами. Написана куча серверов и т.д. Но вот лично меня не все устроило в существующих протоколах. Они показались мне не достаточно гибкими и не во всем логичными. Как вы уже догадались решил я изобрести свой велосипед json-rpc-1.5

    Главные отличия от существующих протоколов это:


    • Опциональный параметр «sign» — Signature (подпись) или Token
    • В запросах вместо параметра «param» используется параметр «data», т.к. мы всегда отправляем данные, а не просто параметры.
    • Во всех ответах всегда возвращается параметр «result» и в нем находится описание результата запроса «success» или «error».
    • Все данные в ответах приходят в параметре «data»
    • Можно использовать алиасы для названия параметров запроса и ответа

    Может показаться. что отличия незначительные, но они принципиально важные.
    Кстати, данный протокол появился на практике, т.е. создавая json api я использовал подход описанный в этом протоколе.

    PS:


    Получив кучу отрицательных комментариев и минусов, решил еще раз проверить, может я действительно, что то не так делаю? Естественно, всё что я здесь пишу, это мое личное мнение и я никому ничего не навязываю. Приведу пару примеров:
    1. Пример запроса JSON API Yandex директ:
    {
        "method": "GetClientInfo",
        "param": ["agrom"],
        "locale": "ru",
        "token": "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"
    }
    

    У них же можно почитать и про токены: Авторизационные токены

    2. Пример из Сбербанк API Оплата из мобильного приложения с использованием Apple Pay
    JSON запрос приводить не буду, он большой, по ссылке можно посмотреть.
    Важно, что JSON запрос содержит «paymentToken». Вот ссылка на требования к формированию токена от Apple

    Важно понимать, что токены и подписи в API используются часто, естественно на ряду с другими методами защиты. И те кто работает с разного рода API, прекрасно это знают.
    Поделиться публикацией

    Похожие публикации

    Комментарии 23

      +7
      Непонятно зачем это нужно. Существующие стандарты JSON-RPC и так гибкие дальше некуда. Передавайте sign параметром, кто мешает. Переименовали param -> data, назвали это новым стандартом… Такое себе. Приведите хоть один реальный пример, где ваш «стандарт» окажется лучше существующего 2.0 и поясните в чем именно будет преимущество.
        –1
        Передавайте sign параметром, кто мешает.

        Мешает стандарт.
        в стандарте предусмотрено 4 параметра:
        {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}

        и «sign» можно добавит только в «params», но в параметрах должны быть данные и помещать туда подпись можно но, мне кажется, не совсем логично.

        Переименовали param -> data
        Опять, же с точки зрения логики. мы передаем не просто параметры. а данные т.е. data и принимаем не просто результат, а данные т.е. data.

        А понятие результат сводится к получению «флага» успех или ошибка и мы будем обрабатывать полученные данные в зависимости от «флага».

        т.е. при стандартном подходе логика обработки ответа примерно такая:
        вариант 1 success
        1. получили ответ
        2. есть ли параметр result
        3. обрабатываем result

        вариант 2 error
        1. получили ответ
        2. есть ли параметр result
        3. если нет — ищем параметр error
        4. если есть error — обрабатываем

        При моем подходе. примерно так:
        вариант 1 success
        1. получили ответ
        2. есть ли параметр result и он == success
        3. обрабатываем data. если нужно

        вариант 2 error
        1. получили ответ
        2. есть ли параметр result и он == error
        3. обрабатываем data

        Мне кажется, что мой вариант немного логичней, но это не точно)

        На самом деле описанным стандартом я пользуюсь и доволен, но у меня закрались сомнения, верна ли моя логика, по этому я потратил вечер, изложил принципы которыми пользуюсь в виде некого альтернативного стандарта и написал данную статью. Цель послушать умных людей. а не навязать свое мнение.

        Хотя мои сомнения появились когда я начал делать проект с использованием микро-сервисной архитектуры. А для обмена данными между сервисами есть 2 основных варианта
        RPC или Message Bus. Я как раз остановился на RPC. Но может нужен другой стандарт?
        Типа: Remote Service Call (Удаленный вызов сервиса)?

        Тогда мое творчество можно назвать по другому, типа: JSON-RPS Version 1.0 )
          0
          А зачем нужен sign? Если это для защиты целостности сообщений, то как бы почему не общепринятый TLS? Если же это для JWT-токена, то почему не общепринятый `Authorization: Bearer`?
            0
            А зачем нужен sign? Если это для защиты целостности сообщений, то как бы почему не общепринятый TLS? Если же это для JWT-токена, то почему не общепринятый `Authorization: Bearer`?

            TLS не имеет отношения к RPC, это шифрование протокола, а мы наоборот от протокола передачи данных абстрагируемся.
            JWT вещь отличная, но это это открытый стандарт (RFC 7519) для создания токенов доступа и к RPC тоже никак не относится.

            Токен в конечном итоге это некая хешированная строка, как вы ее создадите, по каким стандартам не важно. А передавать то этот токен как то нужно. Для этого параметр «sign», для передачи токен строки или нескольких строк.

              0
              Вы так и не ответили, зачем он нужен. Если этот sign не нужен, то и передавать его нет смысла. Тем более, вы «абстрагируетесь от протокола передачи данных», но тем не менее, навязываете какое-то дополнительное невнятное поле, задачи которого с головой покрываются транспортными средствами.

              Или это все же про «подпись» md5(«super»+args+«secret») в духе flash-игр начала 2000-х?
                –1
                md5(«super»+args+«secret») в духе flash-игр начала 2000-х?

                Возможно) или можно
                hash('sha256', $json),
                или можно передать JWT token, это же просто строка

                Пример готового JWT:

                eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.EkN-DOsnsuRjRO6BxXemmJDm3HbxrbRzXglbN2S4sOkopdU4IsDxTI8jO19W_A4K8ZPJijNLis4EZsHeY559a4DFOd50_OqgHGuERTqYZyuhtF39yxJPAjUESwxk2J5k_4zM3O-vtd1Ghyo4IbqKKSy6J9mTniYJPenn5-HIirE

                Но даже если это не строка, какая разница, передавайте все. что душе угодно в этом параметре, лишь бы вы были уверены, что это авторизированный запрос.

                навязываете какое-то дополнительное невнятное поле

                Поле не обязательное.

                  +1
                  Сорян, вы говорите глупости. Для «авторизированности» запроса токен передается в хедере Authorization, для чего он и был создан. Для проверки целостности запроса и идентификации отправителя используйте TLS, для чего он и был создан.

                  Вы же придумываете костыль поверх костыля, не понимая основных принципов работы того, что вы пытаетесь своим костылем заменить. Не делайте так.
                    0
                    Честно говоря не ожидал такой агрессии и непонимания(
                    Хотел уже было пуститься в аргументированный спор, но не вижу смысла.

                    Вы для себя все решили. Имеете право.
                      0
                      Ну нет, давайте все таки Вашу аргументацию. Интересно все таки, чем вам не подходят существующие механизмы, указанные предыдущим комментатором
                        0
                        Всё устраивает)

                        Все
                        существующие механизмы
                        для чего то предназначены и используются по назначению.

                        Что бы завершить эти не конструктивные споры, нашел сегодня пару примеров,
                        как Yandex и Сбербанк используют токены в своих JSON API вы при желании можете найти еще множество примеров.
                        Дополнил этими примерами свою статью, смотрите после «PS:»

                        Повторюсь:
                        Важно понимать, что токены и подписи в API используются часто, естественно на ряду с другими методами защиты. И те кто работает с разного рода API, прекрасно это знают.
            0
            Тогда мое творчество можно назвать по другому, типа: JSON-RPS Version 1.0 )

            имел в виду JSON-RSC (Remote Service Call)
          +6
          Спасибо, это не нужно, закапывайте.
            0

            Алилуйя, вы изобрели велосипед.

              +1

              jsonapi.org

              +2
              Тут есть два момента — RPC (remote procedure call), оно же удаленный вызов процедуры. С каких-то древнейших времен повелось, что у процедуры могут быть «параметры» и она возвращает «результат».

              В вашей реализации, где в result вы передаете success или ошибку, я не могу результат мапить на конструктор объекта, его использующий. А в jsonrpc 2.0 — могу смело. Наличие result — гарантирует что результат получен. Наличие error — гарантирует что произошла ошибка. Одновременно их быть не может. К тому же наличие error.code позволит мне сделать локализацию, а добавление error.data — позволит подробно описать условия выполнения ошибки, для дальнейшей отладки. В зависимости от логики обработки, мне ничто не мешает передавать session_id (к примеру) как параметр или какой-нибудь X-Auth-Token в заголовке.
                –1
                С каких-то древнейших времен повелось, что у процедуры могут быть «параметры» и она возвращает «результат».


                Открыли мне глаза, практически. Видимо я не такой старый)
                Но для меня результат и получаемые данные, это не одно и тоже.
                т.к. результат на запрос мы получаем всегда, даже когда сервер молчит, это все равно результат.

                result вы передаете success или ошибку, я не могу результат мапить

                в этом и смысл. Хотите мапить result, а его там нет, там error. Естественно, это все проверяется. Но мне показалось логичным, в начале понять, что нужно сделать, т.е.

                result = success — обрабатываем данные из data
                result = error — реагируем на ошибку, а все данные о ошибке в data

                просто отделил сообщение о результате от данных, мне пока кажется это логичным.

                мне ничто не мешает передавать session_id (к примеру) как параметр или какой-нибудь X-Auth-Token в заголовке.

                да, согласен. но если «общаются» 2 сервера и нужно абстрагироваться от канала?
                т.е. мы не знаем, что за канал передачи данных и о заголовках тоже ничего не знаем. А идентифицировать запросы, а в ряде случаев и ответы нужно.
                  0
                  Ответ, ошибка и отсутствие ответа — это результат запроса. А поле result по спецификации jsonrpc 2.0 — это результат выполнения вызываемой процедуры. Если процедуру выполнить успешно не удалось — результата нет и есть ошибка. Впрочем ваш вариант тоже имеет право на жизнь, но я от такой схемы отказался, мне проще проверить наличие свойства error, а не его значение. И при его отсутствии — вызвать обработчик результата. В противном случае — обработчик ошибки.

                  JsonRPC 2.0 позволяет идентифицировать запросы и ответы по идентификатору. Ничто не мешает передавать идентификатор сессии в params — так будет работать везде, хоть через HTTP, хоть через брокеры сообщений/запросов (я использую NATS — очень надежный механизм, рекомендую).
                    0
                    Согласен,
                    Ничто не мешает передавать идентификатор сессии в params

                    писал, по этому поводу чуть выше:
                    «sign» можно добавит только в «params», но в параметрах должны быть данные и помещать туда подпись можно но, мне кажется, не совсем логично.

                    Думаю лучше провести идентификацию немного раньше, не разбирая данные, данных может быть очень много, зачем нам тратить ресурсы на их разбор, если это не авторизированный запрос?
                      0
                      Впрочем ваш вариант тоже имеет право на жизнь,

                      Спасибо! И я считаю, что на вкус и цвет…
                        0

                        А вот в этом заключается очень частый просчет разработчиков различных API. Часто делают так (простой пример обновления профиля пользователя):


                        { 
                           "session_id": "fc94bb40-b938-409e-9855-3dc283680a0a",
                           "password": "verysecretpassword",
                           "first_name": "Vasia",
                          "last_name": "pupkin",
                          "birth_day": "2000-01-01"
                        }

                        Что и приводит к необходимости очищать данные от сессии и пароля, после их проверки.


                        Если запрос построить так:


                        { 
                           "session_id": "fc94bb40-b938-409e-9855-3dc283680a0a",
                           "password": "verysecretpassword",
                           "person": {
                               "first_name": "Vasia",
                               "last_name": "pupkin",
                               "birth_day": "2000-01-01"
                            }
                        }

                        то на каждый шаг требуется только одно свойство:


                        1. Проверяем валидность сессии по session_id (и получаем идентификатор пользователя)
                        2. Проверяем пароль по password и идентификатору профиля
                        3. Вызываем обновление данных профиля по содержимому profile
                          В любой момент мы можем прекратить действия.
                          0
                          Думаю лучше провести идентификацию немного раньше, не разбирая данные, данных может быть очень много, зачем нам тратить ресурсы на их разбор, если это не авторизированный запрос?

                          Нет, это не аргумент, отвечаю сам себе)))
                          т.к. не важно где находится параметр «sign», к моменту его поиска в запросе, JSON уже должен быть разобран(
                    0

                    Для себя раз и навсегда решил сделать так: всегда приходят data, issuccess и errors. Issuccess зависит тупо от содержимого errors (issuccess => errors==null). В errors есть code, message и description. Сам ответ всегда одинаковой структуры и его спокойно можно десериализовать в объект на клиенте с любой типизацией. Ориентировался на дотнет. Все это дарует неисчислимые блага на клиенте и унификацию апи, все счастливы. И никакого гемора с разбором ответов, когда то есть данные, то их нет.

                      0
                      Я так понял, что вы предпочитаете подход похожий на мой

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое