Рубрики
Software

Принимаем POST запросы в Odoo

Типичная задача.Допустим, нам нужно принимать какие-то данные из внешнего источника, обрабатывать их и возвращать результат.

Я не буду описывать всю логику, а сконцентрируюсь лишь на основных моментах при работе с контроллерами в Odoo. Как обычно, все делаем в отдельном модуле.

Наш контроллер:

# -*- coding: utf-8 -*-
from odoo import http

class CustomPostRoute(http.Controller):
    @http.route('/custom-route/', auth='public')
    def index(self, **kw):
        return "OK"

Обратите внимание. Мы используем auth=’public’. Благодаря этому нам не нужна авторизация для доступа к странице.

После перехода по адресу /custom-route/ мы увидим следующее:

Принимаем POST запросы в Odoo
На странице видим текст «ОК».

Сейчас наш URL доступен только по запросам: GET, HEAD, TRACE и OPTIONS. Это связано с тем, что начиная с Odoo 9 реализована CSRF защита.

Если отправить POST запрос, мы получим ошибку:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>Session expired (invalid CSRF token)</p>

Как отключить CSRF в Odoo?

Чтобы отключить CSRF для страницы, достаточно прописать в контроллере csrf=False. После этого, если мы отправим POST запрос, получим стандартный ответ «ОК».

Следующим действием, ограничим доступные методы. Разрешим только GET и POST запросы. В контроллере прописываем methods=[‘GET’, ‘POST’].

# -*- coding: utf-8 -*-
from odoo import http

class CustomPostRoute(http.Controller):
    @http.route('/custom-route/', auth='public', csrf=False, methods=['GET', 'POST'])
    def index(self, **kw):
        return "OK"

Теперь, если мы выполним любой другой метод, нас будет ожидать 405 ошибка с уведомлением, что этот метод запрещен:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>

Идем дальше. Давайте для каждого из методов будет возвращать разные ответы. Для этого получим значение метода, который используется и выведем разный текст:

# -*- coding: utf-8 -*-
from odoo import http

class CustomPostRoute(http.Controller):
    @http.route('/custom-route/', auth='public', csrf=False, methods=['GET', 'POST'])
    def index(self, **kw):
        request_method = http.request.httprequest.method

        if request_method == "GET":
            text = f"Запрос отправлен методом {request_method}!"
        else:
            request_data = 'не получены'
            text = f"Запрос отправлен методом {request_method}, данные: {request_data}."

        return text

Мы получили 2 разных текста, для каждого из запросов: «Запрос отправлен методом GET!» и «Запрос отправлен методом POST, данные: не получены.» Для пост запросов будем сохранять данные и выводить на экран в виде текста.

Как получить JSON данные в Odoo?

Чтобы получить JSON, нужно в контроллер добавить type=»json». По дефолту это значение равно «html». Теперь мы сможем принимать данные в JSON формате.

Учтите, что изменив type, у нас измениться Content-Type ответа. Вместо text/html будет application/json.

Пример POST запроса для Odoo с данными в json:

curl -i -X POST -H "Content-Type: application/json" -d '{"params": {"value":"8571519351"}}' 'localhost:8071/custom-route/'

Результатом:

HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 285
Set-Cookie: session_id=61f1b59ea1efba4c725708246946669c7677737a; Expires=Thu, 23-Apr-2020 20:33:39 GMT; Max-Age=7776000; HttpOnly; Path=/
Server: Werkzeug/0.14.1 Python/3.7.3
Date: Fri, 24 Jan 2020 20:33:39 GMT

{"jsonrpc": "2.0", "id": null, "result": "\u0417\u0430\u043f\u0440\u043e\u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u043c\u0435\u0442\u043e\u0434\u043e\u043c POST, \u0434\u0430\u043d\u043d\u044b\u0435: \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u044b."}

Сервер возвращает JSON в формате {«jsonrpc»: «2.0», «id»: null, «result»: «наш ответ»}.

Добавим возможность сохранения данных и их отображения в строке:

# -*- coding: utf-8 -*-
from odoo import http

class CustomPostRoute(http.Controller):
    @http.route('/custom-route/', auth='public', csrf=False, methods=['GET', 'POST'])
    def index(self, **kw):
        request_method = http.request.httprequest.method

        if request_method == "GET":
            text = f"Запрос отправлен методом {request_method}!"
        else:
            request_data = str(http.request.params)
            text = f"Запрос отправлен методом {request_method}, данные: {request_data}."

        return text

При помощи http.request.params мы забираем значение params из отправленых данных.

Если нет возможности или желания изменить входные данные, так чтобы данные, которые мы отправляем уходили в {«params»: }, можно использовать другой способ:

# -*- coding: utf-8 -*-
from odoo import http
import json

class CustomPostRoute(http.Controller):
    @http.route('/custom-route/', auth='public', csrf=False, methods=['GET', 'POST'])
    def index(self, **kw):
        request_method = http.request.httprequest.method

        if request_method == "GET":
            text = f"Запрос отправлен методом {request_method}!"
        else:
            request_data = str(http.request.params)
            
            if bool(request_data) == False:
                print(f"WORK WITH cache,  {request_data}")
                _cached_data = http.request.httprequest._cached_data
                request_data = json.loads(_cached_data.decode('utf-8'))
                request_data = str(request_data)

            text = f"Запрос отправлен методом {request_method}, данные: {request_data}."

        return text

Как видите, мы проверяем, есть ли что-то в params. Если ничего нет, забираем http.request.httprequest._cached_data, конвертируем utf-8 и JSON.

Код в статье не претендует на звание самого лучшего или эффективного решения. Это просто пример на скорую руку, как создать собственный эндпоинт в Odoo.