基于ArcGIS API 4.12 for JS 的开发实践(五)——JavaScript调用WebService请求

1、使用场景

我在练习ArcGIS API 4.12 for JavaScript 开发的时候,需要把数据库(我用的MySQL5.7)中的数据,通过查询调取出来,然后在客户端也就是网页端用JavaScript来呈现出来,比如我在文章:基于ArcGIS API 4.12 for JS 的开发实践(四)——html5tooltips气泡标注窗 里面用到的,把智能设备的经纬度及设备状态信息从数据库调取出来,然后通过定制html5tooltips这个开源插件实现比较舒服的呈现方式。下图就是智能设备在数据库中存放的方式:

               

那有同学就问了,JavaScript也可以直接有API来操作数据库啊,干嘛要多此一举呢?首先,JavaScript确实有这个能力直接操作数据库,但是JavaScript是直接暴露在客户端,任何人都是通过浏览器的开发功能查看你的JavaScript源码,这样你的数据库就有暴露的风险,虽然可以通过加密JavaScript源码来避免这种情况,可是破解和加密这对双胞胎使得这种风险依然存在。

所以我们可以通过WebService这种方式,在服务器端发布WebService,客户端用JavaScript通过URL来调用WebService,这样客户端最多也就暴露了一个服务器端的WebService服务URL,这对服务器端的数据库威胁大大降低。另外对于刚上手JavaScript的开发者,使用自己熟悉的语言比如C#或者Java来开发一个服务器端的WebService服务,难度将大大降低。我比较熟悉C#,所以我在Visual Studio上面开发了一个asp.net WebService服务。

2、基于asp.net的 WebService服务

(1)创建一个WebService服务

在Visual Studio中创建WebService服务的操作流程就不多说了,傻瓜式的步骤,如果不清楚百度一下吧。我这里着重讲一下JavaScript调用的WebService服务创建时候要注意的内容。创建好之后,一个自定义的服务大概是下面代码的样子:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class smcJsonService : System.Web.Services.WebService
{
    //webservice Methord 
    ……………………………………………………
}

这就是我们创建好的自定义的WebService服务,我用的服务名是“smcJsonService”。注意:

[System.Web.Script.Services.ScriptService]

这一行代码,系统默认是注释掉的,并且有一句提醒就是:若要允许ASP.NET AJAX调用此Web服务,请去掉注释。因为我们要在JavaScript调用此Web服务,那么我们就需要把这行代码的注释去掉。

(2)添加WebMethod(不带参数),Dojo的XMLHttpRequest进行Post

在class smcJsonService的大括号里面添加我们的第一个WebMethod,代码如下:

[WebMethod]
public string GetAllRows()
{
    DataTable dt = MySqlHelper.getRows("SELECT * FROM smartcover.smc ");
    string jsonResult = JsonHelper.DataTableToJson(dt);
    return jsonResult;
}

这个就是一个WebMethod,我们后面发布之后可以通过URL来调用这个方法。这里有几个说明:

第一:WebMethod方法GetAllRows()上面有一个[WebMethod]修饰,这个就是告诉编译器,这是一个WebMethod。这个可以是有参数的,可以是没有参数的,返回值也可以自定义,这里我返回一个字符串。

第二:代码第一句“DataTable dt = MySqlHelper.getRows("SELECT * FROM smartcover.smc ");”这里是通过C#查询数据库,并把返回值放到一个DataTable里面。

第三:代码第二句“string jsonResult = JsonHelper.DataTableToJson(dt);”,这里是我把存放数据库查询结果的DataTable转换成JSON格式的数据。并返回。(文章顶部会提供MySqlHelper、JsonHelper的完整代码,需要自取)

最后需要在JavaScript端来调用,我这里先使用Dojo的xhr来发送请求:

require([
    "dojo/_base/xhr",
    "dojo/domReady!"],
    function(xhr,
            dom,
            on) 
    {
        xhr.post({
                //请求自建的WebService服务,返回的是JSON字符串
                url:"http://localhost/SMCJsonService/smcJsonService.asmx/GetAllRows",
                headers: { "Content-Type": "application/json" },
                handleAs : "json",
                load : function(data) 
                {
                    //dosomting
                    var jb=JSON.parse(data.d);
                },
                error:function (error) 
                {
                    console.log("error"+error);
                }
               });
    }
);

这里是使用Dojo的require以及xhr来向WebService发送请求,首先要在require的参数中加入"dojo/_base/xhr",然后在回调函数function中定义变量名xhr,然后在回调函数中调用xhr.post,正式向WebService发送请求。请注意,因为我们返回的是JSON格式的字符串,所以这里的“headers”以及“handleAs”都采用JSON的方式,然后在“load”加载成功事件中,它的参数“data”就是调用的返回值,但是“data”本身并不是GetAllRows()返回的JSON字符串,而是在data.d中存放,所以我们把JSON字符串要重新解析为JSON格式的数据:var jb=JSON.parse(data.d);,或者返回数据后就可以进行你的操作,比如把数据库中的数据呈现出来,这里的代码就不详细说了。这里返回的是我的设备点集,是一个JSON数组,我可以遍历它。

注意上述内容,是不带参数的WebMethod,一般不会出现问题,如果遇到“Failed to load resource: the server responded with a status of 500 (Internal Server Error)”这个错误,在只要你在浏览器地址栏里面能打开你的WebService的前提下,多半是因为参数问题或者返回格式的问题,要好好查一下。下面我说说带参数的WebMethod。

(3)添加WebMethod(带参数),Dojo的XMLHttpRequest进行Post

在class smcJsonService的大括号里面添加我们的第二个WebMethod,代码如下:

[WebMethod]
public string UserCheck(string jsonObject)
{
    ResultJson rj = new ResultJson();
    if (jsonObject == null || jsonObject == "")
    {
        User myuser = JsonHelper.JsonDeserialize<User>(jsonObject);
        string username = myuser.UserName;
        string userpassword = myuser.UserPassword;
        //dosomthing(用SQL语句验证用户密码是否正确)
        return  JsonHelper.ObjectToJson(rj);//通过对ResultJson赋不同的值,来指示不同的验证结果
    }
}

大家看上述代码,这次UserCheck这个WebMethod带了一个参数,字符串string类型,但其实这是一个JSON字符串;返回值也是一个字符串string类型,这也是一个JSON字符串。里面有一个我自定义的类ResultJson,而JsonHelper可以帮助我把自定义类ResultJson转换成一个JSON字符串返回。

大家注意,参数是一个JSON字符串,可以是前端的一个JSON对象,然后由JavaScript转换成JSON字符串。比如我在JavaScript端有这样的代码:

        var userJson={
            UserName:myusername,
            UserPassword:myuserpassword
        };

上面的JavaScript是我获取了前端的用户名和密码,然后希望在后端进行数据库验证是否密码符合。两个字段“UserName”和“UserPassword”并不是随意起的名字,而是在后端我有一个“User”类,而这个“User”类是有“UserName”和“UserPassword”这两个公共属性的,JavaScript和ASP.NET这两边名字必须完全一致。以下是ASP.NET端“User”类的代码:

public class User
{
        private string _userName;
        /// <summary>
        /// 用户名
        /// </summary>
        public string UserName
        {
            get { return _userName; }
            set { _userName = value; }
        }
        private string _userPassword;
        /// <summary>
        /// 要用户密码
        /// </summary>
        public string UserPassword
        {
            get { return _userPassword; }
            set { _userPassword = value; }
        }
        private string _telephone;
        /// <summary>
        /// 电话
        /// </summary>
        public string Telephone
        {
            get { return _telephone; }
            set { _telephone = value; }
        }
        private string _mail;
        /// <summary>
        /// 用户邮箱
        /// </summary>
        public string Mail
        {
            get { return _mail; }
            set { _mail = value; }
        }
        private string _registerTime;
        /// <summary>
        /// 注册时间
        /// </summary>
        public string RegisterTime
        {
            get { return _registerTime; }
            set { _registerTime = value; }
        }
        public User()
        {
            _userName = string.Empty;
            _userName = string.Empty;
            _userName = string.Empty;
            _userName = string.Empty;
            _registerTime = "1990/1/1 00:00:00";
        }
    }

avaScript和ASP.NET这两边名字完全一致后,我就可以在ASP.NET端解析传过来的参数“jsonObject”,就是下面这句代码:

        User myuser = JsonHelper.JsonDeserialize<User>(jsonObject);

ASP.NET端的代码其实没有什么可说的,就是解析传过来的参数,再执行一些验证操作,最后返回验证结果,对于ASP.NET端来说,传入参数和返回值都是字符串string类型的。

下面我着重说一下JavaScript端的代码,以及常碰到的问题,先贴代码:

var userJson={
        UserName:myusername,
        UserPassword:myuserpassword
    };
var data1=JSON.stringify(userJson);
var data2={jsonObject:data1};
var data3=dojo.toJson(data2);
var data4=JSON.stringify(data2);

require([
    "dojo/_base/xhr",
    "dojo/domReady!"],
    function(xhr,
            dom,
            on) 
    {
        xhr.post({
                //请求自建的WebService服务,返回的是JSON字符串
                url:"http://localhost/SMCJsonService/smcJsonService.asmx/UserCheck",
                headers: { "Content-Type": "application/json" },
                postData: data3,
                handleAs : "json",
                load : function(data) 
                {
                    //dosomting
                    var jb=JSON.parse(data.d);
                    console.log(jb.Result);
                },
                error:function (error) 
                {
                    console.log("error"+error);
                }
               });
    }
);

因为我们请求的WebService服务UserCheck是需要输入参数的,因此我们在xhr.post事件中加了一行代码,就是让xhr提交请求的时候把参数带上,就是下面这行代码:

                    postData: data3,

上面的“data3”,是需要传到WebService服务的参数,“postData”是xhr.post的参数名。而“data3”的来历,大家可以看下require语句的上面部分,也就是下面这段代码:

var userJson={
        UserName:myusername,
        UserPassword:myuserpassword
    };
var data1=JSON.stringify(userJson);
var data2={jsonObject:data1};
var data3=dojo.toJson(data2);
var data4=JSON.stringify(data2);

上面代码中,首先我根据从客户端获取到的用户名和密码两个值(myusername和myuserpassword),创建了一个JSON对象“userJson”,然后我把这个JSON对象转换成JSON字符串,即“data1”。然后由“data1”这个变量,再创建了一个JSON对象“data2”。然后我又把“data2”这个JSON对象转换成JSON字符串,即“data3”和“data4”,这里我用了两种转换方式,结果都是一样的。读者可以选用其中一种。

细心的读者,可能看到了,这个“data2”JSON对象里面,“data1”是JSON字符串,为什么里面有一个“jsonObject”这个名称呢?从何而来呢?这个“jsonObject”其实就是我在WebMethod函数“UserCheck”中自定义的一个参数名称,这个名称读者可以换成其他方便自己辨识的名称,并非固定的。也就是说如果,你的WebMethod函数的参数名是“User”,这么这里这个“data2”JSON对象里面就应该用var data2={User:data1};,只要跟你WebMethod函数的参数名一样就行了。最后,我们需要把这个“data2”JSON对象转换成JSON字符串,如果不转换直接用“postData: data2,”就会报“Failed to load resource: the server responded with a status of 500 (Internal Server Error)”这个错误。经过我的测试,“data3”和“data4”都可以正常调用WebMethod,而“data1”和“data2”都会报上面这个Internal Server Error错误,读者可以自行测试。

说实话,这个问题困扰了我很久,我也是不断的找文章,不断地测试才发现问题所在,希望对大家有所帮助,节省时间。

(4)使用AJAX进行Post请求webservice

除了用Dojo的XMLHttpRequest进行Post,还可以用jQuery的$.ajax进行Post请求,其实$.ajax的Post请求跟XMLHttpRequest的Post请求差不多,只有些许的差别,下面看代码:

var userJson={
        UserName:myusername,
        UserPassword:myuserpassword
};
var data1=JSON.stringify(userJson);
var data2={jsonObject:data1};
var data3=dojo.toJson(data2);
var data4=JSON.stringify(data2);
    
$.ajax({
    type: "POST",
    url: "http://localhost/SMCJsonService/smcJsonService.asmx/UserCheck",
    data: data3,
    contentType: "application/json; charset=utf-8",
    dataType: "json"
    }).done(function(data)
    {
        var jb=JSON.parse(data.d);
        console.log(jb.Result);
    }).fail(function(a, b, c, d) 
    {
        alert("Failure: " 
            + JSON.stringify(a) + " " 
            + JSON.stringify(b) + " " 
            + JSON.stringify(c) + " " 
            + JSON.stringify(d) );
});

上面这段代码就不详说了,跟之前的XMLHttpRequest的Post请求差不多,只是处理函数名称有不一样,另外一定要记着加上“contentType: "application/json; charset=utf-8",”,否则会报“Failed to load resource: the server responded with a status of 500 (Internal Server Error)”这个错误。

我在开发过程中,上述两种方式都试过,有时候还是会出现“Failed to load resource: the server responded with a status of 500 (Internal Server Error)”这个错误,而在浏览器地址栏中输入WebService的服务URL,可以正常打开。如果你还是出现这个Internal Server Error错误,或者是CORS policy跨域访问的错误我建议你用下一jsonp这种数据传输方式,jsonp是专门为解决跨域问题而生的。下面是使用jsonp这种数据传输方式的代码:

var userJson={
        UserName:myusername,
        UserPassword:myuserpassword
};
var data1=JSON.stringify(userJson);
var data2={jsonObject:data1};
var data3=dojo.toJson(data2);
var data4=JSON.stringify(data2);
    
$.ajax({
    type: "POST",
    url: "http://localhost/SMCJsonService/smcJsonService.asmx/UserCheck",
    data: data2,
    jsonp: "callback",
    contentType: "application/json; charset=utf-8",
    dataType: "jsonp"
    }).done(function(data)
    {
        var v=data.Result;
        console.log(v);
    }).fail(function(a, b, c, d) 
    {
        alert("Failure: " 
            + JSON.stringify(a) + " " 
            + JSON.stringify(b) + " " 
            + JSON.stringify(c) + " " 
            + JSON.stringify(d) );
});

大家看上面的代码,跟XMLHttpRequest的Post不一样,并且XMLHttpRequest的Post中没有支持jsonp的内容;并且也跟前面的$.ajax的Post代码不一样,主要有以下不同:

第一:$.ajax的Post请求里面多了一段代码“jsonp: "callback",”,这段代码必须加上,并且对应ASP.NET端的WebMethod也需要相应修改,先按下不说。

第二:$.ajax的Post请求里面的“dataType”由原来的“json”换成了“jsonp”。

第三:$.ajax的Post请求里面的“data”由原来的“data3”换成了“data2”。如果你继续用“data3”这样的值,就会报“Failed to load resource: the server responded with a status of 500 (Internal Server Error)”这个错误。

第四:$.ajax的Post请求的返回结果不是原来的“data.d”进行取值了,“var v=data.Result;”这行代码里而是返回的直接是一个JSON对象。

第五:需要修改ASP.NET端WebMethod的代码,修改后的代码如下:

[WebMethod]
public string UserCheck(string jsonObject)
{
    ResultJson rj = new ResultJson();
    Context.Response.Clear();
    Context.Response.ContentType = "aplication/json";
    string callback = Context.Request.QueryString["callback"] != null ?     Context.Request.QueryString["callback"] : "?";
    if (jsonObject == null || jsonObject == "")
    {
        User myuser = JsonHelper.JsonDeserialize<User>(jsonObject);
        string username = myuser.UserName;
        string userpassword = myuser.UserPassword;
        //dosomthing(用SQL语句验证用户密码是否正确)
        Context.Response.Write(callback + "(" + JsonHelper.ObjectToJson(rj) + ");");
        Context.Response.End();
        return callback + "(" + JsonHelper.ObjectToJson(rj) + ");";//通过对ResultJson赋不同的值,来指示不同的验证结果
    }
}

这里面添加了几行代码,读者应该能看出来首先是这三句:

Context.Response.Clear();
Context.Response.ContentType = "aplication/json";
string callback = Context.Request.QueryString["callback"] != null ?     Context.Request.QueryString["callback"] : "?";

然后就是最后返回值的时候记上下面三句:

Context.Response.Write(callback + "(" + JsonHelper.ObjectToJson(rj) + ");");
Context.Response.End();
return callback + "(" + JsonHelper.ObjectToJson(rj) + ");";

在下愚钝,暂时还不能理解以上六句代码的内在含义,不过确实解决了JavaScript访问WebService时遇到的跨域问题,比如“has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.”这个问题真是困扰很久,只有不断的查,不断的试才能解决问题。其实后来我发现使用WebAPI也可以,好像更方便,因为我暂时还没有涉猎,等我研究好了再分享给大家。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值