在odoo的controllers.py文件中,如何获取post请求体中的json字符串?

In the controllers.py file of odoo, how to get the json string in the post request body?

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

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

不能使用直接转JSON字符串的方法(json.dumps()),因为这样得到的字符串和原来的JSON字符串不一样请求正文,会导致验证签名失败。

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

这是我的controllers.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})

要获取原始 body 数据,您可以使用以下代码:

raw_body_data = http.request.httprequest.data

pychong

您可以将 JSON-RPC 中的值发送到您的 Json controller

JS文件: 在像这样调用控制器之前传递值

var ajax = require('web.ajax');
ajax.jsonRpc("/custom/url", 'call', {'Your Key': Your Value}).then(function(data) {
    if (data) {
        // Code
    } else {
       // Code
}});

Py 文件: 像这样从 post 获取数据,

@http.route(['/custom/url'], type='json', auth="public", website=True)
def custom_cotroller(self, **post):
    get_data = post.get('Your Key')
    # Your Customise Code

谢谢

@Dipen Shah @CoolFlash95 @Charif DZ 大家好,我已经找到了这个问题的解决方案problem.But,我列出了解决方案,希望我们能够了解问题的根本原因,所以让我们检查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类型是dict.Unfortunately,源码不允许self要有'another'属性,其中包含请求中的原始字符串body.However,很容易添加'another'属性,为什么不呢?

我们可以使用 setattr 来动态更改源 code.Let 中的方法,更改 JsonRequest 的方法 __init__ 并添加另一个名为 [=23 的属性=].

eg.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

至此问题已经解决