httpflaskpython-requestsmime-types

What is the difference between `request.form` and `request.get_json()`?


Situation description:

@app.route('/auth/audio/save', methods=['POST'])
def save_audio():
    # files = request.files
    resp_dict = {'message': "success"}
    file = request.files.get("file")
    if file.filename == "":
        resp_dict['message'] = "[No file] file.filename == ''"
        return jsonify(resp_dict), 400
    # data = request.get_json()
    data = request.form
    logger.info(f"data: {data}")
    check_required_params(data, ['user_id'])
    user_id = data["user_id"]
    audio_dir = os.path.join(app_dir, 'db_files/audio')
    save_path = os.path.join(audio_dir, f'{user_id}.bin')
    file.save(save_path)
    return jsonify(resp_dict), 200

I have a Flask view function which handles POST request that sends a file and data wrapped inside the request body.

The send request codes show below:

def test_send_audio():
    cur_dir = os.path.dirname(os.path.abspath(__file__))
    audiopath = os.path.join(cur_dir, "test.wav")
    files = {'file': open(audiopath, 'rb')}
    data = {"user_id": 2}
    # headers = {
    #     "Content-Type": "multipart/form-data",
    # }
    # NOTE: Not sure why handlers can't get files when I add headers
    response = requests.post(
        save_audio_url,
        # headers=headers,
        files=files,
        data=data,
    )

My question:

  1. In view function, what is the difference between using 'request.form' and 'request.get_json()'? In my case which one is the best practice? What is the best use case for the other one?
  2. Why adding the headers like "Content-Type": "multipart/form-data" will let the view funtion failed to get file when handling the request, but if I don't add this headers it will work?
  3. In general, what is the best practice to implement the case that I described?

Solution

    1. request.form stores form data. request.get_json() fetches JSON data in the request body. These two types of data are differentiated by their media types. Form data would be multipart/form-data, application/x-www-form-urlencoded, or application/x-url-encoded. JSON data would be application/json. The difference here is that multipart/form-data can handle file uploads, which is what you are doing here, while application/json cannot. JSON data is a better option when you send structured data that doesn't involve files.

    2. As the name suggests, multipart types are data that have multiple parts. Different parts should be separated by a "boundary", which is an arbitrary string that separates these different pieces of data. A better explanation is here. In your code, requests library will automatically generate the boundary and populate the data in the request body using that boundary. So you should not manually specify the content type in the header. You can try printing request.headers and request.get_data() to see this.

    3. You are trying to receive a file and save it using user_id as the filename. I'm gonna assume user_id is the id of the user that send this request(let me know if that's the case). We don't normally store or send session info(in this case user_id) in a request body as it's a poor security practice. Rather, we encode session info in a token(often a JWT token) and put it in the request header.

    Hope it helps.