Запросы Python Post к HTTP RestAPI с аутентификацией JwToken генерируют повторяющиеся записи

Я писал процедуру API для проверки публикации в http RestAPI с аутентификацией JwToken. В этом сценарии это система управления пациентами, и я назначаю встречу. Бизнес-правило API не допускает дублирование заказов одновременно.

Я использую виртуальную среду Python 3.5.3 (пишу в Pycharm IDE) и запускаю свои тесты с использованием фрейма Pytest. Также используется PyJWT 1.5.2., Запросы 2.18.3, simplejson 3.11.1 и urllib3 1.22 установлены (я предполагаю, что Requests использует urllib3). Я использую simplejson.dumps вместо обычного json.dumps, потому что в виртуальной среде этой библиотеки не было, и у меня возникли проблемы с ее добавлением. Насколько я могу сказать, simplejson имеет те же функции, что и процедура dumps.

Используя приведенный ниже код, я обнаружил, что могу выполнить вызов request.post, чтобы успешно доставить полезную нагрузку данных Json и сгенерировать сообщение, но затем кажется, что впоследствии выполняется 2-е сообщение, которое генерирует ошибку конфликта 409. У меня есть доступ к базе данных журналов на соответствующем сервере API, и я вижу, что на самом деле он пытался опубликовать дважды, но я не могу понять, почему это происходит, и я думаю, что есть что-то в библиотеке запросов, которая вызывается дважды, Возможно из-за Json, который я отправляю.

Вывод выглядит так:

https://targerserver.url.com.au/API/Core/v2.1/appointment/
200
{'statusMessages': [], 'appointment': {'startDateTime': '2017-08-15T11:00:00 +10:00', 'appointmentReferenceNumber': '39960337', 'notes': '', 'clients': [{'clientId': 'abeff2be-ce6e-4324-9b57-e28ab7967b6c'}], 'status': 'Booked', 'locationId': 'd8d4fe7c-765a-46a3-a389-54ce298a27e9', 'notifyPractitioner': False, 'endDateTime': '2017-08-15T11:30:00 +10:00', 'subject': 'Jim Beam ', 'appointmentId': '08b37ce3-25e1-4e2a-9bb7-9ec2d716f83b', 'practitioner': {'practitionerId': 'a630f4ad-8b4b-4e06-8cee-7db56ba8b9bf'}}}
collected 1 item

test_PMSAPI_availability.py https://targerserver.url.com.au/API/Core/v2.1/appointment/
409

Мой Json требует объект (который является словарем), а также список ключей для другого поля (в котором есть одна запись), и мне интересно, не обрабатывает ли библиотека запросов это. Это пример того, как выглядит JSON

payload_str = {"startDateTime":"2017-08-15T11:00+10:00","endDateTime":"2017-08-15T11:30+10:00","practitioner": {"practitionerId":"a630f4ad-8b4b-4e06-8cee-7db56ba8b9bf"}, "locationId":"d8d4fe7c-765a-46a3-a389-54ce298a27e9","clients":[{"clientId":"abeff2be-ce6e-4324-9b57-e28ab7967b6c"}]}

У меня похожий код, который работал для вызовов Get в той же системе, но публикация Json действительно кажется проблематичной. У нас есть другие инструменты для звонков на те же конечные точки API, которые, похоже, не имеют этой проблемы. Журналы показывают, что данные JSON, которые я предоставляю, идентичны данным другого инструмента с такими же данными.

Что я могу видеть из результатов, так это получить 200 успешных кодов при первоначальном ответе, но затем, если запрос response.status_code стал 409 ответом. Я также пытался ничего не делать с ответом на случай, если запрос был вызван и возник конфликт.

Мой код выглядит так:

import jwt
import _datetime
import requests
import simplejson
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from string import Template

def app_undertest_credentials(keyname):
    app_credentials = {'app_consumer_id': 'MyTestApp',
                       'app_consumer_secret': 'where my secret goes',
                       'app_access_token': 'where my access token goes',
                       'base_url': 'https://targerserver.url.com.au'
                       }

    return app_credentials.get(keyname)
def end_points_dict(keynameStr, versionStr):
    end_points = {'location': '/API/Core/$version/location/',
                  'practitioner': '/API/Core/$version/practitioner/',
                  'availabilityslot': '/API/Core/$version/AvailabilitySlot/',
                  'client': '/API/Core/$version/client/',
                  'healthfundproviderlist': '/API/Core/$version/healthfundproviderlist/',
                  'timezone': '/API/Core/$version/timezone/',
                  'clientgroup': '/API/Core/$version/clientgroup/',
                  'appointment': '/API/Core/$version/appointment/'
                  }
    lower_keynameStr = keynameStr.lower()
    url_extension_no_version = Template(end_points.get(lower_keynameStr))
    url_extension_with_version = url_extension_no_version.safe_substitute(version=versionStr)
    return url_extension_with_version

def test_api_appointment_post():
    # Set Client app credentials
    app_consumer_id = app_undertest_credentials('app_consumer_id')
    app_consumer_secret = app_undertest_credentials('app_consumer_secret')
    app_access_token = app_undertest_credentials('app_access_token')
    base_url = app_undertest_credentials('base_url')
    end_point_url_sfx_str = end_points_dict('Appointment', 'v2.1')
    httpmethod = 'POST'

    # Create dictionary for json post payload
    data_payload = {'startDateTime':'2017-08-15T11:00+10:00',
                'endDateTime':'2017-08-15T11:30+10:00',
                'practitioner': {'practitionerId':'a630f4ad-8b4b-4e06-8cee-7db56ba8b9bf'},
                'locationId': 'd8d4fe7c-765a-46a3-a389-54ce298a27e9',
                'clients': [{'clientId':'abeff2be-ce6e-4324-9b57-e28ab7967b6c'}]

    # Create claims dictionary payload for generation of JwToken
    claims = {
        'iss': 'http://myappsdomain.com.au',
        'aud': 'https://targetservers.domain.com.au',
        'nbf': _datetime.datetime.utcnow(),
        'exp': _datetime.datetime.utcnow() + _datetime.timedelta(seconds=60),
        'consumerId': app_consumer_id,
        'accessToken': app_access_token,
        'url': base_url + end_point_url_sfx_str,
        'httpMethod': http_method
    }

    #create jwtoken and then convert to string
    encoded_jwt_byte = jwt.encode(claim_payload, app_consumer_secret, algorithm='HS256')
    jwt_str = encoded_jwt_byte.decode()

    #Create authentication header
    headers = {'Authorization': 'JwToken' + ' ' + jwt_str, 'content-type': 'application/json'}

    uri = base_url + end_point_url_sfx_str
    response = requests.post(uri, headers=headers, json=datapayload)
    print(response.status)
    print(response.json())
    response.close()

Я рассматриваю возможность использования wireshark, чтобы выяснить, что на самом деле отправляет мой вызов, но я подозреваю, что это просто скажет мне, что вызовы отправляются дважды

2 ответа

Комментарий: Но мне требуется строка в кодировке base64 для отправки запроса API.

Относительно источника

segments.append(base64url_encode(signature))
    return base64.urlsafe_b64encode(input).replace(b'=', b'')
return b'.'.join(segments)

все есть base64 и вернулся как bytes, Так что вы должны быть в порядке, используя

jwt_str = str(encoded_jwt_byte)

Извините, не могу использовать PyJWT,
Пробовал с python_jwt и работает как положено.

Протестировано с Python:3.4.2 - запросы:2.11.1

Вам действительно нужно encode(... а потом decode()?

#create jwtoken and then convert to string
encoded_jwt_byte = jwt.encode(claim_payload, app_consumer_secret, algorithm='HS256')
jwt_str = encoded_jwt_byte.decode()

Будет jwt_str не быть таким же, как claim_payload?

Хорошо, нашел причину моей проблемы с повторяющимся сообщением. Это была моя собственная глупая ошибка. Внизу моего файла Python, находящегося в нижней части экрана, я вызвал тестовую функцию и не заметил эту строку (следовательно, я пропустил публикацию в своем коде выше). Это поместило тестовую функцию в полный повторяющийся цикл. ....:(Такая глупая ошибка новичка. Спасибо stovfl за совет по обработке закодированного JwToken.

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

Другие вопросы по тегам