在Odoo的Controlers.py文件中,如何获取POST请求体中的json字符串?

2022-03-12 00:00:00 python json http odoo werkzeug

问题描述

我在Controlers.py文件中定义了一个类来接收HTTP请求。远程服务器发送POST请求,请求正文中的数据是JSON字符串。

我可以通过http.request.jsonrequest方法将JSON字符串直接转换成字典来获取请求体中的数据,但目前我需要直接获取请求体中的原始JSON字符串,而不是字典中的原始JSON字符串来验证签名。

无法使用直接转换为JSON字符串的方法(json.dumps()),因为这样得到的字符串与原请求Body中的JSON字符串不一致,会导致签名验证失败。

我该怎么办呢?请帮帮我。谢谢您。

这是我的Controlers.py

# -*- coding: utf-8 -*-
from odoo import http
class CallbackNotification(http.Controller):

    def safety_judgement(self):
        """
        :return:
        """
        header = http.request.httprequest.headers.environ
        signature = header['HTTP_X_TSIGN_OPEN_SIGNATURE']
        time_stamp = header['HTTP_X_TSIGN_OPEN_TIMESTAMP']

        remote_addr = http.request.httprequest.remote_addr
        if remote_addr != '47.99.80.224':
            return

    @http.route('/signature/process/my_odoo', type='json', auth='none')
    def receive_institution_auth(self, **kw):
        """
        :param kw:
        :return:
        """
        self.safety_judgement()
        request_body = http.request.jsonrequest

        action = request_body['action']
        flow_num = request_body['flowId']
        http_env = http.request.env

        sign_process_id = http_env['sign.process'].sudo().search([('flow_num', '=', flow_num)]).id
        if action == 'SIGN_FLOW_UPDATE':
            third_order = request_body['thirdOrderNo']
            name_id_user_list = third_order.split(',')
            model = name_id_user_list[0]
            record_id = name_id_user_list[1]
            approve_user_id = name_id_user_list[2]

            if approve_user_id != 'p':
                record_obj = http_env[model].sudo(user=int(approve_user_id)).browse(int(record_id))

            sign_result = request_body['signResult']
            result_description = request_body['resultDescription']
            account_num = request_body['accountId']
            org_or_account_num = request_body['authorizedAccountId']

            sign_user_id = http_env['sign.users'].sudo().search([('account_num','=',account_num)]).id
            http_manual_env = http_env['manual.sign'].sudo()
            if account_num == org_or_account_num:
                manual_id = http_manual_env.search([('sign_process_id','=',sign_process_id),
                                                           ('sign_user_id','=',sign_user_id)]).id
            else:
                institution_id = http_env['institution.account'].sudo().search([('org_num','=',org_or_account_num)]).id
                manual_id = http_manual_env.search([('sign_process_id', '=', sign_process_id),
                                                    ('sign_user_id', '=', sign_user_id),
                                                    ('institution_id','=',institution_id)]).id


            if sign_result == 2:
                http_manual_env.browse(manual_id).write({'sign_result':'success'})
                http.request._cr.commit()
                if approve_user_id != 'p':
                    record_obj.approve_action('approved','')
                else:
                    http_env[model].sudo().browse(int(record_id)).write({'partner_sign_state':'success'})

            elif sign_result == 3:
                http_manual_env.browse(manual_id).write({'sign_result':'failed'})
                if approve_user_id == 'p':
                    http_env[model].sudo().browse(int(record_id)).write({'partner_sign_state':'failed'})

            elif sign_result == 4:
                http_manual_env.browse(manual_id).write({'sign_result':'reject'})
                http.request._cr.commit()
                if approve_user_id != 'p':
                    record_obj.approve_action('reject', result_description)
                else:
                    http_env[model].sudo().browse(int(record_id)).write({'partner_sign_state':'reject','partner_reject_reason':result_description})

解决方案

@Dipen Shah@CoolFlash95@Charif DZ 大家好,我已经找到了这个问题的解决方案。但是当我列出解决方案时,我希望我们能够了解问题的根本原因,所以让我们来检查一下Odoo的源代码。

from odoo.http import JsonRequest--odoo version 10.0--line598

from odoo.http import JsonRequest--odoo version 11.0--line609

在Odoo10中 request = self.httprequest.stream.read(),然后self.jsonrequest = json.loads(request)

在Odoo11中 request=self.httprequest.get_data().decode(self.httprequest.charset),然后self.jsonrequest = json.loads(request)

我们发现JsonRequestself对象具有jsonrequest属性,即类型为DISCORD。遗憾的是,源代码不允许self具有‘Another’属性,该属性包含请求体中的原始字符串。但是添加‘Another’属性非常容易,何乐而不为呢?

我们可以使用setattr动态更改源代码中的方法。让我们更改JsonRequest的方法__init__,并添加另一个名为stream_str的属性。

如Odoo版本为10,python版本为2.7

# -*- coding: utf-8 -*-
import logging
from odoo.http import JsonRequest
import werkzeug
import json
_logger = logging.getLogger(__name__)

def __init__(self, *args):
    """
    We have copied the method __init__ directly from the source code and added 
    only one line of code to it
    """
    super(JsonRequest, self).__init__(*args)

    self.jsonp_handler = None

    args = self.httprequest.args
    jsonp = args.get('jsonp')
    self.jsonp = jsonp
    request = None
    request_id = args.get('id')

    if jsonp and self.httprequest.method == 'POST':
        # jsonp 2 steps step1 POST: save call
        def handler():
            self.session['jsonp_request_%s' % (request_id,)] = self.httprequest.form['r']
            self.session.modified = True
            headers = [('Content-Type', 'text/plain; charset=utf-8')]
            r = werkzeug.wrappers.Response(request_id, headers=headers)
            return r

        self.jsonp_handler = handler
        return
    elif jsonp and args.get('r'):
        # jsonp method GET
        request = args.get('r')
    elif jsonp and request_id:
        # jsonp 2 steps step2 GET: run and return result
        request = self.session.pop('jsonp_request_%s' % (request_id,), '{}')
    else:
        # regular jsonrpc2
        request = self.httprequest.stream.read()
        
        # We added this line of code,a new attribute named stream_str contains the origin string in request body when the request type is json. 
        self.stream_str = request

    # Read POST content or POST Form Data named "request"
    try:
        self.jsonrequest = json.loads(request)
    except ValueError:
        msg = 'Invalid JSON data: %r' % (request,)
        _logger.info('%s: %s', self.httprequest.path, msg)
        raise werkzeug.exceptions.BadRequest(msg)

    self.params = dict(self.jsonrequest.get("params", {}))
    self.context = self.params.pop('context', dict(self.session.context))

# Replacing the __init__ method in the source code with the new __init__ method, but without changing the source code
setattr(JsonRequest, '__init__', __init__)

在路由函数的定义中,我们可以这样做。

# -*- coding: utf-8 -*-
from odoo.http import Controller,route,request
class CallbackNotification(http.Controller):
    @route('/signature/process/my_odoo', type='json', auth='none')
        def receive_institution_auth(self, **kw):
            # When the type='json',the request is the object of JsonRequest,we can get the new attribute stream_str very easy!
            stream_str = request.stream_str

现在问题已解决。

相关文章