如果要在客户端指定服务器端Web Service方法所接收的参数类型,就必须在客户端通过“__type”来指定,但是这就暴露了服务器端的具体类型了,这可不太好。现在我们就来看一下应该如何解决这个问题。在这里,我会使用以前的文章《深入Atlas系列:Web Sevices Access in Atlas示例(3) - 在Web Services方法中使用多态》里用过的例子,不过它的内容是使用CTP版本的Atlas,已经过期,因此还是需要一些改变。这个示例会分成好几步进行,我们一点点地来看它的实现:

1、定义需要的类型

首先,我们定义一下所需的类型。我们的目标是计算某种类型员工的工资,于是,我们先定义一个员工的抽象类:

namespace Jeffz.HiddenTypes
{
    public abstract class Employee
    {
        private int _Years;

public int Years
        {
            get
            {
                return this._Years;
            }
            set
            {
                this._Years = value;
            }
        }

public string RealStatus
        {
            get
            {
                return this.GetType().Name;
            }
        }

public abstract int CalculateSalary();
    }
}

然后定义一下可怜的实习生,不管干多少年,永远只有2000元工资:

namespace Jeffz.HiddenTypes
{
    public class Intern : Employee
    {
        public override int CalculateSalary()
        {
            return 2000;
        }
    }
}

然后是签第三方公司的合同工,底薪5000,每年增加1000:

namespace Jeffz.HiddenTypes
{
    public class Vendor : Employee
    {
        public override int CalculateSalary()
        {
            return 5000 + 1000 * (Years - 1);
        }
    }
}

最后是正式员工(全职工),底薪12000,每年增加2000:

namespace Jeffz.HiddenTypes
{
    public class FulltimeEmployee : Employee
    {
        public override int CalculateSalary()
        {
            return 12000 + 2000 * (Years - 1);
        }
    }
}

2、制作一个不隐藏服务器端类型的应用

首先,自然是定义一个Web Service,我们将其命名为ExposedRealTypesService.asmx

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class ExposedRealTypesService : System.Web.Services.WebService
{
    [GenerateScriptType(typeof(Intern))]
    [GenerateScriptType(typeof(Vendor))]
    [GenerateScriptType(typeof(FulltimeEmployee))]
    [WebMethod]
    public string CalculateSalary(Employee employee)
    {
        return "I'm " + employee.RealStatus + ", my salary is " + employee.CalculateSalary() + ".";
    }
    
}

在这里我们使用了GenerateScriptTypeAttribute来告诉这个Web Service:“我们可能会使用这些类来作为参数传递给你,请注意JSON字符串里的__type标志”,于是我们就能使用了。我们来看一下我们需要的HTML:

<asp:ScriptManager ID="ScriptManager" runat="server">
    <Services>
        <asp:ServiceReference Path="ExposedRealTypesService.asmx" InlineScript="false" />
    </Services>
</asp:ScriptManager>
    
<div>Years:<input type="text" id="txtYears" /></div>
<div>
    Status:
    <select id="comboStatus" style="width:150px;">
        <option value="Jeffz.HiddenTypes.Intern">Intern</option>
        <option value="Jeffz.HiddenTypes.Vendor">Vendor</option>
        <option value="Jeffz.HiddenTypes.FulltimeEmployee">FTE</option>
    </select>
</div>
<input type="button" onclick="calculateSalary()" value="Calculate!" />
<h1>Result:</h1>
<div id="result"></div>

所需的JavaScript代码如下:

function calculateSalary()
{
    var emp = eval("new " + $get("comboStatus").value + "()");
    emp.Years = parseInt($get("txtYears").value, 10);
    
    ExposedRealTypesService.CalculateSalary(emp, onComplete);
}
        
function onComplete(result)
{
    $get("result").innerHTML = result;
}

由于comboStatue的value就是客户端的类名,因此我使用了拼接字符串并且eval的方法生成客户端的类,并作为参数传递过去。其余的代码应该非常简单,我们就来看一下使用效果吧:

打开页面,先选择Intern,输入工龄为2,点击“Calculate!”按钮:

再选择FTE,输入工龄为5,点击“Calculate!”按钮:

嘿,这说明我们客户端传了不同的服务器对象给Web Service方法了。

3、隐藏客户端的__type信息

我们打开Fiddler看看请求的内容吧:

嗯?看到了JSON字符串里的信息了不?明明白白地写着“Jeffz.HiddenTypes.Intern”!哎,怎么能把我们服务器端的类型信息给暴露出去呢?这样是不是太危险了一点?不过没有关系,我们可以这么做。首先,修改一下Web Service方法,我们这里就另存为HiddenRealTypesService.asmx吧:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class HiddenRealTypesService : System.Web.Services.WebService
{
    [GenerateScriptType(typeof(Intern), ScriptTypeId="Intern")]
    [GenerateScriptType(typeof(Vendor), ScriptTypeId = "Vendor")]
    [GenerateScriptType(typeof(FulltimeEmployee), ScriptTypeId = "FTE")]
    [WebMethod]
    public string CalculateSalary(Employee employee)
    {
        return "I'm " + employee.RealStatus + ", my salary is " + employee.CalculateSalary() + ".";
    }    
}

注意到了在使用GenerateScriptTypeAttribute时我们改变了什么吗?对了,就是我们设置了“ScriptTypeId”的值。这是什么?我们来看一下GenerateScriptTypeAttribute的使用方式吧。如下:

internal class WebServiceData : JavaScriptTypeResolver
{
    ……

private void ProcessIncludeAttributes(GenerateScriptTypeAttribute[] attributes)
    {
          foreach (GenerateScriptTypeAttribute attribute1 in attributes)
          {
                if (!string.IsNullOrEmpty(attribute1.ScriptTypeId))
                {
                      this._typeResolverSpecials[attribute1.Type.FullName] = attribute1.ScriptTypeId;
                }

……

this.ProcessClientType(type1);
          }
    }

……
}

这段代码就是ASP.NET AJAX用于处理Web Service的WebServiceData类,当然它也提供了许多功能。在ProcessIncludeAttributes方法中会处理所有的GenrateScriptTypeAttribute,可以看到在这里为每个Attribute的ScriptTypeId与Type的FullName进行了映射。这种映射是不是让你想到了JavaScriptTypeResovler?没错,WebServiceData类就是继承了JavaScriptTypeResolver,它辅助了ASP.NET AJAX客户端访问Web Service方法时的序列化与反序列化工作。关于这一点,在我之前的文章《深入Atlas系列:探究序列化与反序列化能力(上) - 客户端支持,JavaScriptTypeResolver与JavaScriptConverter》里有比较详细的描述。

我们来看一下效果,当然在这之前还需要修改一下页面中ScriptManager的Service引用,如下:

<asp:ScriptManager ID="ScriptManager" runat="server">
    <Services>
        <asp:ServiceReference Path="HiddenRealTypesService.asmx" />
    </Services>
</asp:ScriptManager>

再用Fiddler看一下传输内容吧,如下:

嘿,这样就不会把服务器端的具体类型暴露给别人了,不是吗?嗯,我们再多想想……:)

4、隐藏客户端代码调用时使用的具体信息

我们还有相当的路要走。有没有发现,我们在调用中带有了非常“明显”的类型信息。我们的方式其实和下面的差不多:

var emp = new Jeffz.HiddenTypes.Intern();
……
HiddenRealTypesService.CalculateSalary(emp, onComplete);

“Jeffz.HiddenTypes.Intern”?这不还是服务器端的具体类型吗?那么该怎么办呢?其实“new Jeffz.HiddenTypes.Intern()”操作也只是返回了一个普通的Object对象,不过它带有“__type”这个信息,其值为服务器端具体类型的ID。因此我们其实只要像下面这么做,也能得到同样的效果了。

首先,将<select />元素改成如下的形式:

<select id="comboStatus" style="width:150px;">
    <option value="Intern">Intern</option>
    <option value="Vendor">Vendor</option>
    <option value="FTE">FTE</option>
</select>

这样就不会暴露出服务器端的具体类型了。然后我们相应地修改JavaScript代码,如下:

function calculateSalary()
{
    var emp = { '__type' : $get("comboStatus").value };
    emp.Years = parseInt($get("txtYears").value, 10);
            
    HiddenRealTypesService.CalculateSalary(emp, onComplete);
}

再使用一下代码,一切正常,我们需要知道的只是服务器端具体类型的ID。这下完美了吧!再等等,再想想……

5、取消使用Service代理

我们还使用了ASP.NET AJAX为Web Service生成的代理,我们服务器端具体类型的信息都在那里,这不行,赶快取消!当然,在取消的同时不能破坏了Web Service方法的正常使用。我们该怎么做呢?

事实上,Web Service代理是通过访问XXXXX.asmx/js(Release模式)或者XXXXX.asmx/jsdebug输出的。我们必须禁止它。在这里我选择的方式是在Global.asax提供Application_BeginRequest的定义,它会在请求的一开始被调用。代码如下:

void Application_BeginRequest(object sender, EventArgs e)
{
    string pathInfo = this.Context.Request.PathInfo;
    
    if ("/js".Equals(pathInfo, StringComparison.OrdinalIgnoreCase) ||
        "/jsdebug".Equals(pathInfo, StringComparison.OrdinalIgnoreCase))
    {
        throw new InvalidOperationException("You can't get the proxy script!");
    }
}

我们判断了请求地址的PathInfo,如果为“/js”或者“/jsdebug”时,则抛出异常,这样就禁止获得了任何的Web Service的Proxy信息,当然您也可以有选择地禁掉部分而不是全部。

哎,这样我们不就不能使用Web Service的Proxy了吗?没错,所以我们还必须修改一下JavaScript代码,使用一种相对“原始”的方式访问Web Service方法。如下:

var proxy = {
    _get_path : function(){ return "HiddenRealTypesService.asmx" },
    get_defaultFailedCallback : function(){},
    get_defaultUserContext : function(){},
    get_timeout : function(){ return 0; /* unlimited */ }
};

function calculateSalary()
{
    var emp = { '__type' : $get("comboStatus").value };
    emp.Years = parseInt($get("txtYears").value, 10);
    
    Sys.Net._WebMethod._invoke(
        proxy, // proxy
        "CalculateSalary", // method name for calling
        "CalculateSalary", // method name for showing
        false, // do not use HTTP GET
        { employee : emp }, // parameters
        onComplete // onSuccess
    );
}

这里定义了一个Proxy,这是使用Sys.Net._WebMethod所必须的。上面代码的原理,在我以前的文章《深入Atlas系列:Web Sevices Access in Atlas(7) - RTM中的客户端支持》有比较详细的分析。

到现在,我们终于完全隐藏了服务器端的类型信息,不过这是靠编写更多的代码而换来的(不过似乎还算好)。孰优孰劣,孰轻孰重,就只能请大家自己判断了。:)

点击这里下载代码。

深入Atlas系列:Web Sevices Access in Atlas示例(6) - 在客户端隐藏服务器端类型信息...相关推荐

  1. 深入Atlas系列:Web Sevices Access in Atlas示例(4) - 使用HTTP GET调用Web Services方法...

    在之前的例子里,由于Atlas客户端在调用Web Services方法时总是使用了Sys.Net.ServiceMethod类,因此始终使用了HTTP POST方法与服务器端进行交互.POST方法有其 ...

  2. 深入Atlas系列:探究Application Services(2) - 自定义服务器端Profile Service支持

    在上一篇文章中,我们讨论了使用ASP.NET AJAX默认的Profile Service.一般来说,它已经能够迎合大多数应用的需要了.不过除此之外,ASP.NET AJAX还提供了让我们自定义Pro ...

  3. 深入Atlas系列:探究序列化与反序列化能力(下) - JavaScriptSerializer

    在ASP.NET AJAX中,客户端的序列化与反序列能力由Sys.Serialization.JavaScriptSerializer类的serialize和deserialize两个静态方法提供.在 ...

  4. Atlas系列一:Atlas功能特点FAQ

    1:Atlas是否支持多字符集? 支持,可以在test.cnf中指定. #默认字符集,设置该项后客户端不再需要执行SET NAMES语句 charset = utf8 2:Atlas是否支持事物操作? ...

  5. return error怎么定义_SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势

    200105-SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势 虽然 http 的提供了一整套完整.定义明确的状态码,但实际的业务支持中,后端并不总会遵守这套规则 ...

  6. boot返回码规范 spring_SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势

    200105-SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势 虽然 http 的提供了一整套完整.定义明确的状态码,但实际的业务支持中,后端并不总会遵守这套规则 ...

  7. WEB前后端交互原型通用元件库、常用组件、信息输出、信息输入、信息反馈、综合系列、页面交互、首页、分类页、内容详情、用户中心、注册登录、找回密码、元件库、web元件库、rplib、axure

    WEB前后端交互原型通用元件库.常用组件.信息输出.信息输入.信息反馈.综合系列.页面交互.首页.分类页.内容详情.用户中心.注册登录.找回密码.元件库.web元件库.rplib.axure原型 we ...

  8. 基于PaddleHub一键部署的图像系列Web应用服务

    基于PaddleHub一键部署的图像系列Web应用服务 思路过程 相关资源 1.Github源码:https://github.com/livingbody/AutoCutout 2.CSDN文章:h ...

  9. Zabbix监控学习系列(2):agent的安装与Server端添加客户端

    Zabbix监控学习系列(2) 简介描述 1. windows的客户端安装 1. 1手动安装包安装,安装过程中配置 1. 2免安装压缩包,解压后修改配置文件 2. Linux的客户端安装 3.在Zab ...

最新文章

  1. 报名 | 计算机视觉讲座:师兄带你从菜鸟到实战!
  2. Bash 文件夹操作
  3. 《Spring设计思想》AOP设计思想与原理(图文并茂)
  4. c语言稀疏矩阵做除法,稀疏矩阵的除法
  5. shell获取ip的值
  6. java开发环境jdk1.8_linux 搭建 jdk1.8 java开发环境
  7. RISC-V正在采取行动,避免MIPS类的碎片化
  8. 前端技术基础(一):浏览器相关
  9. Windows Phone 7 文件下载进度和速度显示
  10. 马云谈 5G 危机;腾讯推出车载版微信;Ant Design 3.22.1 发布 | 极客头条
  11. Vue父组件访问子组件属性和方法、父子组件双向绑定(两种方法)
  12. excel教程自学网_超实用!良心推荐15个神级自学网站,内容全面质量又高
  13. python随手记自动记账_简化记账——我的“随手记”
  14. 【简历】不带简历就是潇洒?醒醒吧
  15. RS232/RS485转4G DTU 上传基于Modbus协议的温湿度传感器数据到远程TCP服务器
  16. 手把手教你php调用短信接口(smsapi)实现发送短信验证码
  17. 2012百度移动开发者大会汇报
  18. 一次win10更新引发的Grub Rescue
  19. nape.dynamics.InteractionGroup
  20. Natural Sea Beauty以色列护肤品NSB外星人面膜,为肌肤赋予能量

热门文章

  1. 利用Landsat8 进行利用土地分类
  2. 200多位专家热议“智慧城市” 建议尽快完善标准体系
  3. linux 线程与CPU绑定
  4. GRUB引导菜单加密
  5. 如何利用百度API地图进行定位,非gps 定位
  6. AGC029 E: Wandering TKHS
  7. 法总统:英国若“无协议脱欧” 将成最大输家
  8. 《Unity着色器和屏幕特效开发秘笈》—— 3.4 创建BlinnPhong高光类型
  9. Spring-security配置
  10. Linux如何编译安装源码包软件