Публикация

Веб-сервис, доступный без авторизации + CORS

Вопрос

Скажите, как правильно вызвать веб-сервис созданный в одной 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  рядом с другими локациями, добавляем:
<location path="ServiceModel/UsrMyTestService.svc">
   <system.web>
      <authorization>
         <allow users="*" />
      </authorization>
   </system.web>
</location>

4) В папке *сайт*\Terrasoft.WebApp\ в файле Web.config в секции appSettings меняем  значение ключа AllowedLocations. В значение добавляем:
ServiceModel/UsrMyTestService.svc;

5) В папке *сайт*\Terrasoft.WebApp\ServiceModel\http в файле services.config в блоке сервисов добавляем:
<service name="Terrasoft.Configuration.UsrMyTestService">
   <endpoint name="UsrMyTestServiceEndPoint"
      address=""
      binding="webHttpBinding"
      behaviorConfiguration="RestServiceBehavior"
      bindingNamespace="http://Terrasoft.WebApp.ServiceModel"
      contract="Terrasoft.Configuration.UsrMyTestService" />
</service>

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: {}
	};
});

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

 

Нравится

Поделиться

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