Есть необходимость отправлять динамические структуры как в js, к примеру через тип JObject, но получаем 500 когда пробуем в веб-сервисе вернуть данный тип.



Как сделать что-то типо такого: 

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
}

 

.net core

Нравится

2 комментария

Стас, добрый день!

Для сериализации/десериализации вы можете в сервисе использовать либу Newtonsoft.

Для этого сперва нужно добавить следующие using:

using Newtonsoft.Json;

using Newtonsoft.Json.Linq;

После чего в методе сервиса вызвать:

var content = JsonConvert.SerializeObject(objectToSerialize);

Хочется что бы это был стандартный сериализатор, а не возвращать строку которая ещё раз будет сериализирована стандартным .net  сериализатором. 

Показать все комментарии

Веб-сервис.

Как для GET метода передать кастомный тип объекта во входных параметрах?

Есть тип:

    [DataContract]

    public class SomeType {

        [DataMember]

        public string path { get; set; }

        [DataMember]

        public string als { get; set; }

    }




Есть метод: 

[OperationContract]

        [WebInvoke(Method = "GET", 

            RequestFormat = WebMessageFormat.Json, 

            BodyStyle = WebMessageBodyStyle.Wrapped,

            ResponseFormat = WebMessageFormat.Json,

            UriTemplate = "/ping/{s}")]

            [return: MessageParameter(Name = "data")]

        public object Pong(string s, SomeType SomeData) {

            return JsonConvert.SerializeObject(SomeData);

        }

При попытке отправить гет запрос через постман и передать данные, получаем всегда ответ: 



"data": "{\"path\":null,\"als\":null}

 

Что по факту равно если вообще не передавать параметр.



Но если мы делаем метод POST и передаем в теле этот же параметр вот так:

{

    "SomeData":  {

        "path": "Name",

        "als": "name"

    }

}

 

То работает, чего я не понимаю? 

Нравится

5 комментариев

Я не помню, что бы в get запросе можно было передавать тело запроса анонимно,  для такого метод post предназначен, а get данные через параметры в url всегда передаются.

можно попробовать принять json из урла , сделав так, но это уже извращение

`?someBody={key:"value"}`? 

Dima Avdoshin,

тело запроса это же просто данные, какая разница что передавать данные если их конечно не много через гет или через пост? особенно если ты хочешь только читать данные

Dima Avdoshin,

вопрос в тому как передать в гет запросе данные так что бы это потому преобразовалось в объект, потому как с List<Guid>  передается и все норм, я вот думаю в чем разница между Guid и моими типами? скрин приложу как передаю

ответ: 

{

    "data": "{\"path\":null,\"als\":null}[\"c31b1382-d7c1-4318-a241-16a64c825720\"]sasdasdasd"

}

Стас Гаврилюк,

к сожалению я не смог сделать так как ты, вероятно может получится с помощью класса обертки (код ниже), где в knowType указываются все возвращаемые типы, можешь назвать его как указано ниже

 

[DataContract]
[KnownType(typeof(OpenIdStatus))]
[KnownType(typeof(string))]
public class ReturnValue
{
    [DataMember]
    public object Value { get; set; }
}

также для меня работало следующее

/ping/12?someObj='{"path":"dsadsadas","als":"dsadsadasdsadasdasdsa"}'

тоже самое и с обжектом работает

[OperationContract]
        [WebInvoke(Method = "GET", 
            RequestFormat = WebMessageFormat.Json, 
            BodyStyle = WebMessageBodyStyle.Wrapped,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "/ping/{s}?someObj={SomeData}")]
            [return: MessageParameter(Name = "data")]
        public dynamic  Pong(string s, dynamic SomeData) {
            return JsonConvert.DeserializeObject(SomeData);
        }

 

параметр ввиде объекта определенного типа не принимает , не смог нагуглить почему, пишет , что 

>Операция &quot;Pong&quot; в контракте &quot;WebService1C&quot; содержит переменную запроса с именем

        &quot;SomeData&quot; и типом &quot;Terrasoft.Configuration.SomeType&quot;, но тип

        &quot;Terrasoft.Configuration.SomeType&quot; не является преобразуемым посредством

        &quot;QueryStringConverter&quot;. Переменные для переменных запроса UriTemplate должны иметь типы, которые могут

        преобразовываться при помощи &quot;QueryStringConverter&quot;.

Вероятно тут дело в самой сути GET POST запросов. Если вариант предложенный Дмитрием не сработает, то тут останется только использовать POST запрос.

Показать все комментарии

Нужно для правильной возвращаемой структуры типов CompositeObject, без Key/Value, а нативно - {"key": value}

 

Нравится

3 комментария

нативно, имеешь ввиду через jobject? или имеешь ввиду задать column key кастомный, если оно, то аннотация(не помню как с# это называется) jsonproperty может помочь наверное

[JsonProperty("Ref_Key", NullValueHandling = NullValueHandling.Ignore)]
или имеешь ввиду избавиться от имени метода в возвращаемом объекте?
тогда наверное стоит юзать это BodyStyle = WebMessageBodyStyle.Bare в аннотации

 [OperationContract]

        [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json,RequestFormat =

        WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]

в этом случае, тебе не надо будет писать название объекта передаваемого в тело метода и возвращаемом тоже не будет его в названии

типа так user:{name:"3213"} станет {name:"3232"}

 

Dima Avdoshin,

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

 

задача сама в том что возвращать сразу красиво, а не через строку  через - JsonConvert.SerializeObject.



сейчас на такой код в сервисе: 

 

        [OperationContract]

        [WebInvoke(Method = "GET", 

            RequestFormat = WebMessageFormat.Json, 

            BodyStyle = WebMessageBodyStyle.Wrapped,

            ResponseFormat = WebMessageFormat.Json,

            UriTemplate = "/ping/{s}")]

            [return: MessageParameter(Name = "data")]

        public object Pong(string s) {

            var res = new CompositeObject();

            res.Add("ping", "pong");

            return res;

        }

 

получаем такой ответ: 

 

{

"data": [

{

"Key": "ping",

"Value": "pong"

}

]

}

 

а надо: {"data":  [{ "ping": "pong"}]} или если короче, то что возвращает JsonConvert.

Стас Гаврилюк,

не решал такого рода задачу, но вот что нагуглил https://stackoverflow.com/questions/17806811/dynamic-objects-in-wcf-not…

Показать все комментарии
Лучший ответ
[OperationContract]
        [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped,
            RequestFormat = WebMessageFormat.Json,
            ResponseFormat = WebMessageFormat.Json,
             UriTemplate = "/UpdateUser/{id}")]
        [return: MessageParameter(Name = "result")]
 
        public Result UpdateUser(PortalUser user, string id){}

UriTemplate = "/UpdateUser/{id}" если я тебя правильно понял

[OperationContract]
        [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped,
            RequestFormat = WebMessageFormat.Json,
            ResponseFormat = WebMessageFormat.Json,
             UriTemplate = "/UpdateUser/{id}")]
        [return: MessageParameter(Name = "result")]
 
        public Result UpdateUser(PortalUser user, string id){}

UriTemplate = "/UpdateUser/{id}" если я тебя правильно понял

Dima Avdoshin, 

дякую :)

Показать все комментарии

Добрый день.



Подскажите, пожалуйста, есть ли возможность в web service для "data type: object" установить тип данных объект, а не массив с объектами?



https://prnt.sc/132ydy4

Нравится

1 комментарий

Добрый день!

Нет, подобную настройку выполнить нельзя. Это не имеет смысла поскольку сам объект это есть контейнер для других полей. Уточните какую задачу Вы решаете, возможно, мы сможем подсказать как ее можно будет выполнить.

Показать все комментарии

Вызвал сервис методом callService, но не понимаю, как получить данные из своего сервиса. В методе сервиса возвращается аргумент типа String. Хотел бы его на клиентском коде как-то вывести.

runService: function () {
				ServiceHelper.callService({
					serviceName: "CustomService",
					methodName: "ReturnCurrentUser",
					callback: function() {
                        Terrasoft.utils.showMessage({
                            caption: "Сервис запустил ",
                            buttons: [Terrasoft.MessageBoxButtons.OK.returnCode],
                            defaultButton: 0,
                            scope: this
                        });
					},
					scope: this
				}, this);
			},

 

Нравится

1 комментарий
Лучший ответ

в аргументе метода

callService передайте параметр callback: function(response) {
var answer = response.ReturnCurrentUserResult;
ваш обработчик ответа
}
И scope: this 

В самом callService this не нужен

 

в аргументе метода

callService передайте параметр callback: function(response) {
var answer = response.ReturnCurrentUserResult;
ваш обработчик ответа
}
И scope: this 

В самом callService this не нужен

 

Показать все комментарии

Добрый день, коллеги!

Есть задача по интеграции сайта со сторонним приложением. На стороне реализовано api с рядом запросов. Но реализация сторонних запросов такова, что мне приходится в бизнес процессе вызывать запросы в цикле. Может кто-то подскажет вариант вызова вэб-сервиса в тексте задания-сценария БП?

Нравится

1 комментарий

Ольга, можно либо самостоятельно реализовать логику вызова сервиса кодом (пример есть тут), либо настроить нужный веб-сервис в разделе обычным образом, а затем взять код запуска в схеме WebServiceUserTask и скопировать его в функцию в разрабатываемом БП вместе со всеми вспомогательными функциями и параметрами, которую затем запускайте в своём скрипте в цикле. Вот основная логика оттуда:

if (ServiceUrl == Guid.Empty || string.IsNullOrEmpty(ServiceMethod)) {
	return true;
}
string serviceUrl = (new Select(UserConnection)
	.Column("URL")
	.From("WebServiceURL")
	.Where("Id").IsEqual(new QueryParameter("Id", ServiceUrl)) as Select)
	.ExecuteScalar&lt;string&gt;();
HttpWebRequest webRequest = CreateWebRequest(serviceUrl, ServiceMethod);
string Request = @"&lt;soap:Envelope xmlns:soap=""http://www.w3.org/2003/05/soap-envelope"" xmlns:web=""http://www.webserviceX.NET/""&gt;
&lt;soap:Header/&gt;
	&lt;soap:Body&gt;";
Request += RequestBodyInternal;
Request += @"
	&lt;/soap:Body&gt;
&lt;/soap:Envelope&gt;";
webRequest.ContentLength = Request.Length;
using (Stream stream = webRequest.GetRequestStream()) {
	using (StreamWriter streamWriter = new StreamWriter(stream)) {
		streamWriter.Write(Request);
		streamWriter.Close();
	}
}
using (WebResponse response = webRequest.GetResponse()) {
	using (StreamReader rd = new StreamReader(response.GetResponseStream())) { 
		Responce = rd.ReadToEnd();
	}
}
PrepareResponceResult();
if (!IsLoggingRequestAndResponce) {
	Request = string.Empty;
	Responce = string.Empty;
}
return true;

 

Показать все комментарии

Коллеги всем доброго времени суток!

Столкнулся со следующей проблемой, обработки исключений возвращаемых при запросе к внешнему API.

Делаю запрос через WebClient

 

public void Patch(string method, Dictionary<string, string> parameters)
        {
            try
            {
                NameValueCollection parametersValues = new NameValueCollection();
 
                foreach (var parameter in parameters)
                {
                    if (String.IsNullOrEmpty(parameter.Value))
                    {
                        parametersValues.Add(parameter.Key, null);
                        continue;
                    }
                    parametersValues.Add(parameter.Key, parameter.Value);
                }
 
                string uri = $"{abUrl}{method}";
                using (WebClient webClient = new WebClient())
                {
                    string credentials = Convert.ToBase64String(
                        Encoding.ASCII.GetBytes(LoginDev + ":" + PassDev));
                    webClient.Headers[HttpRequestHeader.Authorization] = $"Basic {credentials}";
                    webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                    byte[] result = webClient.UploadValues(uri, "PATCH", parametersValues);
                    var response = Encoding.UTF8.GetString(result);
                }
 
            }
            catch (WebException e)
            {
                var exceptionMessage = e.Message;
                if (e.Status.Equals(WebExceptionStatus.ProtocolError))
                {
                    if (e.Response is HttpWebResponse)
                    {
                        var sr = new StreamReader(e.Response.GetResponseStream());
                        var response = sr.ReadToEnd();
                        sr.Close();
                        sr.Dispose();
                    }
                }
            }
        }

Когда пытаюсь обработать WebException в Bpm то мне записывает в логи - http://prntscr.com/kr7aja

 

При выполнении данного кода через консольное приложение обработка срабатывает корректно - http://prntscr.com/kr7baj

Подскажите в чем может быть проблема, как бороться?

Заранее благодарен.

Нравится

6 комментариев

Попробуйте, вместо public void Patch сделать public string Patch, объявите переменную, например result и в catch, сделайте result += e.Message, ну и по итогу соответственно return result

Литвинко Павел,

я в самом методе Patch в блоке catch логирую 

e.Message - http://prntscr.com/kr838h

в Bpm у меня метод Patch возвращает string. Суть в другом почему в catch обрабатывается Too many redirects, а при выполнении данного метода из консольного приложения обрабатывается корректно, то что возвращает сервер к которому запрос был.

Нигрескул Алексей,

А в консоли, на этом прерывается выполнение? Повторно не вызывается? Возможно он просто неоднократно вызывается и по итогу в лог попадает конечная ошибка

Литвинко Павел,

В консоли - при выполнении кода вылетает в catch и потом на выход метода 

При выполнении метода из консоли запустил fiddler запрос ушел один раз - http://prntscr.com/kr8oum

Попробуйте в Fiddler сравнить запросы, уходящие на сервер при работе как из сайта, так и отдельной программы. Видимо, там будет отличие если не в тексте, то заголовках запроса, приводящее к бесконечным редиректам.

Показать все комментарии

Здравствуйте, коллеги!

В ходе мероприятия в Сколково на презентации дизайнера процессов коснулись темы вызова внешних веб-сервисов из процесса с использованием нового элемента в дизайнере процессов.

К сожалению, не нашел подробного описания возможностей этого элемента в технической документации.

Прошу помочь с реализацией следующей функции.

Имеется веб-сервис, имеющий корректное wsdl-определение. Сервис получает от процесса GET-команду (с параметром type ['jpeg', 'pdf', 'xml']) и в зависимости от параметра возвращает:
1) jpeg - изображение
2) pdf - файл документа
2) xml - со значениями полей в строках и изображениями в кодировке base64

по получении ответа процесс должен:
1) сохранить jpeg-файл в карточку контакта (например, фото контакта)
2) сохранить pdf-файл вложением в карточку контакта (например, цифровая копия подписанного договора обслуживания)
3) разобрать полученный xml-файл, создать новую карточку контакта и сохранить значения в соответствующие поля, включая закодированное base64 изображение (например, паспортные данные контакта с изображением подписи, фото и скана паспорта)

Буду благодарен за примеры кода.

Нравится

4 комментария

Добрый день!
Пока такого элемента, который бы вызывал внешние сервисы нет.
Для решения подобной задачи нужно писать в ScriptTask’е на .Net обработчик http запросов и в пространство имен (Usings) подключать нужные классы.
Примеры кода можно загуглить.

Спасибо, Олег!

Я посмотрел в дизайнере процессов установленного у нас экземпляра bpm'online 7.8. Там действительно нет того элемента, что я имел ввиду. Жаль, что я не помню его названия. Но в бета-версии, которую нам демонстрировали на мероприятии в Сколково в этом году в рамках сессии для администраторов, был такой элемент, который позволял вызывать внешние web-сервисы, при условии, что они оформлены в соответствии со стандартами по протоколу SOAP.

Очень прошу перепроверить с теми, кто делал ту презентацию. Юлия Старун там была, насколько я помню.

Заранее спасибо!

Добрый день !!!

если такой элемент процесса появится в дизайнере процессов, тогда BPMOnline станет самой крутой системой по сравнению с другими. Она и сейчас самая крутая система. Но с данным инструментом просто будет решать многие задачи.

Здравствуйте! Я зашел в дизайнер процессов из конфигурации системы. Элемент, который я имел ввиду называется "Выполнить действие процесса". В его настройках нужно указать Пользовательское действие "Вызвать веб-сервис" или "Вызвать Web-сервис".

Если выбрать первый вариант - "Вызвать веб-сервис", то во вкладке Параметров нужно указать следующие параметры:
- RequestBodyInternal
- RequestParameters
- URL сервиса
- Вернуть запрос и ответ действия
- Запрос
- Метод сервиса
- Ответ
- Параметры запроса
- Результат запроса
- Сервис
- Статус выполнения вызова

Если выбрать второй вариант - "Вызвать Web-сервис", то во вкладке Параметров нужно указать следующие параметры:
- URL web-сервиса
- Метод
- Название
- Параметры метода

Мой SOAP сервис вызывается (из локальной сети) по адресу:
http://[ip_address]/soap?format=pdf
http://[ip_address]/soap?format=jpg
http://[ip_address]/soap?format=xml

Его описание доступно по
http://[ip_address]/soap?wsdl

Текст WSDL во вложении (нужно изменить расширение с txt на xml).

Пример XML-ответа - во вложении (нужно изменить расширение с txt на xml).
Пример PDF-ответа - во вложении.
Пример JPG-ответа - во вложении.

Это была вводная. Сама задача была описана в исходном сообщении темы. Подозреваю, что мне нужно использовать первый вариант ("Вызвать веб-сервис") - у него вроде больше настраиваемых параметров. Теперь вопрос знатокам (или "подсказка зала" - уж как повезет :) :

Кто подскажет, как использовать этот элемент? Если можно - с практическими примерами.

Заранее спасибо!

Показать все комментарии

Взявся розбиратися з написанням web-сервісів.
З початковою операцією сервісу [http://bpmonline-dev.local/ServiceModel/AuthService.svc/Login] питань не виникло.
Авторизуюсь, витягую coocies. Все добре.

Але є запитання з подальшою технологією роботи.

[http://localhost/ServiceModel/AuthService.svc/Help] видає варіанти використання сервісу:
Uri Method Description
GetSolutions
POST
Service at http://localhost/ServiceModel/AuthService.svc/GetSolutions

GetSolutionsData
POST
Service at http://localhost/ServiceModel/AuthService.svc/GetSolutionsData

Login
POST
Service at http://localhost/ServiceModel/AuthService.svc/Login

GET
Service at http://localhost/ServiceModel/AuthService.svc/Login?UserName={USERNAME}&UserPassword={USERPASSWORD}&SolutionName={SOLUTIONNAME}&TimeZoneOffset={TIMEZONEOFFSET}

Я так зрозумів, що для формування http-звернення до сервера потрібно визначити номер рішення (solution) яке має бути підставлене в рядок звернення:
http://localhost/номер рішення>/ServiceModel/ProcessEngineService.svc/назва створеного сервісу>Execute

Питання: як визначити номер рішення ?
(якщо можна - з прикладом).

Нравится

3 комментария

Ну от, потрібну інформацію видає сервіс
http://localhost/ServiceModel/AuthService.svc/GetSolutionsData:
[{"Key":"TSBpm","Value":0},{"Key":"TSBpm.1","Value":1},{"Key":"TSBpm.2","Value":2},{"Key":"TSBpm.3","Value":3}]

Добый день, Игорь!
Обычно в продуктивной среде используется только 1 рабочее решение (конфигурация), и его номер - 0. Другие конфигурации могут использоваться для разработки и их номера соответсвуют полю [Number] таблицы [SysSolution], а на IIS должно быть добавлено соответсвующее приложение для каждой конфигурации, как видно на скрине:

Ну, загалом справився.
Написав програмку, в яку вхідними параметрами передаю web-ресурс, назву служби, назву рішення та ще кілька параметрів для проформи.

Спочатку проводжу аутентифікацію за допомогою служби:
http://localhost/ServiceModel/AuthService.svc/Login

Потім читаю список рішень в JSON-форматі за допомогою тієї ж служби:
http://localhost/ServiceModel/AuthService.svc/GetSolutionsData

в цьому списку шукаю рішення за назвою, визначаю номер рішення.

Далі, маючи web-ресурс, назву служби з вхідних параметрів та номер рішення, формую звернення до цієї служби:
http://localhost/&lt;№ рішення>/ServiceModel/ProcessEngineService.svc//Execute

Писав для зовнішнього запуску служби завантаження email-ів, яка раніше запускалася windows-планувальником за допомогою утилітки ProcessExecutor54.exe.

Я в ImapClient вніс зміни на предмет фіксації типу вкладеного в email-файлу, і служба завантаження email-ів в інтерфейсі BPMonline працювала нормально, а при зовнішньому запуску типи файлів не фіксувались (наче ProcessExecutor54.exe десь знаходила старий варіант служби).

Тому вирішив написати свій стартер для web-служб BPMonline.
Написав.
Працює зараз в тестовому режимі.
Поки що все [ok].

Якщо комусь цікаво - можу поділитися .NET утиліткою BPMonlineServise.exe

Показать все комментарии