Вопрос
Скажите, как правильно вызвать веб-сервис созданный в одной 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: {}
};
});
В результате у нас есть метод вызова стороннего веб сервиса на стороннем домене (а разные порты, считаются разными доменами) и закеширован скоуп прямо в документе (это делается только для теста, в реальной работе Вам такое не понадобится) можно теперь попробовать вызвать этот метод прямо из консоли браузера, и увидеть результат: