Вопрос
Скажите, как правильно вызвать веб-сервис созданный в одной bpm из сторонней bpm?
Получаю ошибку Cross-origin resource sharing.
Ответ
Все что будет сказано далее, тестировалось на своих тестовых площадках. Единственное что колонку в лиде, по которой будет производиться поиск, назвали с префиксом: UsrTest. И сами классы утилс, и сервиса, так же назвали с префиксами: UsrMyTestService, UsrLeadSearchUtils.
В итоге, на старте, у нас имеется:
Код схемы сервиса
namespace Terrasoft.Configuration.CustomConfigurationService { using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Linq; using System.ServiceModel; using System.ServiceModel.Web; using System.ServiceModel.Activation; using System.Web; using System.IO; using Terrasoft.Core; using Terrasoft.Core.Configuration; using Terrasoft.Core.DB; using Terrasoft.Core.Entities; [ServiceContract] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] public class UsrMyTestService { private UserConnection _userConnection; private UserConnection UserConnection { get { return _userConnection ?? (_userConnection = HttpContext.Current.Session["UserConnection"] as UserConnection); } } [OperationContract] [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)] public testclass myTestServiceFunction(string test) { ////// var currentWebOperationContext = WebOperationContext.Current; var outgoingResponseHeaders = currentWebOperationContext.OutgoingResponse.Headers; var incomingRequestOrigin = currentWebOperationContext.IncomingRequest.Headers["Origin"]; outgoingResponseHeaders.Add("Access-Control-Allow-Origin", incomingRequestOrigin); /////// testclass result = new testclass(); UsrLeadSearchUtils leadSearchUtil = new UsrLeadSearchUtils(this.UserConnection); try { var leadCollection = leadSearchUtil.SearchLeadByCustomerID(test); foreach (var item in leadCollection) { result.Response.Add(new SearchItem() { Id = item.GetTypedColumnValue<Guid>("LeadId"), Name = item.GetTypedColumnValue<string>("LeadName"), }); } } catch (Exception ex) { result.Success = false; result.ErrorMessage = ex.Message; }; return result; } //////// [OperationContract] [WebInvoke(Method = "OPTIONS", UriTemplate = "SaveWebFormLeadData")] public void GetWebFormLeadDataRequestOptions() { var outgoingResponseHeaders = WebOperationContext.Current.OutgoingResponse.Headers; outgoingResponseHeaders.Add("Access-Control-Allow-Origin", "*"); outgoingResponseHeaders.Add("Access-Control-Allow-Methods", "POST"); outgoingResponseHeaders.Add("Access-Control-Allow-Headers", "Origin, Content-Type, Accept"); } ////////// [DataContract] public class testclass { public testclass() { this.Success = true; this.Response = new List<SearchItem>(); } [DataMember] public List<SearchItem> Response { get; set; } [DataMember] public bool Success { get; set; } [DataMember] public string ErrorMessage { get; set; } } [DataContract] public class SearchItem { [DataMember] public Guid Id { get; set; } [DataMember] public string Name { get; set; } } } }
Код схемы утилит
namespace Terrasoft.Configuration { using System; using System.Collections.Generic; using System.IO; using Terrasoft.Core; using Terrasoft.Core.Configuration; using Terrasoft.Core.DB; using Terrasoft.Core.Entities; public class UsrLeadSearchUtils { public UsrLeadSearchUtils (UserConnection userConnection) { this.UserConnection = userConnection; } private UserConnection UserConnection { get; set; } public EntityCollection SearchLeadByCustomerID(string email) { if (string.IsNullOrEmpty(email)) { throw new ArgumentException("email is undefined."); } var entitySchemaManager = this.UserConnection.GetSchemaManager("EntitySchemaManager") as EntitySchemaManager; var esq = new EntitySchemaQuery(entitySchemaManager, "Lead"); esq.RowCount = 20; esq.AddColumn("Id").Name = "LeadId"; esq.AddColumn("LeadName").Name = "LeadName"; esq.AddColumn("UsrTest").Name = "UsrTest"; esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Equal, "UsrTest", email)); return esq.GetEntityCollection(this.UserConnection); } } }
Сервис пробуем вызвать из той же bpm’online где сервис и создан, следующим образом:
onGetServiceInfoClick: function() { var usrTest = "test1"; var serviceData = { test: usrTest }; ServiceHelper.callService("UsrMyTestService", "myTestServiceFunction", function(response) { var result = response.myTestServiceFunctionResult; alert(result.Response[0].Name); }, serviceData, this ); }
Результат поиска мы получаем. И алертом выводим имя первого найденного лида по поисковой строке.
Теперь давайте попробуем сделать запрос к этому сервису из другой bpm’online.
Для этого наш сервис должен быть зарегистрирован как сервис доступный без авторизации, и для этого как раз необходимы как изменения кода самого сервиса, так и правки в конфигах сайта, на котором размешен сервис.
Правки в конфигах детальнее.
1) Вносим правки в наш «UsrMyTestService», помещаем его в немспейс «Terrasoft.Configuration», дописываем «UriTemplate» к методу, и опциям, наследуемся от «BaseService» а так же используем «SystemUserConnection» вместо обычного, так как авторизации не будет.
Результат:
namespace Terrasoft.Configuration { using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Linq; using System.ServiceModel; using System.ServiceModel.Web; using System.ServiceModel.Activation; using System.Web; using System.IO; using Terrasoft.Core; using Terrasoft.Core.Configuration; using Terrasoft.Core.DB; using Terrasoft.Core.Entities; using Terrasoft.Web.Common; [ServiceContract] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] public class UsrMyTestService : BaseService { private SystemUserConnection _systemUserConnection; private SystemUserConnection SystemUserConnection { get { return _systemUserConnection ?? (_systemUserConnection = (SystemUserConnection)AppConnection.SystemUserConnection); } } [OperationContract] [WebInvoke(Method = "GET", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json, UriTemplate = "myTestServiceFunction/{test}/")] public testclass myTestServiceFunction(string test) { /////// var currentWebOperationContext = WebOperationContext.Current; var outgoingResponseHeaders = currentWebOperationContext.OutgoingResponse.Headers; var incomingRequestOrigin = currentWebOperationContext.IncomingRequest.Headers["Origin"]; outgoingResponseHeaders.Add("Access-Control-Allow-Origin", incomingRequestOrigin); /////// testclass result = new testclass(); try { UsrLeadSearchUtils leadSearchUtil = new UsrLeadSearchUtils(this.SystemUserConnection); var leadCollection = leadSearchUtil.SearchLeadByCustomerID(test); foreach (var item in leadCollection) { result.Response.Add(new SearchItem() { Id = item.GetTypedColumnValue<Guid>("LeadId"), Name = item.GetTypedColumnValue<string>("LeadName"), }); } } catch (Exception ex) { result.Success = false; result.ErrorMessage = ex.Message; }; return result; } [OperationContract] [WebInvoke(Method = "OPTIONS", UriTemplate = "*")] public void GetWebFormLeadDataRequestOptions() { var outgoingResponseHeaders = WebOperationContext.Current.OutgoingResponse.Headers; outgoingResponseHeaders.Add("Access-Control-Allow-Origin", "*"); outgoingResponseHeaders.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); outgoingResponseHeaders.Add("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, X-Requested-With, X-Requested-With, x-request-source"); outgoingResponseHeaders.Add("Access-Control-Request-Headers", "X-Requested-With, x-request-source, accept, content-type"); } [DataContract] public class testclass { public testclass() { this.Success = true; this.Response = new List<SearchItem>(); } [DataMember] public List<SearchItem> Response { get; set; } [DataMember] public bool Success { get; set; } [DataMember] public string ErrorMessage { get; set; } } [DataContract] public class SearchItem { [DataMember] public Guid Id { get; set; } [DataMember] public string Name { get; set; } } } }
Класс утилит остается таким, как и был.
2) В папке сайта в котором написан наш сервис, *сайт*\Terrasoft.WebApp\ServiceModel
Создаем файл «UsrMyTestService.svc»
В содержимое файла пишем:
<%@ ServiceHost Language="C#" Debug="true" Service="Terrasoft.Configuration.UsrMyTestService" CodeBehind="UsrMyTestService.svc.cs" %>
3) В папке *сайт* \Terrasoft.WebApp в файле Web.config рядом с другими локациями, добавляем:
4) В папке *сайт*\Terrasoft.WebApp\ в файле Web.config в секции appSettings меняем значение ключа AllowedLocations. В значение добавляем:
ServiceModel/UsrMyTestService.svc;
5) В папке *сайт*\Terrasoft.WebApp\ServiceModel\http в файле services.config в блоке сервисов добавляем:
address=""
binding="webHttpBinding"
behaviorConfiguration="RestServiceBehavior"
bindingNamespace="http://Terrasoft.WebApp.ServiceModel"
contract="Terrasoft.Configuration.UsrMyTestService" />
6) В папке *сайт*\Terrasoft.WebApp\ServiceModel\https в файле services.config в блоке сервисов делаем то же самое, что и в пункте 5.
Сервис будет доступен по адресу:
http(или https)://адрес-сайта/0/ServiceModel/UsrMyTestService.svc
В рамках теста, все выше сказанное было сделано на нашем локальном сайте:
http://localhost:8012/
Теперь для того что бы обратится к этому сервису с другого сайта, который на наших тестовых площадках находится по адресу:
http://localhost:8006/
Доработаем к примеру схему контакта следующим образом:
define("ContactPageV2", ["ContactPageV2Resources", "GeneralDetails", "JQuery"], function(resources, GeneralDetails) { return { entitySchemaName: "Contact", details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/, diff: /**SCHEMA_DIFF*/[ ]/**SCHEMA_DIFF*/, attributes: {}, methods: { onEntityInitialized: function() { this.callParent(arguments); document.scp = this; }, test: function() { var params = { test: "test1" }; require(["jQuery"], function() { $.ajax({ url: "http://localhost:8012/0/ServiceModel/UsrMyTestService.svc/myTestServiceFunction/" + params.test + "/", success: function(data) { alert(JSON.stringify(data)); }, error: function() { alert("Error occured!"); } }); }); } }, rules: {}, userCode: {} }; });
В результате у нас есть метод вызова стороннего веб сервиса на стороннем домене (а разные порты, считаются разными доменами) и закеширован скоуп прямо в документе (это делается только для теста, в реальной работе Вам такое не понадобится) можно теперь попробовать вызвать этот метод прямо из консоли браузера, и увидеть результат: