Помогите с POST запросом

tiktokpay

Участник
Автор темы
81
87
Версия MoonLoader
.026-beta
Пытаюсь сделать автоматические ответы на анаграммы в самп рп серверах, застопорился на отправке POST запроса на сайт. На выходе выдаёт шляпу, хотя вроде делал всё по гайду. С сервером куда делаю запрос, с ним всё в порядке, если делаю через самп - не получаеться.


Мой код:
local effil = require 'effil'

function main()
    repeat wait(0) until isSampAvailable()
    sampRegisterChatCommand("test", test_func)
    wait(-1)   
end

function test_func(arg)
    local data = {
        ['method'] = 'anagram',
        ['text'] = tostring(arg),
        ['count_symbol'] = tostring(#arg)
    }
    
    asyncHttpRequest('POST', 'https://rustxt.ru/api/index.php/', {data = encodeJson(data)},
         function(response)
            print(response.text)
         end,
         function(err)
            print(err)
         end)
end

function asyncHttpRequest(method, url, args, resolve, reject)
   local request_thread = effil.thread(function (method, url, args)
      local requests = require 'requests'
      local result, response = pcall(requests.request, method, url, args)
      if result then
         response.json, response.xml = nil, nil
         return true, response
      else
         return false, response
      end
   end)(method, url, args)
   if not resolve then resolve = function() end end
   if not reject then reject = function() end end
   lua_thread.create(function()
      local runner = request_thread
      while true do
         local status, err = runner:status()
         if not err then
            if status == 'completed' then
               local result, response = runner:get()
               if result then
                  resolve(response)
               else
                  reject(response)
               end
               return
            elseif status == 'canceled' then
               return reject(status)
            end
         else
            return reject(err)
         end
         wait(0)
      end
   end)
end

Снимок экрана (53).png

У них даже есть специальная страничка с гайдом, как пользоваться ихним Апи, но у меня не получается((

Притом что через онлайн сервис по POST запросам, всё выходит
Снимок экрана (54).png
Снимок экрана (55).png
 
Решение
Дело в том что в asyncHttpRequest нужно передавать тип string в data, если пытаюсь по твоему примеру, то выскакивает ошибка
Ну да, я и написал, что не помню. Тогда воспользуйся этим, и Content-Type теперь уже точно придется явным оставлять, не убирай.
Lua:
function url_encode(str)
    return string.gsub(str, "([^%w])", string.format("%%%02X", string.byte(str)))
end

function string_data(data)
    local s = ""
    for k,v in pairs(data) do
        s = s .. k .. "=" .. url_encode(v) .. "&"
    end
    return s:sub(0, -2)
end

-- string_data(data) юзнешь в asyncHttpRequest

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

moreveal

Известный
Проверенный
928
620
а почему ты думаешь, что данные в json формате передаются? в онлайн сервисе ведь через multipart/form-data (вообще для тестов запросов юзай postman лучш, удобнее)

я чекнул, x-www-form-urlencoded тоже принимает, поэтому код можно сильно и не усложнять:
Lua:
local effil = require 'effil'

function main()
    repeat wait(0) until isSampAvailable()
    sampRegisterChatCommand("test", test_func)
    wait(-1)
end

function test_func(arg)
    local data = {
        ['method'] = 'anagram',
        ['text'] = tostring(arg),
        ['count_symbol'] = tostring(#arg)
    }
 
    asyncHttpRequest('POST', 'https://rustxt.ru/api/index.php', {data = data, headers={['Content-Type'] = 'application/x-www-form-urlencoded'}}, -- возможно, явное указание типа излишне, можешь чекнуть и без него
         function(response)
            print(response.text)
         end,
         function(err)
            print(err)
         end
    )
end

function asyncHttpRequest(method, url, args, resolve, reject)
   local request_thread = effil.thread(function (method, url, args)
      local requests = require 'requests'
      local result, response = pcall(requests.request, method, url, args)
      if result then
         response.json, response.xml = nil, nil
         return true, response
      else
         return false, response
      end
   end)(method, url, args)
   if not resolve then resolve = function() end end
   if not reject then reject = function() end end
   lua_thread.create(function()
      local runner = request_thread
      while true do
         local status, err = runner:status()
         if not err then
            if status == 'completed' then
               local result, response = runner:get()
               if result then
                  resolve(response)
               else
                  reject(response)
               end
               return
            elseif status == 'canceled' then
               return reject(status)
            end
         else
            return reject(err)
         end
         wait(0)
      end
   end)
end

не проверял работоспособность, не особо помню как запросы через requests кидать, еще кста со своего айпишника ловил ddos-guard, с впн получилось только
 
Последнее редактирование:

Andrinall

Известный
701
518
Попробовал нормальный multipart-post отправить из луашника - выдало это)
Код:
<!doctype html><html><head><title>DDoS-Guard</title><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/> .... и т.д.

Lua:
local ssl       = require 'ssl.https'
local ltn12     = require 'ltn12'
local multipart = require 'multipart-post'

local function request(url, data)
    local response, result = {}, true
    assert(url ~= nil and type(url) == 'string', '!!')
            
    local response_body = {}
    local request = multipart.gen_request(data or {})
    request.url = url
    request.sink = ltn12.sink.table(response_body)
    local ok, status_code, _ = ssl.request(request)
    assert(ok, "not ok")
    assert(status_code == 200, '[' .. (url or '_none_') .. '] : Code ~= 200') -- may be change this
        
    response.text = table.concat(response_body, '\n') or ""
    result, response.json = pcall(decodeJson, response.text)
    response.status = ok and status_code == 200
    return response
end
 

tiktokpay

Участник
Автор темы
81
87
а почему ты думаешь, что данные в json формате передаются? в онлайн сервисе ведь через multipart/form-data (вообще для тестов запросов юзай postman лучш, удобнее)

я чекнул, x-www-form-urlencoded тоже принимает, поэтому код можно сильно и не усложнять:
Lua:
local effil = require 'effil'

function main()
    repeat wait(0) until isSampAvailable()
    sampRegisterChatCommand("test", test_func)
    wait(-1)
end

function test_func(arg)
    local data = {
        ['method'] = 'anagram',
        ['text'] = tostring(arg),
        ['count_symbol'] = tostring(#arg)
    }
 
    asyncHttpRequest('POST', 'https://rustxt.ru/api/index.php', {data = data, headers={['Content-Type'] = 'application/x-www-form-urlencoded'}}, -- возможно, явное указание типа излишне, можешь чекнуть и без него
         function(response)
            print(response.text)
         end,
         function(err)
            print(err)
         end
    )
end

function asyncHttpRequest(method, url, args, resolve, reject)
   local request_thread = effil.thread(function (method, url, args)
      local requests = require 'requests'
      local result, response = pcall(requests.request, method, url, args)
      if result then
         response.json, response.xml = nil, nil
         return true, response
      else
         return false, response
      end
   end)(method, url, args)
   if not resolve then resolve = function() end end
   if not reject then reject = function() end end
   lua_thread.create(function()
      local runner = request_thread
      while true do
         local status, err = runner:status()
         if not err then
            if status == 'completed' then
               local result, response = runner:get()
               if result then
                  resolve(response)
               else
                  reject(response)
               end
               return
            elseif status == 'canceled' then
               return reject(status)
            end
         else
            return reject(err)
         end
         wait(0)
      end
   end)
end

не проверял работоспособность, не особо помню как запросы через requests кидать, еще кста со своего айпишника ловил ddos-guard, с впн получилось только
Дело в том что в asyncHttpRequest нужно передавать тип string в data, если пытаюсь по твоему примеру, то выскакивает ошибка
Lua:
_annagram.lua: {C0C0C0}C:\SSDGames\SAMP\moonloader\lib\requests.lua:160: attempt to call method 'len' (a nil value)
request.lua:
request.headers['Content-Length'] = request.data:len()
Даже если пытаться задать длину отправляемой таблицы вручную, всё равно сыпяться ошибки.

Смотрел отправляемый трафик через WireShark, есть подозрение что самп не может передать русские символы, хотя только от них работает апи сайта. Но может это я не выбрал правильную кодировку для вайршарка, не знаю уже что делать 😭

Снимок экрана (56).png


Теперь правда уже ничего не приходит, моя ошибка в была в слеше(/) после url
Lua:
https://rustxt.ru/api/index.php/
Убрал, но всё равно в response.text пусто, пару раз правда ловил ddos guard, но когда использовал разный впн, ddos guard пропадал, но всё также
в response.text было пусто
Снимок экрана (58).png


Lua:
function test_func(arg)
    local data = {
        ['method'] = 'anagram',
        ['text'] = tostring(arg),
        ['count_symbol'] = tostring(#arg)
    }
    data = encodeJson(data)
    print(data)
   
    asyncHttpRequest('POST', 'https://rustxt.ru/api/index.php', {data = data, headers={['Content-Type'] = 'application/x-www-form-urlencoded'}},
         function(response)
            print(tostring(response.text))
         end,
         function(err)
            print(err)
         end)
end

Попробовал нормальный multipart-post отправить из луашника - выдало это)
Код:
<!doctype html><html><head><title>DDoS-Guard</title><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/> .... и т.д.

Lua:
local ssl       = require 'ssl.https'
local ltn12     = require 'ltn12'
local multipart = require 'multipart-post'

local function request(url, data)
    local response, result = {}, true
    assert(url ~= nil and type(url) == 'string', '!!')
          
    local response_body = {}
    local request = multipart.gen_request(data or {})
    request.url = url
    request.sink = ltn12.sink.table(response_body)
    local ok, status_code, _ = ssl.request(request)
    assert(ok, "not ok")
    assert(status_code == 200, '[' .. (url or '_none_') .. '] : Code ~= 200') -- may be change this
      
    response.text = table.concat(response_body, '\n') or ""
    result, response.json = pcall(decodeJson, response.text)
    response.status = ok and status_code == 200
    return response
end
А вот у меня получилось по твоему образцу отправить и даже получить кое что, только вот в response.text у меня нарушена кодировка, знаешь как исправить?
Lua:
local copas = require 'copas'
local ssl       = require 'ssl.https'
local ltn12     = require 'ltn12'
local multipart = require 'multipart-post'
local encoding = require 'encoding'
encoding.default = 'CP1251'
u8 = encoding.UTF8

local function request(url, data)
    local response, result = {}, true
    assert(url ~= nil and type(url) == 'string', '!!')
           
    local response_body = {}
    local request = multipart.gen_request(data or {})
    request.url = url
    request.sink = ltn12.sink.table(response_body)
    local ok, status_code, _ = ssl.request(request)
    assert(ok, "not ok")
    assert(status_code == 200, '[' .. (url or '_none_') .. '] : Code ~= 200') -- may be change this
       
    response.text = table.concat(response_body, '\n') or ""
    result, response.json = pcall(decodeJson, response.text)
    response.status = ok and status_code == 200
    return response
end

function main()
    repeat wait(0) until isSampAvailable()
    sampRegisterChatCommand("test", test_func)
    wait(-1)  
end

function test_func(arg)
    local url = 'https://rustxt.ru/api/index.php'
    local data = {
        ['method'] = 'anagram',
        ['text'] = u8(tostring(arg)),
        ['count_symbol'] = tostring(#arg)
    }
    local response = request(url, data)
    print(response.text) -- приходит то что надо, но с непонятными символами
end
Приход:
_annagram.lua: {C0C0C0}{"5":["текст","сетке","секст","секте","скете","стеке","тексе","теске","сеете","тесее","тесте","текке","тетке","ткете","сексе","кексе","ссеке"]}
И ещё в момент отправки запроса, в игре происходит микрофриз, не критично но тоже хочется от него избавится

UPD: Проблему с кодировкой решил, достаточно добавить u8:decode(response.text)
 
Последнее редактирование:
  • Нравится
Реакции: Andrinall

moreveal

Известный
Проверенный
928
620
Дело в том что в asyncHttpRequest нужно передавать тип string в data, если пытаюсь по твоему примеру, то выскакивает ошибка
Ну да, я и написал, что не помню. Тогда воспользуйся этим, и Content-Type теперь уже точно придется явным оставлять, не убирай.
Lua:
function url_encode(str)
    return string.gsub(str, "([^%w])", string.format("%%%02X", string.byte(str)))
end

function string_data(data)
    local s = ""
    for k,v in pairs(data) do
        s = s .. k .. "=" .. url_encode(v) .. "&"
    end
    return s:sub(0, -2)
end

-- string_data(data) юзнешь в asyncHttpRequest

Тот способ, что выше - как ты уже заметил, останавливает поток игры, делая оч заметный фриз, да и вообще сильно усложнен, когда есть проверенные временем функи
 
Последнее редактирование:
  • Нравится
Реакции: Andrinall и tiktokpay