我正在构建一个移动应用程序的后端,该应用程序将托管在连接到PostgreSQL数据库的Django站点上。
我从来没有建立任何东西来完成这一点,这是我的第一次尝试。我以前没有研究过这种类型的应用程序,所以请告诉我,下面的代码是否遵循这个任务的最佳实践,是否安全,并且没有任何明显的问题。如果有一个行业标准,这是非常不同的,请让我知道它是什么,为什么它是首选。
我的客户通过视图readJSON连接:
@csrf_exempt
def readJSON(request):
if request.method == 'POST':
try:
c = connections['postgres_db'].cursor()
json_data = json.loads(request.body)
if json_data['tag'] == 'register':
return HttpResponse(cc().register(c, json_data))
elif json_data['tag'] == 'syncprofile':
return HttpResponse(cc().syncProfile(c, json_data))
################ A long list of other possible tags
else:
raise Http404
finally:
c.close()
else:
raise Http404我有一个单独的类,它保存所有处理传入JSON请求的代码。这就是其中一个功能。
def register(self, c, json_data):
import bcrypt as bc
salt = bc.gensalt()
pwrd = json_data['data']['password']
hash = bc.hashpw(pwrd, salt)
try:
c.execute("INSERT INTO profiles VALUES (DEFAULT, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
(json_data['data']['email'], hash, salt, json_data['data']['date_created'],
json_data['data']['state_lvl'], True,
json_data['data']['first_name'], json_data['data']['last_name'],
json_data['data']['sex'], json_data['data']['age'],
json_data['data']['state'], json_data['data']['country'],
json_data['data']['language'], json_data['data']['device_type'],
json_data['data']['device'], json_data['data']['device_width'],
json_data['data']['device_height'], json_data['data']['device_screen_density']
))
json_response = json.dumps({"success": 1, "errors": 0})
return HttpResponse(json_response, mimetype="application/json")
except Exception,e:
json_response = json.dumps({"success": 0, "errors": 1, "msg": str(e)})
return HttpResponse(json_response, mimetype="application/json")下面是上面函数的JSON结构。
{
"tag":"register",
"data":{
"email":"email@email.com",
"password":"app123",
"date_created":"07/10/1967",
"state_lvl":"1",
"first_name":"John",
"last_name":"Smith",
"sex":"m",
"age":23,
"state":"CA",
"country":"USA",
"language":"english",
"device_type":"phone",
"device":"Nexus-5",
"device_width":400,
"device_height":800,
"device_screen_density":331
}
}发布于 2014-03-21 06:20:52
返回HTTP 404 ("Not“)是不正确的,除非您有意发送模糊响应作为一种由模糊的安全措施。如果您需要一个POST,而客户端只发送一个POST,那么正确的响应应该是HTTP 405 (“不允许使用方法”)。对于一个没有适当JSON标记的主体,我认为适当的响应应该是一个非特定的HTTP 400 (“不良要求”)。
在if-else分支中,更愿意先使用较少的代码将分支放在首位,以避免它的发生并减少心理负担。通常,这意味着将错误处理程序放在首位。作为奖励,您可以消除一个级别的缩进。
使用一个with块作为数据库游标,而不是try-finally构造。
假设JSON标记与cc()的方法名称之间有确切的对应关系,则可以动态地分派。( syncProfile()方法与'syncprofile'不匹配,但可以相应地重命名方法。)
@csrf_exempt
def readJSON(request):
if request.method != 'POST':
raise Http405
json_data = json.loads(request.body)
# Whitelist allowable function calls for security
if json_data['tag'] not in ['register', 'syncprofile', … ]:
raise Http400
cc_method = getattr(cc(), json_data['tag'])
with connections['postgres_db'].cursor() as c:
return HttpResponse(cc_method(c, json_data))导入应该列在文件的顶部。我不会将bcrypt命名为bc,因为后者只为了保存几个字符而牺牲了清晰性。
需要插入的列很多,参数列表也很长。我认为如果您使用命名占位符,那么出错的可能性就会更小。
import bcrypt
def register(self, c, json_data):
attr = dict(json_data['data'])
attr['salt'] = bcrypt.gensalt()
password = attr['password']
attr['hash'] = bcrypt.hashpw(password, attr['salt'])
attr['true'] = True
try:
c.execute("""INSERT INTO profiles VALUES (DEFAULT
, %(email)s
, %(hash)s
, %(salt)s
, %(date_created)s
, %(state_lvl)s
, %(true)s
, %(first_name)s
, %(last_name)s
, %(sex)s
, %(age)s
, %(state)s
, %(country)s
, %(language)s
, %(device_type)s
, %(device)s
, %(device_width)s
, %(device_height)s
, %(device_screen_density)s
)""", attr);
json_response = json.dumps({"success": 1, "errors": 0})
except Exception, e:
json_response = json.dumps({"success": 0, "errors": 1, "msg": str(e)})
return HttpResponse(json_response, mimetype="application/json")让客户端指定概要文件创建日期有点奇怪。我只想让数据库发布一个时间戳。
我认为长参数列表表示存在问题的数据库模式。如果您想要向用户配置文件添加另一个属性,则需要进行架构更改。可能应该将profiles表拆分为包含必需项的accounts表和存储任意键值对的account_attributes表。
CREATE TABLE accounts
( id SERIAL PRIMARY KEY
, email TEXT NOT NULL
, hash TEXT NOT NULL
, salt TEXT NOT NULL
, date_created DATE NOT NULL DEFAULT statement_timestamp()
);
CREATE TABLE account_attributes
( account_id INTEGER NOT NULL
, attribute TEXT NOT NULL
, value TEXT NOT NULL
, PRIMARY KEY (account_id, attribute)
, FOREIGN KEY (account_id) REFERENCES accounts (id)
);发布于 2014-03-21 12:48:22
万一有一点帮助..。在寄存器函数中,可以简化c.execute语句字符串的创建。
execute_text = ("INSERT INTO profiles VALUES (DEFAULT, %s, %s, %s, %s, %s, %s" %
(json_data['data']['email'], hash, salt,
json_data['data']['date_created'], json_data['data']['state_lvl'],
True,))
json_fields = ['first_name', 'last_name', 'sex', 'age', 'state', 'country',
'language', 'device_type', 'device', 'device_width', 'device_height',
'device_screen_density']
for field in json_fields:
execute_text = "%s, %s" % (execute_text, json_data['data'][field])
execute_text = "%s)" % execute_texthttps://codereview.stackexchange.com/questions/44244
复制相似问题