使用JwToken身份验证对HTTP RestAPI进行Python发布请求会生成重复的帖子

时间:2017-08-14 07:56:52

标签: python rest api python-requests json-web-token

我一直在编写API程序来测试使用JwToken身份验证发布到http RestAPI。在这种情况下,它是用于患者管理系统,我正在预约。 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具有与转储过程相同的功能。

使用下面的代码我发现我可以运行requests.post调用以成功传递Json数据有效负载并生成帖子,但随后它似乎随后执行第二个帖子,产生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 个答案:

答案 0 :(得分:1)

  

评论:但我需要一个base64编码的字符串来发送api requrest。

关于来源

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相同吗?

答案 1 :(得分:0)

好的找到了我的重复帖子问题的原因。这是我自己的愚蠢错误。在我的屏幕底部的python文件的底部,我已经调用了测试函数并且没有注意到这一行(因此我错过了在上面的代码中发布它)。它将测试功能置于完整的重复循环中。 .... :(这样一个愚蠢的新手错误。 感谢stovfl提供有关处理编码JwToken的建议。

事后看来,我发现有关stackoverflow的每个帖子都有关于API的重复帖子,因为用户代码中有一个循环,我无法找到它。