WebAPI 权限控制解决方案——Phenix.NET企业应用软件快速开发平台.使用指南.21.WebAPI服务(三)
21.1 数据服务
21.1.1基本操作功能
Phenixヾ的数据服务,提供了如下的基本操作:
功能 |
Type |
URI |
参数 |
完整获取实体集合对象 |
GET |
api/Data |
|
分页获取实体集合对象 |
GET |
api/Data |
pageSize=[分页大小]&pageNo=[分页号] |
按条件获取实体集合对象 |
GET |
api/Data |
id=[条件对象] |
按条件分页获取实体集合对象 |
GET |
api/Data |
id=[条件对象]&pageSize=[分页大小]&pageNo=[分页号] |
按主对象获取实体集合对象 |
GET |
api/Data |
masterId=[主对象键值]&groupName=[分组名] |
按主对象分页获取实体集合对象 |
GET |
api/Data |
masterId=[主对象键值]&groupName=[分组名]&pageSize=[分页大小]&pageNo=[分页号] |
获取单个实体对象 |
GET |
api/Data |
id=[主键值] |
提交数据 |
POST |
api/Data |
|
执行服务 |
POST |
api/Data |
注:可操作的实体范围,除受到普通的权限管理控制外,仍可以受到“切片管理”(参见:《Phenix.NET企业应用软件快速开发平台.权限管理.01.简介》)的控制。
从上述接口可见,Phenixヾ的WebAPI服务提供的是通用的接口(仅实现了一个API Controller),即DataController。
这样,为了区分不同的资源,要求HTTP客户端将资源信息藏在HTTP请求的标头Header里:
Header Name |
内容 |
适用于 |
说明 |
Phenix-Data-Name |
数据名/服务名 |
GET、POST |
在服务端注册的实体集合类全名、实体类全名、服务类全名(需实现IEntityCollection、IEntity、IService接口) |
Phenix-Criteria-Name |
条件名 |
GET |
在服务端定义的条件类全名(需实现ICriteria接口) |
Phenix-Master-Name |
主对象名 |
GET |
在服务端注册的实体类全名(需实现IEntity接口) |
注: 实体对象的操作请求,在Http请求标头上标识“Phenix-Data-Name”内容是必须的。否则,如果没有标识上,且是未带参数的Get请求的话,服务端返回的则是Sequence(64位序列号)。
不管我们采用是什么技术、什么平台,HTTP客户端的开发,只要符合上述HTTP消息的规范要求,就能得到Phenixヾ的WebAPI数据服务。
21.1.2数据服务代理
为了说明HTTP客户端如何调用WebAPI数据服务,Phenixヾ在“Phenix.Web.Client”工程里提供了一套标准的数据服务代理类DataProxy。我们可以依照这个实例里的代码,作为样例来编写出适用于自己技术平台的HTTP客户端:
21.1.2.1 Fetch
21.1.2.1.1 方法
检索数据,分检索单个实体和实体集合,都使用到了Get操作:
/// <summary>
/// 获取实体
/// </summary>
/// <param name="dataName">数据名, 在服务端注册的实体集合类全名(需实现IEntityCollection接口)</param>
/// <param name="id">主键值</param>
/// <returns>实体</returns>
public async Task<T> FetchAsync<T>(string dataName, long id)
where T : IEntity
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, String.Format("{0}?id={1}", DATA_URI, id));
request.Headers.Add(Phenix.Web.Client.Properties.Settings.Default.WebDataNameHeaderName, Uri.EscapeDataString(!String.IsNullOrEmpty(dataName) ? dataName : typeof(T).FullName));
HttpResponseMessage response = await _httpClient.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (response.StatusCode != HttpStatusCode.OK)
throw new HttpRequestException(result);
return EntityHelper.JsonUnpack<T>(result);
}
/// <summary>
/// 获取实体集合
/// </summary>
/// <param name="dataName">数据名, 在服务端注册的实体集合类全名(需实现IEntityCollection接口)</param>
/// <param name="criteriaName">条件名, 在服务端定义的条件类全名(需实现ICriteria接口)</param>
/// <param name="criteria">JSON格式条件对象, 在服务端将被反序列化为criteriaName指定条件类的对象</param>
/// <param name="pageSize">分页大小</param>
/// <param name="pageNo">分页号, 从1起始</param>
/// <returns>实体集合</returns>
public async Task<T> FetchListAsync<T>(string dataName, string criteriaName, object criteria, int? pageSize, int? pageNo)
where T : IEntityCollection
{
HttpRequestMessage request;
if (criteria != null)
{
if (String.IsNullOrEmpty(criteriaName))
{
Type criteriaType = criteria.GetType();
if (!criteriaType.IsClass)
throw new ArgumentNullException("criteriaName");
criteriaName = criteriaType.FullName;
}
request = new HttpRequestMessage(HttpMethod.Get, String.Format("{0}?id={1}{2}",
DATA_URI,
Uri.EscapeDataString(criteria is string ? (string)criteria : (criteria is ICriteria ? CriteriaHelper.JsonPack(criteria) : JsonConvert.SerializeObject(criteria))),
pageSize.HasValue && pageNo.HasValue ? String.Format("&pageSize={0}&pageNo={1}", pageSize, pageNo) : null));
request.Headers.Add(Phenix.Web.Client.Properties.Settings.Default.WebCriteriaNameHeaderName, Uri.EscapeDataString(criteriaName));
}
else
request = new HttpRequestMessage(HttpMethod.Get, String.Format("{0}{1}",
DATA_URI,
pageSize.HasValue && pageNo.HasValue ? String.Format("?pageSize={0}&pageNo={1}", pageSize, pageNo) : null));
request.Headers.Add(Phenix.Web.Client.Properties.Settings.Default.WebDataNameHeaderName, Uri.EscapeDataString(!String.IsNullOrEmpty(dataName) ? dataName : typeof(T).FullName));
HttpResponseMessage response = await _httpClient.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (response.StatusCode != HttpStatusCode.OK)
throw new HttpRequestException(result);
return EntityListHelper.JsonUnpack<T>(result);
}
检索到的数据,返回的是JSON格式的字符串,可以在客户端被转换成任何形式的数据。比如,象上述最后一行代码那样,由EntityListHelper提供的JsonUnpack()方法转换为实体集合对象。
21.1.2.1.2 调用
Fetch()函数的调用示例,代码摘录如下:
assemblyEasyList = client.DataProxy.FetchList<AssemblyEasyList>(
"Phenix.Test.使用指南._21._5.Business.AssemblyList",
"Phenix.Test.使用指南._21._5.Business.AssemblyCriteria2",
new LocalAssemblyCriteria()
{
Name = "Phenix.Test." //CriteriaOperate.Equal
});
上述黄色标示的代码段,如果被删除,WebAPI服务会查找业务类的ClassAttribute标签DefaultCriteriaType属性所申明的缺省条件类,来作为本次响应的查询类。如果未申明,则查找与业务类同名且带Criteria后缀的条件类,比如本示例找到的会是AssemblyCriteria类。这些申明的条件类,都应该实现ICriteria接口。
21.1.2.1.3 契约
在本示例中,被打包成JSON传递到服务端的查询对象,是本地客户端一个极为简单的对象:
class LocalAssemblyCriteria
{
public string Name { get; set; }
}
它只要和服务端所定义的查询类,在属性名称上能互相匹配就可以了:
[System.SerializableAttribute(), System.ComponentModel.DisplayNameAttribute("")]
public class AssemblyCriteria2 : Phenix.Business.CriteriaBase
{
[Phenix.Core.Mapping.CriteriaField(FriendlyName = "AS_NAME", Logical = Phenix.Core.Mapping.CriteriaLogical.And, Operate = Phenix.Core.Mapping.CriteriaOperate.Equal, TableName = "PH_ASSEMBLY", ColumnName = "AS_NAME")]
private string _name;
/// <summary>
/// AS_NAME
/// </summary>
[System.ComponentModel.DataAnnotations.Display(Name = "AS_NAME")]
[System.ComponentModel.DisplayName("AS_NAME")]
public string Name
{
get { return _name; }
set { _name = value; PropertyHasChanged(); }
}
}
甚至,我们可以直接传一个JSON字符串,也能达到同样的结果:
assemblyEasyList = client.DataProxy.FetchList<AssemblyEasyList>(
"Phenix.Test.使用指南._21._5.Business.AssemblyList",
"Phenix.Test.使用指南._21._5.Business.AssemblyCriteria2",
@"{""Name"":""Phenix.Test.""}"); //CriteriaOperate.Equal
最后,将执行过程中发生的Headers截录如下:
GET /api/Data?id=%7B%22Name%22%3A%22Phenix.Test.%22%7D HTTP/1.1
Phenix-Criteria-Name: Phenix.Test.%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97._21._5.Business.AssemblyCriteria2
Phenix-Data-Name: Phenix.Test.%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97._21._5.Business.AssemblyList
Phenix-Authorization: ADMIN,d48a36b5-31c9-41a6-ad35-efbcea458efc,2015-11-01 10:26:18,B7082E34BBFC95236B307E389C6796F7A29312A0D62F0BD683BF81541EB92044F8F4CE263C3AFC09116C14FD88564A4F41E5DFE1E0D755336F47465046E51CB0
Host: 10.0.0.11:8080
21.1.2.2 Save
21.1.2.2.1 方法
提交的数据,可以是单个实体,也可以是实体集合,且允许包含它们的树状结构。
提交的操作,不管是update还是insert、delete,使用的都是Post操作(需要对象能反映出当前的编辑状态):
/// <summary>
/// 提交实体
/// </summary>
/// <param name="dataName">数据名, 在服务端注册的实体类全名(需实现IEntity接口)</param>
/// <param name="entity">实体</param>
/// <returns>成功数量</returns>
public async Task<int> SaveAsync(string dataName, IEntity entity)
{
if (entity == null)
return 0;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, DATA_URI);
request.Headers.Add(Phenix.Web.Client.Properties.Settings.Default.WebDataNameHeaderName, Uri.EscapeDataString(!String.IsNullOrEmpty(dataName) ? dataName : entity.GetType().FullName));
request.Content = new StringContent(EntityHelper.JsonPackChanged(entity));
HttpResponseMessage response = await _httpClient.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (response.StatusCode != HttpStatusCode.OK)
throw new HttpRequestException(result);
return (int)Utilities.ChangeType(result, typeof(int));
}
/// <summary>
/// 提交实体集合
/// </summary>
/// <param name="dataName">数据名, 在服务端注册的实体集合类/实体类全名(需实现IEntityCollection/IEntity接口)</param>
/// <param name="entityCollection">实体集合</param>
/// <returns>成功数量</returns>
public async Task<int> SaveAsync(string dataName, IEntityCollection entityCollection)
{
if (entityCollection == null)
return 0;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, DATA_URI);
request.Headers.Add(Phenix.Web.Client.Properties.Settings.Default.WebDataNameHeaderName, Uri.EscapeDataString(!String.IsNullOrEmpty(dataName) ? dataName : entityCollection.GetType().FullName));
request.Content = new StringContent(EntityListHelper.JsonPackChanged(entityCollection));
HttpResponseMessage response = await _httpClient.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (response.StatusCode != HttpStatusCode.OK)
throw new HttpRequestException(result);
return (int)Utilities.ChangeType(result, typeof(int));
}
/// <summary>
/// 提交对象
/// </summary>
/// <param name="dataName">数据名, 在服务端注册的实体集合类/实体类全名(需实现IEntityCollection/IEntity接口)</param>
/// <param name="obj">JSON格式对象, 将被传到服务端, 需包含IsNew、IsSelfDeleted、IsSelfDirty属性以指明对象状态</param>
/// <returns>成功数量</returns>
public async Task<int> SaveAsync(string dataName, object obj)
{
if (obj == null)
return 0;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, DATA_URI);
request.Headers.Add(Phenix.Web.Client.Properties.Settings.Default.WebDataNameHeaderName, Uri.EscapeDataString(dataName));
request.Content = new StringContent(obj is string ? (string)obj : JsonConvert.SerializeObject(obj));
HttpResponseMessage response = await _httpClient.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (response.StatusCode != HttpStatusCode.OK)
throw new HttpRequestException(result);
return (int)Utilities.ChangeType(result, typeof(int));
}
函数返回的是成功提交的纪录数。如果未能成功提交,服务端返回的是异常提示信息,在客户端被封装在HttpRequestException异常对象里抛出。
21.1.2.2.2 调用
Save()函数的调用示例代码如下:
count = client.DataProxy.Save(
"Phenix.Test.使用指南._21._5.Business.Assembly",
new LocalAssembly()
{
IsSelfDirty = true,
AS_ID = assemblyId,
Caption = Sequence.Value.ToString()
});
21.1.2.2.3 契约
在本示例中,被打包成JSON传递到服务端的数据对象,是本地客户端一个极为简单的对象:
class LocalAssembly
{
/// <summary>
/// 新增状态
/// </summary>
public bool IsNew { get; set; }
/// <summary>
/// 删除状态
/// </summary>
public bool IsSelfDeleted { get; set; }
/// <summary>
/// 更新状态
/// </summary>
public bool IsSelfDirty { get; set; }
public long? AS_ID { get; set; }
public string Caption { get; set; }
}
上述黄色标示的代码段,是表示当前对象的编辑状态,这被用来告知服务端,应该如何提交本对象的数据。如果没有这些编辑状态属性,服务端将默认为这个对象处于IsSelfDirty = true状态。
数据对象,应该包含PrimaryKey属性(本示例为AS_ID属性),否则服务端是无法持久化这个对象的。而且,所有需要被持久化属性的名称,都应该和服务端的实体类定义互相匹配。
同查询对象一样,数据对象也可以直接传JSON字符串。
最后,将执行过程中发生的Headers截录如下:
POST /api/Data HTTP/1.1
Phenix-Data-Name: Phenix.Test.%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97._21._5.Business.Assembly
Phenix-Authorization: ADMIN,58733271-1be1-4232-a931-6110cc5f1d83,2015-11-01 11:11:04,E3774F2B6FF6406791108EA47BBE54A667BF9D249F6583F6AB5309081A68D7E54FC4E25BDB25661B522FAFC5BE75E74AD1B3F3D63F5C9B8039F7C93996A7B915
Content-Type: text/plain; charset=utf-8
Host: 10.0.0.11:8080
Content-Length: 108
Expect: 100-continue
21.1.2.3 Execute
21.1.2.3.1 方法
执行服务,实质上是CSLA的Command模式在WebAPI上的翻版,是把WebAPI当成执行服务引擎来使用,HTTP动词是Post:
/// <summary>
/// 执行服务
/// </summary>
/// <param name="serviceName">服务名, 在服务端注册的服务类全名(需实现IService接口)</param>
/// <param name="service">服务</param>
/// <returns>服务结果</returns>
public async Task<T> ExecuteAsync<T>(string serviceName, T service)
where T : IService
{
if (service == null)
throw new ArgumentNullException("service");
Type type = service.GetType();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, DATA_URI);
request.Headers.Add(Phenix.Web.Client.Properties.Settings.Default.WebDataNameHeaderName, Uri.EscapeDataString(!String.IsNullOrEmpty(serviceName) ? serviceName : type.FullName));
request.Content = new StringContent(JsonConvert.SerializeObject(service));
HttpResponseMessage response = await _httpClient.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (response.StatusCode != HttpStatusCode.OK)
throw new HttpRequestException(result);
return (T)JsonConvert.DeserializeObject(result, type);
}
/// <summary>
/// 执行服务
/// </summary>
/// <param name="serviceName">服务名, 在服务端注册的服务类全名(需实现IService接口)</param>
/// <param name="obj">JSON格式对象, 将被传到服务端</param>
/// <returns>服务结果</returns>
public async Task<object> ExecuteAsync(string serviceName, object obj)
{
if (obj == null)
throw new ArgumentNullException("obj");
Type type = obj.GetType();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, DATA_URI);
request.Headers.Add(Phenix.Web.Client.Properties.Settings.Default.WebDataNameHeaderName, Uri.EscapeDataString(serviceName));
request.Content = new StringContent(obj is string ? (string)obj : JsonConvert.SerializeObject(obj));
HttpResponseMessage response = await _httpClient.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (response.StatusCode != HttpStatusCode.OK)
throw new HttpRequestException(result);
return obj is string ? result : JsonConvert.DeserializeObject(result, type);
}
这为我们开发WebAPI服务提供了强大的灵活性,可以纯粹以面向对象的形式编写服务端的业务逻辑代码:
[JsonObject(MemberSerialization.Fields)]
[System.SerializableAttribute()]
public class Service : Phenix.Core.Data.ServiceBase<Service> //Phenix.Business.CommandBase<Service>
{
public Service()
{
}
private string _assembly;
public Assembly Assembly
{
get { return Phenix.Core.Mapping.EntityHelper.JsonUnpack<Assembly>(_assembly); }
set { _assembly = Phenix.Core.Mapping.EntityHelper.JsonPack(value); }
}
/// <summary>
/// 处理执行指令(在运行持久层的程序域里被调用)
/// </summary>
protected override void DoExecute()
{
Assembly assembly = Assembly;
assembly.Caption = assembly.Caption + "被服务端改写";
Assembly = assembly;
EventLog.Save(Phenix.Core.Security.UserPrincipal.User, MethodBase.GetCurrentMethod(),
assembly.GetOldValue(Assembly.CaptionProperty) + "->" + assembly.Caption);
}
}
仅需继承自Phenix.Core.Data.ServiceBase,或者实现Phenix.Core.Mapping.IService接口:
21.1.2.3.2 调用
Execute()函数的调用示例代码如下:
Service service = client.DataProxy.Execute(
new Service()
{
Assembly = assembly
});
21.1.2.3.3 契约
在本示例中,客户端和服务端共享了同一个Service类,这在实际开发场景下,完全可以不必这么实现。同前文的做法一样,客户端可以使用一个简单的对象,将需要传递的属性命名与Service类的定义达成一致(本示例是Assembly属性)即可,或者直接传递JSON格式的字符串,都是可以得到相同的结果。
最后,将执行过程中发生的Headers截录如下:
POST /api/Data HTTP/1.1
Phenix-Data-Name: Phenix.Test.%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97._21._5.Business.Service
Phenix-Authorization: ADMIN,4534dfeb-35e0-44ab-963f-96c71b778cbc,2015-11-01 12:15:27,63A955F8056353BFDBDDE2FC1A8CF1D879DEA3C244EB64D1702810836F71FBCDEEAE66D0E5063542C765F2720649E076C07358A8A20A0D5B65EBC0CAFB3F72DE
Content-Type: text/plain; charset=utf-8
Host: 10.0.0.11:8080
Content-Length: 4489
Expect: 100-continue
21.1.3注册服务端事件
Phenixヾ的WebAPI服务不同于CSLA在remoting/WCF架构下的移动对象模式,不可能将数据封装成实体对象,而是纯粹的数据持久层服务(相对来说,在牺牲了业务框架特性的前提下,WebAPI服务在小批量数据下的Fetch和Save性能,理论上要优于remoting服务)。所以,业务类(继承自BusinessBase)上的服务端事件函数(比如OnSavingSelf系列函数等)都不可能被WebAPI服务调用到,业务规则(继承自ObjectRule)也不可能被WebAPI服务调用到。
为补偿因缺失上述这些业务框架特性所带来的服务端业务逻辑干预能力的弱化,Phenixヾ提供了一系列的服务端事件及其注册机制:
服务端事件,分为Fetch、Save处理过程的前事件和后事件,被定义在了IData接口里。
前事件可以拦截到客户端传递过来的数据(比如查询条件)并干预WebAPI数据服务的处理(或直接自行处理),而后事件可拦截到WebAPI数据服务的处理结果。
要注册上服务端事件,需用到AppHub的Data属性,Data属性实现了IData接口中一系列的服务端事件注册函数。
AppHub所在程序集是Phenix.Services.Library,我们开发的Business如需拦截服务端事件,就应该引用它:
服务端事件的注册和注销,都是在服务端被调用的,服务端事件的执行也应该运行在服务端。为此,我们要用到Phenixヾ提供的插件Host。具体方法是,在Business程序集里新增一个Plugin类,编译后添加到插件Host中:
Plugin类代码如下:
public class Plugin : PluginBase<Plugin>
{
public Plugin()
: base() { }
#region 方法
#region 覆写 PluginBase
/// <summary>
/// 启动
/// 由 PluginHost 调用
/// </summary>
/// <returns>确定启动</returns>
protected override bool Start()
{
AppHub.Data.FetchingRegister(typeof(Assembly), FetchingEventHandler);
AppHub.Data.FetchedRegister(typeof(Assembly), FetchedEventHandler);
AppHub.Data.FetchingRegister(typeof(AssemblyList), FetchingEventHandler);
AppHub.Data.FetchedRegister(typeof(AssemblyList), FetchedEventHandler);
AppHub.Data.SavingRegister(typeof(Assembly), SavingEventHandler);
AppHub.Data.SavedRegister(typeof(Assembly), SavedEventHandler);
AppHub.Data.SavingRegister(typeof(AssemblyList), SavingEventHandler);
AppHub.Data.SavedRegister(typeof(AssemblyList), SavedEventHandler);
return true;
}
/// <summary>
/// 暂停
/// 由 PluginHost 调用
/// </summary>
/// <returns>确定停止</returns>
protected override bool Suspend()
{
AppHub.Data.FetchingUnregister(typeof(Assembly), FetchingEventHandler);
AppHub.Data.FetchedUnregister(typeof(Assembly), FetchedEventHandler);
AppHub.Data.FetchingUnregister(typeof(AssemblyList), FetchingEventHandler);
AppHub.Data.FetchedUnregister(typeof(AssemblyList), FetchedEventHandler);
AppHub.Data.SavingUnregister(typeof(Assembly), SavingEventHandler);
AppHub.Data.SavedUnregister(typeof(Assembly), SavedEventHandler);
AppHub.Data.SavingUnregister(typeof(AssemblyList), SavingEventHandler);
AppHub.Data.SavedUnregister(typeof(AssemblyList), SavedEventHandler);
return true;
}
#endregion
/// <summary>
/// 检索前事件处理函数
/// </summary>
public void FetchingEventHandler(FetchEventArgs e)
{
EventLog.Save(Phenix.Core.Security.UserPrincipal.User, MethodBase.GetCurrentMethod(), CriteriaHelper.JsonPack(e.Criterions.Criteria));
}
/// <summary>
/// 检索后事件处理函数
/// </summary>
public void FetchedEventHandler(FetchEventArgs e)
{
EventLog.Save(Phenix.Core.Security.UserPrincipal.User, MethodBase.GetCurrentMethod(), e.Result);
}
/// <summary>
/// 提交前事件处理函数
/// </summary>
public void SavingEventHandler(SaveEventArgs e)
{
EventLog.Save(Phenix.Core.Security.UserPrincipal.User, MethodBase.GetCurrentMethod(), e.SourceJson);
}
/// <summary>
/// 提交后事件处理函数
/// </summary>
public void SavedEventHandler(SaveEventArgs e)
{
EventLog.Save(Phenix.Core.Security.UserPrincipal.User, MethodBase.GetCurrentMethod(), e.Result.ToString());
}
#endregion
}
插件添加到Host里会自动启动,Plugin对象的Start()函数会被调用到,也就执行了服务端事件的注册方法。插件启动后,可随时被暂停(Plugin对象的Suspend()函数会被调用到)。
服务端事件所传递的参数:
因这些参数都继承了ShallEventArgs,我们可以在XXXing事件里通过将e.Applied设置为true来告知WebAPI数据服务,本事件已自行处理了Fetch或Save操作。这时,事件代码里应该通过给e.Result赋值,将自行处理得到的返回结果传给WebAPI数据服务,WebAPI数据服务会将e.Result内容打包返回给HTTP客户端。
21.1.4案例与建议
测试工程“Phenix.Test.使用指南.21.5”已基本上覆盖了所有可能的数据操作方法,请编译后运行观察效果。
需注意的是,本测试工程在客户端也用到了业务类、实体类,与之相关的配置信息需要被下载到本地。而WebAPI仅提供的是数据操作服务,所以需要额外通过调用Phenixヾ的remoting/WCF服务来下载他们的配置信息:
Phenix.Services.Client.Library.Registration.RegisterWorker(NetConfig.LocalAddress);
在实际开发场景中,客户端如果能够用上remoting/WCF服务的话,就没必要使用WebAPI技术。而一旦使用了WebAPI技术,客户端就没必要用上业务类、实体类。所以,也就不需要上述代码来获取配置信息了。
WebAPI 权限控制解决方案——Phenix.NET企业应用软件快速开发平台.使用指南.21.WebAPI服务(三)的更多相关文章
- WebAPI 身份认证解决方案——Phenix.NET企业应用软件快速开发平台.使用指南.21.WebAPI服务(一)
21 WebAPI服务 ASP.NET Web API,是微软在.NET Framework 4.5上推出的轻量级网络服务框架,虽然作为ASP.NET MVC 4的一部分,但却是一套全新的.独立的 ...
- SNF快速开发平台MVC-EasyUI3.9之-WebApi身份验证问题解决方案
在我们的整体bs框架当中前端采用的是MVC+WebApi的处理方式.WebApi使用起来确实很方便但也会有新的麻烦事,就是身份验证. 如果没有启用身份认证,那么任何匿名用户只要知道了我们服务的url, ...
- SNF快速开发平台MVC-EasyUI3.9之-WebApi和MVC-controller层接收的json字符串的取值方法和调用后台服务方法
最近项目组很多人问我,从前台页面传到后台controller控制层或者WebApi 时如何取值和运算操作. 今天就都大家一个在框架内一个取值技巧 前台JS调用代码: 1.下面是选中一行数据后右键点击时 ...
- SNF快速开发平台MVC-EasyUI3.9之-WebApi跨域处理方案
在做Web开发中,常常会遇到跨域的问题,到目前为止,已经有非常多的跨域解决方案.由于时间有限,本文不会深入. 笔者遇到的问题是Js调用WebAPI中的数据进行跨域的场景.涉及若干跨域方案:目前采用we ...
- JeeSite 企业信息化快速开发平台
平台简介 JeeSite是基于多个优秀的开源项目,高度整合封装而成的高效,高性能,强安全性的开源Java EE快速开发平台. JeeSite本身是以Spring Framework为核心容器,Spri ...
- Basic4android:多功能的Android应用软件快速开发平台
Basic4android 是目前最简单.最强大的Android平台快速应用开发工具. ( "Basic4android is the simplest and most powerful ...
- 企业信息化快速开发平台--JeeSite
JeeSite是在Spring Framework基础上搭建的一个Java基础开发平台,以Spring MVC为模型视图控制器,MyBatis为数据访问层, Apache Shiro为权限授权层,Eh ...
- webapi 权限控制解决方案
随着移动互联网的发展,webapi的应用越来越广泛,本文是笔者总结的webapi的认证校验案例,欢迎指出 案例分为两个功能: 1.用户登录,传入账号和密码到api服务器,然后服务器使用FormsAut ...
- 企业信息化快速开发平台JeeSite
网站:http://jeesite.com/ 可用于企业后台管理
随机推荐
- rocketmq 使用中碰到的一些问题
1.rocket 入门使用 http://rocketmq.apache.org/docs/quick-start/ 2.内存不够 默认配置内存配置比较高,在测试环境启动会有问题因此需要调整默认的内存 ...
- 创建readonly只读用户脚本
身为一名运维工作人员,保证服务器的安全是必要项,当开发人员或测试人员需登录到服务器查看日志等操作时,可只给定特定的权限防止误操作的惨况产生. 以下脚本内容均为我本人环境,如有更改可自行修改. ~]# ...
- tf.gather_nd()
tf.gather_nd( params, indices, name=None, batch_dims=0) TensorFlow链接:https://tensorflow.google.cn/ap ...
- 小样本学习(few-shot learning)在文本分类中的应用
1,概述 目前有效的文本分类方法都是建立在具有大量的标签数据下的有监督学习,例如常见的textcnn,textrnn等,但是在很多场景下的文本分类是无法提供这么多训练数据的,比如对话场景下的意图识别, ...
- <人人都懂设计模式>-装饰模式
书上,真的用一个人穿衣打拌来讲解装饰模式的呢. from abc import ABCMeta, abstractmethod class Person(metaclass=ABCMeta): def ...
- K8S当中的本地卷(Local PV)的使用
Local PV是从kuberntes 1.10开始引入,本质目的是为了解决hostPath的缺陷.通过PV控制器与Scheduler的结合,会对local PV做针对性的逻辑处理,从而,让Pod在多 ...
- Python3 if 变量variable SQL where 语句拼接
最近在写python3的项目,在实际中运用到了根据 if 判断变量variable ,然后去拼接where子句.但是在百度.BING搜索中未找到合适的答案,这是自己想出来的典型php写法,这里做一下记 ...
- Game Publisher
“Amazon Appstore https://developer.amazon.com/why-amazonApple Store https://developer.apple.com/prog ...
- [教程]Ubuntu16.04安装QQ,Tim,微信,百度网盘等
[教程]Ubuntu16.04安装QQ,Tim,微信,百度网盘等 本文参考这篇blog step 1 先安装 deep-win环境. 戳这里下载压缩包 解压后在文件夹里打开终端,输入 sudo sh ...
- 【Gamma】Scrum Meeting 7
前言 会议定点:大运村公寓 会议时间:2019/6/5 会议目的:明确下阶段目标 一.任务进度 组员 上周任务进度 下阶段任务 大娃 修复后端bug 辅助做好引导录屏 二娃 撰写会议博客 撰写会议博客 ...