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

简介:
深入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  =   " [url]http://tempuri.org/[/url] " )]
[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中的客户端支持 》有比较详细的分析。


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

  点击这里下载代码。



本文转自 jeffz 51CTO博客,原文链接:http://blog.51cto.com/jeffz/60657,如需转载请自行联系原作者

相关文章
|
6月前
|
算法 Java Go
【GoGin】(1)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
gin 框架中采用的路优酷是基于httprouter做的是一个高性能的 HTTP 请求路由器,适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用,特别适合需要高性能和简单路由的应用场景。
555 4
|
6月前
|
弹性计算 定位技术 数据中心
阿里云服务器配置选择方法:付费类型、地域及CPU内存配置全解析
阿里云服务器怎么选?2025最新指南:就近选择地域,降低延迟;长期使用选包年包月,短期灵活选按量付费;企业选2核4G5M仅199元/年,个人选2核2G3M低至99元/年,高性价比爆款推荐,轻松上云。
677 11
|
存储 弹性计算 安全
阿里云服务器付费类型、地域、镜像、存储、带宽和安全组设置与选择注意事项参考
在我们通过自定义购买的方式购买阿里云服务器器ECS时,会有多个选项,有的新手用户可能并不是很清楚这些选项是什么,选择或设置时需要注意什么,本文将从付费类型、地域与可用区、镜像、存储、带宽和安全组等多个方面,为您详细解析云服务器购买过程中各个参数与配置的选择注意事项,以供参考。
597 66
|
存储 监控 API
【Azure App Service】分享使用Python Code获取App Service的服务器日志记录管理配置信息
本文介绍了如何通过Python代码获取App Service中“Web服务器日志记录”的配置状态。借助`azure-mgmt-web` SDK,可通过初始化`WebSiteManagementClient`对象、调用`get_configuration`方法来查看`http_logging_enabled`的值,从而判断日志记录是否启用及存储方式(关闭、存储或文件系统)。示例代码详细展示了实现步骤,并附有执行结果与官方文档参考链接,帮助开发者快速定位和解决问题。
329 22
|
弹性计算 安全 Linux
阿里云服务器镜像解析:镜像类型对比、适用场景与选择策略参考
阿里云服务器镜像,作为ECS实例的“装机盘”,不仅提供了操作系统,还包含了初始化应用数据和预装软件,云服务器镜像的选择对于云服务器的性能和稳定性起着至关重要的作用,选择合适的镜像对于云服务器的性能和稳定性至关重要。本文将深入解析阿里云服务器提供的多种镜像类型,从公共镜像到社区镜像,全面介绍每种镜像的特点、优势以及选择建议,帮助用户根据自身需求做出适合自己的选择。
1420 12
|
12月前
|
Go API 定位技术
MCP 实战:用 Go 语言开发一个查询 IP 信息的 MCP 服务器
随着 MCP 的快速普及和广泛应用,MCP 服务器也层出不穷。大多数开发者使用的 MCP 服务器开发库是官方提供的 typescript-sdk,而作为 Go 开发者,我们也可以借助优秀的第三方库去开发 MCP 服务器,例如 ThinkInAIXYZ/go-mcp。 本文将详细介绍如何在 Go 语言中使用 go-mcp 库来开发一个查询 IP 信息的 MCP 服务器。
745 2
|
存储 弹性计算 固态存储
阿里云服务器配置怎么选择?根据用户类型及使用场景配置推荐
如何选择阿里云服务器配置?2025年全解析!个人用户可选68元/年的轻量应用服务器(2核2G、200M带宽),企业用户推荐199元/年的ECS通用算力型u1实例(2核4G、5M带宽)。针对不同需求,还有内存型、计算型、高主频型及GPU服务器等多规格实例。带宽选择需根据访问量,小流量应用3M即可,高流量建议10M起步。存储方面,系统盘40GB够用,数据盘按需选择ESSD或SSD云盘,确保I/O性能满足业务需求。阿小云为你整理最新攻略,助你高效选型!
|
人工智能 搜索推荐 程序员
用 Go 语言轻松构建 MCP 客户端与服务器
本文介绍了如何使用 mcp-go 构建一个完整的 MCP 应用,包括服务端和客户端两部分。 - 服务端支持注册工具(Tool)、资源(Resource)和提示词(Prompt),并可通过 stdio 或 sse 模式对外提供服务; - 客户端通过 stdio 连接服务器,支持初始化、列出服务内容、调用远程工具等操作。
2655 5

热门文章

最新文章

下一篇
开通oss服务