Difference between JSON arguments

I recently tried passing a JSON encoded body in a request to create a section - rather than passing a /x-www-form-urlencoded body. The results were really surprising - rather than creating a section named "DumbleTest" it created a section with the name "II2202 Collaboration Course 2018-08-08  Links to an external site.". The output below shows the requests and replies with the token replaced by "xxx" (I have manually highlighted the key differences):

 

./create_section_test.py -v 5694 "DumbleTest"
ARGV : ['-v', '5694', 'DumbleTest']
VERBOSE : True
REMAINING : ['5694', 'DumbleTest']
Testing : False
'Wed Aug 8 12:10:06 2018'
url: https://kth.test.instructure.com/api/v1/courses/5694/sections
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): kth.test.instructure.com:443
send: b'POST /api/v1/courses/5694/sections HTTP/1.1\r\nHost: kth.test.instructure.com\r\nUser-Agent: python-requests/2.19.1\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nAuthorization: Bearer xxx\r\nContent-Length: 38\r\nContent-Type: application/json\r\n\r\n'
send: b'{"course_section[name]": "DumbleTest"}'
reply: 'HTTP/1.1 200 OK\r\n'
DEBUG:urllib3.connectionpool:https://kth.test.instructure.com:443 "POST /api/v1/courses/5694/sections HTTP/1.1" 200 163
header: Cache-Control header: Content-Encoding header: Content-Type header: Date header: ETag header: P3P header: Server header: Set-Cookie header: Set-Cookie header: Set-Cookie header: Status header: Strict-Transport-Security header: Vary header: X-Canvas-Meta header: X-Canvas-User-Id header: X-Content-Type-Options header: X-Frame-Options header: X-Rate-Limit-Remaining header: X-Request-Context-Id header: X-Request-Cost header: X-Request-Processor header: X-Robots-Tag header: X-Runtime header: X-Session-Id header: X-UA-Compatible header: X-XSS-Protection header: Content-Length header: Connection ('result of creating section: {"id":11124,"course_id":5694,"name":"II2202 '
'Collaboration Course '
'2018-08-08","start_at":null,"end_at":null,"restrict_enrollments_to_section_dates":null,"nonxlist_course_id":null,"sis_section_id":null,"sis_course_id":null,"integration_id":null}')
----------------------------------------------------------------------
./create_section_test.py -v 5694 "DumbleTest"
ARGV : ['-v', '5694', 'DumbleTest']
VERBOSE : True
REMAINING : ['5694', 'DumbleTest']
Testing : False
'Wed Aug 8 12:12:25 2018'
url: https://kth.test.instructure.com/api/v1/courses/5694/sections
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): kth.test.instructure.com:443
send: b'POST /api/v1/courses/5694/sections HTTP/1.1\r\nHost: kth.test.instructure.com\r\nUser-Agent: python-requests/2.19.1\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nAuthorization: Bearer xxx\r\nContent-Length: 35\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n'
send: b'course_section%5Bname%5D=DumbleTest'
reply: 'HTTP/1.1 200 OK\r\n'
DEBUG:urllib3.connectionpool:https://kth.test.instructure.com:443 "POST /api/v1/courses/5694/sections HTTP/1.1" 200 143
header: Cache-Control header: Content-Encoding header: Content-Type header: Date header: ETag header: P3P header: Server header: Set-Cookie header: Set-Cookie header: Set-Cookie header: Status header: Strict-Transport-Security header: Vary header: X-Canvas-Meta header: X-Canvas-User-Id header: X-Content-Type-Options header: X-Frame-Options header: X-Rate-Limit-Remaining header: X-Request-Context-Id header: X-Request-Cost header: X-Request-Processor header: X-Robots-Tag header: X-Runtime header: X-Session-Id header: X-UA-Compatible header: X-XSS-Protection header: Content-Length header: Connection ('result of creating section: '
'{"id":11125,"course_id":5694,"name":"DumbleTest","start_at":null,"end_at":null,"restrict_enrollments_to_section_dates":null,"nonxlist_course_id":null,"sis_section_id":null,"sis_course_id":null,"integration_id":null}')

 

However, if rather than sending '{"course_section[name]": "DumbleTest"}' one sends '{"course_section": {"name": "DumbleTest2"}}' it works as expected:

 

./create_section_test.py -v 5694 "DumbleTest2"
ARGV : ['-v', '5694', 'DumbleTest2']
VERBOSE : True
REMAINING : ['5694', 'DumbleTest2']
Testing : False
'Wed Aug 8 12:38:43 2018'
url: https://kth.test.instructure.com/api/v1/courses/5694/sections
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): kth.test.instructure.com:443
send: b'POST /api/v1/courses/5694/sections HTTP/1.1\r\nHost: kth.test.instructure.com\r\nUser-Agent: python-requests/2.19.1\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nAuthorization: Bearer xxx\r\nContent-Length: 43\r\nContent-Type: application/json\r\n\r\n'
send: b'{"course_section": {"name": "DumbleTest2"}}'
reply: 'HTTP/1.1 200 OK\r\n'
DEBUG:urllib3.connectionpool:https://kth.test.instructure.com:443 "POST /api/v1/courses/5694/sections HTTP/1.1" 200 144
header: Cache-Control header: Content-Encoding header: Content-Type header: Date header: ETag header: P3P header: Server header: Set-Cookie header: Set-Cookie header: Set-Cookie header: Status header: Strict-Transport-Security header: Vary header: X-Canvas-Meta header: X-Canvas-User-Id header: X-Content-Type-Options header: X-Frame-Options header: X-Rate-Limit-Remaining header: X-Request-Context-Id header: X-Request-Cost header: X-Request-Processor header: X-Robots-Tag header: X-Runtime header: X-Session-Id header: X-UA-Compatible header: X-XSS-Protection header: Content-Length header: Connection ('result of creating section: '
'{"id":11126,"course_id":5694,"name":"DumbleTest2","start_at":null,"end_at":null,"restrict_enrollments_to_section_dates":null,"nonxlist_course_id":null,"sis_section_id":null,"sis_course_id":null,"integration_id":null}')

 

I can understand the logic in the difference in functionality, but do not understand why if the JSON is not considered to have the proper form why it would would make up a name for the section based on today's date.

The difference in the routines are shown below.


def create_section_in_course(course_id, section_name):
    global Verbose_Flag
    # Use the Canvas API to create a single section for this course
    #POST /api/v1/courses/:course_id/sections
    # course_section[name]
    #
    url = baseUrl + '%s/sections' % (course_id)
    if Verbose_Flag:
        print("url: " + url)
    #
    # The following code works when using x-www-form-urlencoded to encode the body:
    # payload={'course_section[name]': section_name}
    # r = requests.post(url, headers = header, data=payload)

    # The following code works when using json encoding of the body:
    payload={'course_section':
             {
                 'name': section_name
             }
    }
    r = requests.post(url, headers = header, json=payload)

    #
    if Verbose_Flag:
        write_to_log("result of creating section: " + r.text)
    #
    if r.status_code == requests.codes.ok:
        page_response=r.json()
        return page_response
    return None