'NoneType' object is not subscriptable

Lucas Padden picture Lucas Padden · Oct 29, 2016 · Viewed 21.3k times · Source

I am creating an ios app that uses a server written in flask + python, and when I make a connection to the server to register a user I keep getting a 'NoneType' object is not subscriptable error in my server.py file. Basically my question is what is causing this error and how am I able to fix this. Also if anyone can point me in the right direction of different or easier ways to do this I would appreciate it thanks!

Here is the server.py file:

import bcrypt
from flask import Flask, request, make_response,jsonify
from flask_restful import Resource, Api
from pymongo import MongoClient
from json import JSONEncoder
from bson.objectid import ObjectId
from functools import wraps


app = Flask(__name__)

mongo = MongoClient('localhost', 27017)

app.db = mongo.eventure_db

app.bcrypt_rounds = 12

api = Api(app)

# Authentication code.
def check_auth(username, password):
    # check_auth should access the database and check if the username +  password are correct.
    # create a collection to hold the users.

   user_collection = app.db.users
   user = user_collection.find_one({'username': username})

   if user is None:
        return False
    else:
        # check if hash generated matches stored hash
        encodedPassword = password.encode('utf-8')
        if bcrypt.hashpw(encodedPassword, user['password']) == user['password']:
            return True
        else:
            return False



# User resource

class User(Resource):

   def post(self):
        if (request.json['username'] == None
                or request.json['password'] == None):
                return ({'error':  'Request requires username and password'},
                    400,
                    None)

        user_collection = app.db.users
        user = user_collection.find_one({'username':       request.json['username']})

        if user is not None:
            return ({'error': 'Username already in use'}, 400, None)
        else:
            encodedPassword = request.json['password'].encode('utf-8')
            hashed = bcrypt.hashpw(
                encodedPassword, bcrypt.gensalt(app.bcrypt_rounds))
            request.json['password'] = hashed
            user_collection.insert_one(request.json)

    @requires_auth
    def get(self):
        return (None, 200, None)


api.add_resource(User, '/eventure/api/v1.1/user/')

# Must define a custom JSON Serializer for flask_restful
# this is because ObjectId is not a string, and therefore,
# Flask's default serializer cannot serialize it.

@api.representation('application/json')
def output_json(data, code, headers=None):

    resp = make_response(JSONEncoder().encode(data), code)
    resp.headers.extend(headers or {})
        return resp



if __name__ == '__main__':
    app.config['TRAP_BAD_REQUEST_ERRORS'] = True
    app.run(host='localhost', port=8789, debug=True)

And this is my register function in swift:

@IBAction func register(_ sender: AnyObject) {

    let url = URL(string: "http://localhost:8789/eventure/api/v1.1/user/")

    var request = URLRequest(url: url!)

    request.httpMethod = "POST"

    request.setValue(generateBasicAuthHeader(username: username.text!, password: password.text!), forHTTPHeaderField: "Authorization")


    let session = URLSession.shared

    let task = session.dataTask(with: request) { data, response, error in
        if let response = response, let data = data {
            print(String(data: data, encoding: String.Encoding.utf8))
        }
    }

    task.resume()




    self.username.text = ""
    self.password.text = ""



}

traceback:

  [28/Oct/2016 19:22:33] "POST /eventure/api/v1.1/user/ HTTP/1.1" 500 -
  Traceback (most recent call last):
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 270, in error_router
    return original_handler(e)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/_compat.py", line 32, in reraise
    raise value.with_traceback(tb)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 270, in error_router
    return original_handler(e)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/_compat.py", line 32, in reraise
    raise value.with_traceback(tb)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 471, in wrapper
    resp = resource(*args, **kwargs)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/views.py", line 84, in view
    return self.dispatch_request(*args, **kwargs)
  File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 581, in dispatch_request
    resp = meth(*args, **kwargs)
  File "/Users/Dynee/eventure-backend-api/server.py", line 128, in post
    if (request.json['username'] == None
TypeError: 'NoneType' object is not subscriptable

Also here is the generateBasicAuthHeader function:

func generateBasicAuthHeader(username: String, password: String) -> String {

    let loginString = String(format: "%@:%@", username, password)
    let loginData = loginString.data(using: String.Encoding.utf8)!
    let base64LoginString = loginData.base64EncodedString()
    let basicAuthHeader = "Basic \(base64LoginString)"
    return basicAuthHeader
}

Answer

danidee picture danidee · Oct 29, 2016

You need to explicitly set the content-type to application/json for request.json to work properly in flask. If the header isn't set, request.json would return None.

But the recommended to get json data in flask from a post request is to use request.get_json()

I'll also urge you to test your api with the nifty requests module before using your ios application.

>>> import requests
>>> requests.post(url, json={'name': 'hello world'})

It already sets the appropriate headers required to make a json request

If it works with the requests module, then you can be sure that it's going to work with your ios application. you just need to make sure you're setting the correct content-type.

You can forcefully tell flask to ignore the content-type header with

request.get_json(force=True)