Вопросы по HTTP API для взаимодействия стороннего софта с CapMonster.

  • Автор темы Автор темы DeimosBlack
  • Дата начала Дата начала

DeimosBlack

Новичок
Регистрация
21.10.2016
Сообщения
5
Реакции
0
Баллы
1
Доброго времени суток.
Не нашёл ответа на форуме и в документации и решил написать.

Занимаюсь разработкой клиентского приложения (на с++), в котором необходимо взаимодействовать с CapMonster.

Имеются:
- сервер с CapMonster 2;
- приложение, взаимодействующее с сервером по средством http-запросов;
- сторонний модуль для CM2, установленный и запущенный на капмонстре.

Описание проблемы:
1. Моё приложение правильно работает с AudioSolveMedia-капчей: сервер корректно получает mp3-файл, отправленный через POST form/multi-part и возвращает правильный ответ по GET-запросу.
В данном случае в POST-запрос добавляю поля:

Код:
Развернуть Свернуть Копировать
CapMonsterModule=ZennoLab.AudioSolveMedia
method=post
key={ключ от капмонста}
ParallelMode=true
file=файл(в ASCI)     // так же добавляю http-заголовки Content-Type="audio/mp3" и filename="audio.mp3"

В случае если убрать название файла и ParallelMode - тогда запрос проходит. При изменении других параметров - сервер возвращает ERROR в ответе на POST-запрос, вместо записи вида "OK|{id запущенного процесса на Монстре}". Т.е. если не указывать CapMonsterModule=ZennoLab.AudioSolveMedia, то сервер автоматически не определяет через какой модуль разгадывать капчу.

2. При аналогичном запросе на модуль (который подключен и запущен в КМ на сервере) в ответе на POST-запрос получаю ERROR словно переданы неверные данные (ключ и сам файл проверял ни раз). Файлом изначально является url-encoded data: image/png, base64.

Пробовал 2 варианта:
a. Конвертировать картину в обычный бинарник (в unicode, ASCI, UTF8 ) и отправлять так же, как аудиофайл выше.
b. Не конвертировать/принудительно переконвертировать изображение в base64 url-encoded, посылая следующий запрос:
Код:
Развернуть Свернуть Копировать
CapMonsterModule={НазваниеМодуля}  // название получил следуя инструкции (пкм по модулю в списке подключённых модулей в КМ и выбором пункта "копировать полное имя модуля")
method=base64
key={ключ от капмонста}
ParallelMode=true                  // и без этого поля
file=файл(в base64)                //http-заголовки Content-Type="image/png" [и filename="image.png]"


Итак. Вопросы:
1. Как формировать запрос на сервер так, что бы сервер сам определял тип нужного модуля? (видел в документации, что так можно но не понял как это реализовать).
Пробовал указывать CapMonsterModule ="", вообще не создавать это поле в запросе. Результат таких запросов всегда ERROR и для солвмедиа аудиокапчи, хотя с явным указанием - работает.
2. Как должен выглядеть полностью правильный http POST запрос при загрузке изображения на сервер с CapMonster (какие должны быть заголовки и что должно быть в теле)? (Или: что я делаю не так?)

PS: все запросы производятся на {ip сервера}/in.php
 
модуль сам определяется в капмонстре если я не ошибаюсь.
 
CapMonster2.dll думаю что так
CapMonster2.dll - это библиотека, являющаяся частью самого капмонстра. Капмонстр находится на сервере.

У меня же задача стоит сформировать правильный HTTP-запрос для обращения с другого компьютера к КМ, запущенному на сервере.
 
CapMonster2.dll - это библиотека, являющаяся частью самого капмонстра. Капмонстр находится на сервере.

У меня же задача стоит сформировать правильный HTTP-запрос для обращения с другого компьютера к КМ, запущенному на сервере.
http://zennolab.com/wiki/ru:addons:capmonster:sm-audio посмотри как тут,у меня тоже кап стоит на серваке а зенки на других серваках.я проста в настройках указываю ip сервака.
 
http://zennolab.com/wiki/ru:addons:capmonster:sm-audio посмотри как тут,у меня тоже кап стоит на серваке а зенки на других серваках.я проста в настройках указываю ip сервака.
Уже смотрел. В том примере прямой запрос к модулю
ZennoPoster.CaptchaRecognition("CapMonster2.dll", str, "CapMonsterModule=ZennoLab.AudioSolveMedia&ParallelMode=true");
А обращение к библиотеке дабы это из зенопостера . Я напрямую работую через HTTP.
 
2. Как должен выглядеть полностью правильный http POST запрос при загрузке изображения на сервер с CapMonster (какие должны быть заголовки и что должно быть в теле)? (Или: что я делаю не так?)

Вопрос решился.

Про запрос без указания мрдуля - вопрос актуален.
 
ну если не жалко, для новичков будущих выложи как подрубился что бы потом меньше вопросов было о том что есть давно на форуме
 
Уже смотрел. В том примере прямой запрос к модулю
ZennoPoster.CaptchaRecognition("CapMonster2.dll", str, "CapMonsterModule=ZennoLab.AudioSolveMedia&ParallelMode=true");
А обращение к библиотеке дабы это из зенопостера . Я напрямую работую через HTTP.

Запрос без доп параметров, с автоопределением капчи, по возможности.
Код:
Развернуть Свернуть Копировать
POST http://capmonster2.com/in.php HTTP/1.1
User-Agent: TubeCast v1.0
Accept: */*
Accept-Language: ru
Content-Type: multipart/form-data; boundary=8d3fe5455aca93e
Host: 192.168.1.31:88
Content-Length: 6335

--8d3fe5455aca93e
Content-Disposition: form-data; name="method"

post
--8d3fe5455aca93e
Content-Disposition: form-data; name="soft_id"

19
--8d3fe5455aca93e
Content-Disposition: form-data; name="key"

1234567890
--8d3fe5455aca93e
Content-Disposition: form-data; name="file"; filename="Captcha.png"
Content-Type: image/png
.........
--8d3fe5455aca93e--

Только аудио он на автомате вряд ли определит, т.к. есть еще гугл аудио распознавание, а понять какой мп3 к кому относится проблематичнее , чем сортировать картинки по размерам.
 
Класс

C#:
Развернуть Свернуть Копировать
 public static class FormUpload
    {
        private static readonly Encoding encoding = Encoding.UTF8;
        public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters)
        {
            string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());
            string contentType = "multipart/form-data; boundary=" + formDataBoundary;

            byte[] formData = GetMultipartFormData(postParameters, formDataBoundary);

            return PostForm(postUrl, userAgent, contentType, formData);
        }
        private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData)
        {
            HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;

            if (request == null)
            {
                throw new NullReferenceException("request is not a http request");
            }

            // Set up the request properties.
            request.Method = "POST";
            request.ContentType = contentType;
            request.UserAgent = userAgent;
            request.CookieContainer = new CookieContainer();
            request.ContentLength = formData.Length;

            // You could add authentication here as well if needed:
            // request.PreAuthenticate = true;
            // request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
            // request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes("username" + ":" + "password")));

            // Send the form data to the request.
            using (Stream requestStream = request.GetRequestStream())
            {
                requestStream.Write(formData, 0, formData.Length);
                requestStream.Close();
            }

            return request.GetResponse() as HttpWebResponse;
        }

        private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
        {
            Stream formDataStream = new System.IO.MemoryStream();
            bool needsCLRF = false;

            foreach (var param in postParameters)
            {
                // Thanks to feedback from commenters, add a CRLF to allow multiple parameters to be added.
                // Skip it on the first parameter, add it to subsequent parameters.
                if (needsCLRF)
                    formDataStream.Write(encoding.GetBytes("\r\n"), 0, encoding.GetByteCount("\r\n"));

                needsCLRF = true;

                if (param.Value is FileParameter)
                {
                    FileParameter fileToUpload = (FileParameter)param.Value;

                    // Add just the first part of this param, since we will write the file data directly to the Stream
                    string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n",
                        boundary,
                        param.Key,
                        fileToUpload.FileName ?? param.Key,
                        fileToUpload.ContentType ?? "application/octet-stream");

                    formDataStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header));

                    // Write the file data directly to the Stream, rather than serializing it to a string.
                    formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
                }
                else
                {
                    string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
                        boundary,
                        param.Key,
                        param.Value);
                    formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
                }
            }

            // Add the end of the request.  Start with a newline
            string footer = "\r\n--" + boundary + "--\r\n";
            formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));

            // Dump the Stream into a byte[]
            formDataStream.Position = 0;
            byte[] formData = new byte[formDataStream.Length];
            formDataStream.Read(formData, 0, formData.Length);
            formDataStream.Close();

            return formData;
        }

        public class FileParameter
        {
            public byte[] File { get; set; }
            public string FileName { get; set; }
            public string ContentType { get; set; }
            public FileParameter(byte[] file) : this(file, null) { }
            public FileParameter(byte[] file, string filename) : this(file, filename, null) { }
            public FileParameter(byte[] file, string filename, string contenttype)
            {
                File = file;
                FileName = filename;
                ContentType = contenttype;
            }
        }
    }


Так юзать

C#:
Развернуть Свернуть Копировать
string host = "http://127.0.0.3/in.php";
string file = @"img.png";
string mime = "image/png";
string ua = "Bot 1.0";

// Read file data
FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
fs.Close();

// Generate post objects
Dictionary<string, object> postParameters = new Dictionary<string, object>();
postParameters.Add("CapMonsterModule", "ZennoLab.Bitrix1");
postParameters.Add("file", new FormUpload.FileParameter(data, Path.GetFileName(file), mime));

// Create request and receive response
HttpWebResponse webResponse = FormUpload.MultipartFormDataPost(host, ua, postParameters);

// Process response
StreamReader responseReader = new StreamReader(webResponse.GetResponseStream());
string fullResponse = responseReader.ReadToEnd();
webResponse.Close();
//return fullResponse;


https://ru.wikipedia.org/wiki/Multipart/form-data
 
Код:
Развернуть Свернуть Копировать
HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;
            if (request == null)
            {
                throw new NullReferenceException("request is not a http request");
            }
Зачем создавать объект и потом проверять его на null, если итак ясно, что он будет не null?
В моем понимании правильный код будет такой:
Код:
Развернуть Свернуть Копировать
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(postUrl);
а тут using автоматически закроет поток и close не надо вызывать:
Код:
Развернуть Свернуть Копировать
using (Stream requestStream = request.GetRequestStream())
{
       requestStream.Write(formData, 0, formData.Length);
       requestStream.Close();
}
 
Последнее редактирование:
А ты проверь его в ситуации когда сервак ответит 4хх или 5хх кодом)

ревьювер))
Так для этого есть блок try-catch и обернуть им надо запрос к серверу, а не создание объекта
 
Так для этого есть блок try-catch и обернуть им надо запрос к серверу, а не создание объекта

Моя твоя не понимать.
Судя по вашим постам, Вы очень поверхностно знакомы с программированием, в частности с С#, но постоянно пытаетесь из себя строить гуру, это какая-то новая модная фишка ?
 
  • Спасибо
Реакции: doc
Так для этого есть блок try-catch и обернуть им надо запрос к серверу, а не создание объекта
ну да) чето типа
Код:
Развернуть Свернуть Копировать
        try
        {
            return (HttpWebResponse)req.GetResponse();
        }
        catch (WebException we)
        {
            var resp = we.Response as HttpWebResponse;
            if (resp == null)
                throw;
            return resp;
        }

просто это копипаста перваа из гугла по запросу C# multipart/form-data ))

Моя твоя не понимать.
Судя по вашим постам, Вы очень поверхностно знакомы с программированием, в частности с С#, но постоянно пытаетесь из себя строить гуру, это какая-то новая модная фишка ?

ну да) тут мало кодеров имеющих девелоперские сертификаты MS)
любители кодеры и любители ревьюверы)

и все это оффтоп для сабжа, тс пишет на крестах)
а ему шарп суют)

хотя проще дотпиком открыть CapMonster2.dll и дернуть реализацию от разрабов)
 
Последнее редактирование:
  • Спасибо
Реакции: Adigen
Моя твоя не понимать.
Судя по вашим постам, Вы очень поверхностно знакомы с программированием, в частности с С#, но постоянно пытаетесь из себя строить гуру, это какая-то новая модная фишка ?
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(postUrl);
Покажи мне где тут null объект создается.
Или ты может хочешь сказать, что ты сами веб запросы строишь без обработки исключений?
ну да) чето типа
Код:
Развернуть Свернуть Копировать
        try
        {
            return (HttpWebResponse)req.GetResponse();
        }
        catch (WebException we)
        {
            var resp = we.Response as HttpWebResponse;
            if (resp == null)
                throw;
            return resp;
        }

просто это копипаста перваа из гугла по запросу C# multipart/form-data ))
Я не про момент запроса написал, а про создание объекта HttpWebResponse и потом сразу же проверку на null. С самим запросом итак понятно, что его в любом случае надо оборачивать в блок try catch
P.S. про копипаст итак было понятно, исходя из комментариев к коду
 
Последнее редактирование:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(postUrl);
Покажи мне где тут null объект создается.
Или ты может хочешь сказать, что ты сами веб запросы строишь без обработки исключений?

Я не про момент запроса написал, а про создание объекта HttpWebResponse и потом сразу же проверку на null. С самим запросом итак понятно, что его в любом случае надо оборачивать в блок try catch

ключевой момент тут
WebRequest.... as HttpWebRequest;
тошо у HttpWebRequest нет возвращаемого типа, поэтому и чекаем на null)

понимаю тебя, ломает мозг это, но так прикольнулись архитекторы дотнета, в частности те кто пилил этот класс)) это с этим классом такая шляпа)
 
ключевой момент тут
WebRequest.... as HttpWebRequest;
тошо у HttpWebRequest нет возвращаемого типа, поэтому и чекаем на null)
Этой строчкой мы вообще никак не получим null объект, так как мы можем WebRequest привести к типу HttpWebRequest.
Можно получить исключение, если сделать так (но так никто не делает):
Код:
Развернуть Свернуть Копировать
WebRequest wr;
HttpWebRequest request = (HttpWebRequest)wr;
P.S. даже если пробежаться по примерам из поисковика, то так (проверку на null) никто не делает, кроме тех, кто вообще ничего не понимает в c#.
 
Последнее редактирование:
ну я даже хз тогда кто мсдн доки набирает
https://msdn.microsoft.com/ru-ru/library/system.net.httpwebrequest.abort(v=vs.110).aspx


Код:
Развернуть Свернуть Копировать
private static void TimeoutCallback(object state, bool timedOut) {
      if (timedOut) {
          HttpWebRequest request = state as HttpWebRequest;
          if (request != null) {
              request.Abort();
          }
      }
  }

че эт они вдруг чекают на налл перед вызовом аборта)

вот еще
https://msdn.microsoft.com/ru-ru/library/hh221581.aspx



Код:
Развернуть Свернуть Копировать
HttpWebRequest request = null;
request = WebRequest.Create("http://site.com") as HttpWebRequest;

это все нужно для целостности данных.
ноги растут отсюда
https://msdn.microsoft.com/ru-ru/library/system.net.webexception.status(v=vs.110).aspx
 
Последнее редактирование:
Во на мсдн пояснения есть в комментариях.
все оказывается разжевано че так чекать на null нужно )

https://msdn.microsoft.com/ru-ru/library/system.net.httpwebrequest(v=vs.110).aspx
https://msdn.microsoft.com/ru-ru/library/system.net.webexception.response(v=vs.110).aspx


Значение свойства
Type: System.Net.WebResponse
Если ответ доступен из Интернет-ресурса, WebResponse экземпляр, содержащий отклик из Интернет-ресурса; в противном случае — null.


кусок из класса того
Код:
Развернуть Свернуть Копировать
HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;
            if (request == null)
            {
                throw new NullReferenceException("request is not a http request");
            }

После вызова метода Create мы чекаем request. Он будет null в случае 4хх и 5хх ответов.


Запудрил мне мозг с обьектом своим, когда мы сразу стат. метод Create дергаем)
Аминь)


п.с. тс зайдет охереет че мы тут развели, у него кресты)
 
Во на мсдн пояснения есть в комментариях.
все оказывается разжевано че так чекать на null нужно )

https://msdn.microsoft.com/ru-ru/library/system.net.httpwebrequest(v=vs.110).aspx
https://msdn.microsoft.com/ru-ru/library/system.net.webexception.response(v=vs.110).aspx





кусок из класса того
Код:
Развернуть Свернуть Копировать
HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;
            if (request == null)
            {
                throw new NullReferenceException("request is not a http request");
            }

После вызова метода Create мы чекаем request. Он будет null в случае 4хх и 5хх ответов.


Запудрил мне мозг с обьектом своим, когда мы сразу стат. метод Create дергаем)
Аминь)


п.с. тс зайдет охереет че мы тут развели, у него кресты)
Самих запросов к сайту этот метод никаких не делает, максимум он может через DNS узнать о наличии соответствия адреса в интернете какому-либо ip, но опять же про этот механизм не уверен. В приведенных майками примерах тоже не нашел проверку на null после создания объекта HttpWebRequest, например тут:

Код:
Развернуть Свернуть Копировать
Uri myUri =new Uri("http://www.contoso.com"); 
WebRequest myWebRequest= WebRequest.Create(myUri);
WebResponse myWebResponse= myWebRequest.GetResponse();
В этом примере они не проверяют на null объект, а если он не может стать null при вызове метода Create, следовательно мы можем его привести к типу HttpWebRequest и потом не производить проверку на NullReferenseException.
В том-то и дело, что WebResponse как раз и может выдать ошибку, а WebRequest - нет.
P.S. да уж, ТС реально охренеет от написанного тут
 
ну емае...Карл!)

https://msdn.microsoft.com/ru-ru/library/system.net.httpwebrequest(v=vs.110).aspx
upload_2016-10-27_18-0-44.png



https://msdn.microsoft.com/ru-ru/library/system.net.webexception.response(v=vs.110).aspx

upload_2016-10-27_18-16-37.png
 
Таки все правильно, но это WebResponse отдает ошибку, а WebRequest только создает объект и от него нет ошибки. Я сам раньше путался в них, потом просто запомнил, что по-английски слово response - ответ, а request - требование, запрос.
Все равно же не твой код, так что можешь не отстаивать криворукость его создателя.
P.S. я бы сам без подсказок с инета не написал бы запрос multipart для HttpClient, так как практика почти нулевая в c#, но теорию учу постоянно.
 

Кто просматривает тему: (Всего: 0, Пользователи: 0, Гости: 0)