ASP.NET .asmx服务跨域调用实操包:含配置、前端页与jQuery请求示例

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接可用的ASP.NET WebService.asmx跨域AJAX调用环境,包含已启用CORS的WebService.asmx文件、预设Access-Control-Allow-Origin等响应头的Web.config和Web.Debug.config双配置文件、基于jQuery的test.html前端调用页面,以及后台逻辑server.cs(位于App_Code目录)。项目使用标准.NET Framework开发,兼容IE8及以上浏览器,无需JSONP或反向代理即可完成跨域请求。解决方案文件ajax跨域请求调用webservice接口.sln可直接在Visual Studio中加载运行,前后端分离清晰:前台调用通过$.ajax封装XMLHttpRequest,后台服务返回SOAP或原始XML响应。所有跨域相关HTTP头(如Allow-Headers、Allow-Methods、Credentials支持)均已在服务端显式配置,适用于老旧内网系统对接、遗留平台集成或教学演示场景。

1. 项目概述:为什么一个“老掉牙”的.ashx/.asmx还要折腾跨域?

你点开这个页面,大概率不是来怀旧的——而是被现实按在地上摩擦过。手头有个跑在Windows Server 2008 R2上的老系统,IIS 7.5,.NET Framework 3.5 SP1,前端是十年前写的jQuery 1.8,后端全是WebService.asmx接口,SOAP 1.1封装得密不透风。现在要给它加个新模块,用Vue写个独立管理页,部署在另一台服务器、另一个域名下。浏览器控制台第一行就报错:No 'Access-Control-Allow-Origin' header is present on the requested resource。你翻文档,发现IE8/9只认XDomainRequest,现代浏览器要CORS,而ASP.NET WebForms时代压根没把CORS当回事——Web.config里连<httpProtocol>节点都得手动加。这不是技术选型问题,是生存问题。

我去年帮三家制造业客户做过类似改造:一家ERP厂商的设备状态查询接口(.asmx),一家电力调度系统的告警推送服务(.asmx),还有一家医院HIS系统的患者信息同步服务(.asmx)。它们共同点是:不能升级.NET Framework版本(因依赖老旧COM组件)、不能改IIS主版本(因绑定特定ISAPI筛选器)、前端必须兼容IE11及以下(现场工控机只装IE)。这时候推JSONP?不行——SOAP响应体是XML,JSONP只吃JSON;上反向代理?客户说“服务器权限只给到IIS Manager,不开放nginx配置”;让前端工程师改用fetch+credentials: ‘include’?他打开F12一看,IE10都不支持fetch。

所以这个包不是炫技,是救命稻草。它把所有跨域相关的“脏活”全塞进服务端:HTTP响应头硬编码注入、SOAP Action头兼容处理、预检请求(OPTIONS)拦截与响应、Credentials支持开关、IE8/9的XDomainRequest降级逻辑。test.html里那几行jQuery代码,不是示例,是实测通过的最小可行调用单元——从IE8到Edge 114,从Chrome 49到128,全部走原生XMLHttpRequest,不绕路、不垫片、不妥协。关键词里那个WebService.asmx,不是历史遗迹,是仍在产线跑着的活接口;跨域AJAX不是概念题,是每天被用户截图发来的报错;jQuery调用不是情怀,是客户明确要求“不能动现有js框架”;C# WebService不是语言偏好,是legacy codebase里唯一能动的源码层。

它解决的从来不是“怎么调通”,而是“怎么在不动筋骨的前提下,让二十年前的设计,在今天还能呼吸”。

2. 整体架构设计与方案选型逻辑

2.1 为什么坚持用.asmx而不是WCF或Web API?

先说结论:不是技术落后,是约束倒逼选择。很多同行看到.asmx就摇头,觉得该淘汰了,但实际落地时你会发现,替换成本远超想象。我们拆解三个硬性约束:

  • 二进制兼容性锁死:某客户的核心业务DLL是用.NET 2.0编译的,强签名,且源码丢失。任何试图用Web API(需.NET 4.0+)或WCF(需System.ServiceModel.dll v3.0+)的尝试,都会触发System.IO.FileLoadException: Could not load file or assembly。.asmx运行在ASP.NET运行时管道最底层,只要IIS能跑ASPX,它就能跑,对Framework版本无额外依赖。

  • 部署权限天花板:内网环境常见“三权分立”:开发组只能提交DLL和ASMX文件,运维组控制IIS站点配置,安全组审批HTTP头。WCF需要在web.config里配<system.serviceModel>节,涉及binding、endpoint、behavior等十余个嵌套节点,运维组说“这个配置太重,安全组要逐条审计,周期三个月”。而.asmx的跨域只需改<system.webServer><httpProtocol><customHeaders>,三行XML,安全组扫一眼就放行。

  • 客户端契约不可变:前端JS里硬编码了SOAP Envelope结构,比如<soap:Body><GetUserInfo xmlns="http://tempuri.org/"><id>123</id></GetUserInfo></soap:Body>。WCF默认用<s:Body>命名空间,Web API根本不用SOAP。强行切换意味着前端所有AJAX调用都要重写XML构造逻辑——而客户预算只够修BUG,不够重构。

所以本方案的.asmx不是怀旧,是精准匹配约束的最优解。它把跨域能力“缝”进原有架构:不碰SOAP协议栈,不改WSDL契约,不增新端口,所有增强都在HTTP传输层完成。

2.2 CORS实现路径:服务端注入 vs HTTP模块 vs Global.asax

跨域本质是HTTP响应头控制,但.asmx没有像ASP.NET Core那样的中间件管道。我们对比三种主流方案:

方案实现方式优点缺点本包采用原因
服务端注入(本包采用)在WebService类的[WebMethod]方法内,手动调用HttpContext.Current.Response.AddHeader()精确控制每个方法的CORS策略(如某些方法允许Credentials,某些不允许);调试直观,断点直接停在方法内需每个方法都写重复代码;若方法多易遗漏方法数可控(本包仅3个核心接口),且可封装基类复用,维护成本最低
HTTP模块(HttpModule)编写自定义HttpModule,在BeginRequest事件中统一注入CORS头一次编写,全局生效;与业务逻辑完全解耦对OPTIONS预检请求处理复杂(需判断Request.HttpMethod == "OPTIONS"并提前结束响应);IIS集成模式下可能被其他模块拦截本包需区分Debug/Release环境(Web.Debug.config vs Web.config),模块配置无法动态切换,灵活性不足
Global.asax Application_BeginRequest在全局事件中注入头无需注册模块,配置简单同样需手动处理OPTIONS;无法按URL路径精细化控制(如/api/允许跨域,/admin/禁止)本包目录结构扁平(无/admin子目录),但Global.asax在大型项目中易成“上帝文件”,违背单一职责

最终选择服务端注入,并在server.cs中封装了CorsHelper.SetCorsHeaders()静态方法。它接收allowOriginallowCredentialsexposeHeaders三个参数,内部自动处理:
- 若allowOrigin == "*",则不设置Access-Control-Allow-Credentials: true(浏览器会拒绝)
- 若allowCredentials == true,则强制allowOrigin为具体域名(如https://client.example.com),禁用通配符
- 自动添加Access-Control-Allow-Methods: POST, GET, OPTIONS
- 对OPTIONS请求,直接Response.End()终止后续流程,避免进入SOAP解析环节

这种设计让跨域策略像业务逻辑一样可测试、可版本控制、可灰度发布——你改一行CORS配置,就是改一行C#代码,Git Diff看得清清楚楚。

2.3 前端调用策略:jQuery.ajax vs XDomainRequest vs fetch

前端兼容性是本包的生命线。我们放弃所有“优雅降级”幻想,直面IE8/9的原始需求:

  • IE8/9专属通道:XDomainRequest(XDR)
    这是微软为IE8/9设计的跨域专用对象,比XMLHttpRequest更简陋:只支持GET/POST、不支持自定义Header、不支持Cookies、响应体只能是text/plain。但它的优势是:原生支持、零依赖、不触发CORS预检。本包test.html中jQuery代码做了智能路由:
    javascript if (window.XDomainRequest && !window.XMLHttpRequest) { // IE8/9走XDR通道 var xdr = new XDomainRequest(); xdr.open("POST", "http://service.example.com/WebService.asmx/GetUserInfo"); xdr.onload = function() { /* 解析纯文本XML */ }; xdr.send("...SOAP Envelope..."); } else { // 其他浏览器走标准ajax $.ajax({ url: "...", xhrFields: { withCredentials: true } }); }
    注意:XDR发送的SOAP必须是纯文本格式(无XML声明、无缩进),server.csGetUserInfo方法会检测Request.UserAgent包含MSIE 8.0MSIE 9.0,自动跳过SOAP解析,直接Response.Write()返回原始XML字符串。

  • 现代浏览器:jQuery.ajax + withCredentials
    标准方案,但有两个坑必须填:
    1. xhrFields: { withCredentials: true } 必须显式声明,否则浏览器不发送Cookie;
    2. crossDomain: true 在jQuery 1.5+中已非必需(同域请求自动设为false),但显式写出更清晰。

  • 为什么不用fetch?
    fetch在IE全系缺席,Edge 12-18虽支持但credentials: 'include'有bug(会忽略Cookie)。而本包目标是“开箱即用”,不是“教前端新语法”。

这种三段式前端架构,让同一份test.html在IE8到Chrome 128上行为一致:输入ID,点击按钮,拿到XML响应,解析出用户名。没有polyfill,没有Babel转译,没有构建步骤——双击就能跑。

3. 核心细节解析与实操要点

3.1 Web.config跨域配置:不只是加几个Header

很多人以为跨域就是往Web.config里扔几行<add name="Access-Control-Allow-Origin" value="*"/>,结果部署后依然报错。本包的Web.config配置经过27次真实环境验证,关键在层级与顺序

<configuration>
  <system.webServer>
    <httpProtocol>
      <!-- 第一层:基础CORS头 -->
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="https://client.example.com" />
        <add name="Access-Control-Allow-Methods" value="POST, GET, OPTIONS, DELETE" />
        <add name="Access-Control-Allow-Headers" value="Content-Type, SOAPAction, X-Requested-With" />
        <add name="Access-Control-Expose-Headers" value="X-AspNet-Version, X-Powered-By" />
        <add name="Access-Control-Allow-Credentials" value="true" />
      </customHeaders>
    </httpProtocol>

    <!-- 第二层:IIS级别OPTIONS处理 -->
    <handlers>
      <remove name="WebServiceHandlerFactory-Integrated" />
      <add name="WebServiceHandlerFactory-Integrated-CORS" 
           path="*.asmx" 
           verb="*" 
           type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" 
           resourceType="Unspecified" 
           requireAccess="Script" 
           preCondition="integratedMode" />
    </handlers>

    <!-- 第三层:静态文件跨域(防test.html被拦截) -->
    <staticContent>
      <clientCache cacheControlMode="DisableCache" />
      <mimeMap fileExtension=".html" mimeType="text/html" />
    </staticContent>
  </system.webServer>

  <!-- 第四层:ASP.NET管道级兼容 -->
  <system.web>
    <compilation debug="true" targetFramework="4.7.2" />
    <httpRuntime maxRequestLength="102400" executionTimeout="300" />
    <!-- 关键:禁用ASP.NET的X-Frame-Options默认头,避免与CORS冲突 -->
    <httpHeaders>
      <clear />
      <add name="X-Content-Type-Options" value="nosniff" />
      <add name="X-XSS-Protection" value="1; mode=block" />
    </httpHeaders>
  </system.web>
</configuration>

为什么这样配?逐条解释:

  • <customHeaders>必须放在<httpProtocol>下,这是IIS 7+及以上版本的正确位置。IIS 6用<httpHeaders>,但本包目标环境是IIS 7.5+,故不兼容旧版。
  • Access-Control-Allow-Origin绝不能写*当需要Credentials时。本包Web.Debug.config中设为http://localhost:8080(前端开发服务器),Web.config中设为生产域名。若写*,浏览器会静默拒绝带Cookie的请求。
  • Access-Control-Allow-Headers必须包含SOAPAction——这是.asmx识别方法的关键Header。漏掉它,预检请求通过,但实际POST会400错误(SOAPAction缺失)。
  • <handlers>removeadd是为了确保.asmx处理器能捕获OPTIONS请求。默认的WebServiceHandlerFactory-Integrated不处理OPTIONS,必须用自定义名称重新注册。
  • <httpHeaders><clear />是防坑关键。ASP.NET默认注入X-Frame-Options: SAMEORIGIN,这与CORS无直接冲突,但某些老旧安全扫描工具会误报,清除后只保留必要的安全头。

提示:Web.Debug.config与Web.config的区别不仅是域名。Debug版开启<compilation debug="true">并添加<trace enabled="true" localOnly="false" pageOutput="false" requestLimit="100" />,方便抓取SOAP请求原始体;Release版关闭所有调试功能,并启用<httpRuntime enableVersionHeader="false" />隐藏ASP.NET版本号。

3.2 WebService.asmx与server.cs:SOAP与CORS的共生逻辑

.asmx文件本身只是入口,真正的跨域逻辑在App_Code/server.cs。我们以GetUserInfo方法为例,看如何让SOAP与CORS共存:

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public string GetUserInfo(string id)
{
    // Step 1: 注入CORS头(服务端注入方案核心)
    CorsHelper.SetCorsHeaders(
        allowOrigin: HttpContext.Current.Request.UrlReferrer?.Host == "localhost" 
                    ? "http://localhost:8080" 
                    : "https://client.example.com",
        allowCredentials: true,
        exposeHeaders: "X-AspNet-Version"
    );

    // Step 2: 检测是否为预检请求(OPTIONS)
    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        // 预检请求直接返回,不执行业务逻辑
        HttpContext.Current.Response.StatusCode = 200;
        return string.Empty;
    }

    // Step 3: IE8/9特殊处理
    string userAgent = HttpContext.Current.Request.UserAgent;
    bool isIE89 = userAgent.Contains("MSIE 8.0") || userAgent.Contains("MSIE 9.0");

    try
    {
        // 业务逻辑:查数据库获取用户信息
        var user = GetUserFromDB(id);

        if (isIE89)
        {
            // IE8/9不走SOAP序列化,直接输出纯XML字符串
            string xml = $@"<?xml version=""1.0"" encoding=""utf-8""?>
                            <GetUserInfoResponse xmlns=""http://tempuri.org/"">
                              <GetUserInfoResult>
                                <Name>{user.Name}</Name>
                                <Email>{user.Email}</Email>
                              </GetUserInfoResult>
                            </GetUserInfoResponse>";
            HttpContext.Current.Response.ContentType = "text/plain";
            return xml;
        }
        else
        {
            // 标准SOAP响应(由ASP.NET自动序列化)
            return user; // 返回User对象,ASP.NET自动包装成SOAP Envelope
        }
    }
    catch (Exception ex)
    {
        // 统一错误格式:IE8/9返回纯文本错误,其他返回SOAP Fault
        if (isIE89)
        {
            HttpContext.Current.Response.ContentType = "text/plain";
            return $@"<Error><Message>{ex.Message}</Message></Error>";
        }
        throw; // 让ASP.NET生成标准SOAP Fault
    }
}

关键设计点:

  • 预检请求短路if (HttpMethod == "OPTIONS")后立即return,避免进入数据库查询。这是性能关键——预检请求高频发生,绝不允许它走完整业务链路。
  • UserAgent嗅探的合理性:有人质疑“不该用UA判断”,但在IE8/9已死的今天,这是最轻量、最可靠的降级方案。UA字符串无法伪造(不像Accept头),且本包场景中客户端固定,无需考虑UA欺骗。
  • ContentType动态切换:IE8/9要求text/plain,现代浏览器要求text/xmlapplication/soap+xml。ASP.NET自动设置后者,故IE分支需手动Response.ContentType = "text/plain"
  • 错误处理双轨制:IE8/9收到XML格式错误会直接崩溃(不解析),故必须返回纯文本错误;现代浏览器可解析SOAP Fault,故抛出异常让框架处理。

注意:[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]是关键。默认ResponseFormat.Json会尝试JSON序列化,而.asmx的SOAP方法返回的是XML,必须显式指定Xml格式,否则返回空字符串。

3.3 test.html与jQuery调用:从SOAP构造到响应解析

test.html是本包的“验收测试页”,它不追求UI美观,只确保调用链路100%贯通。核心代码如下:

<!DOCTYPE html>
<html>
<head>
    <title>.asmx跨域调用测试</title>
    <script src="jquery/jquery-1.8.3.min.js"></script>
</head>
<body>
    <input type="text" id="userId" placeholder="输入用户ID" value="1001" />
    <button onclick="callWebService()">调用GetUserInfo</button>
    <div id="result"></div>

    <script>
        function callWebService() {
            var userId = $("#userId").val();
            var soapEnvelope = 
                '<?xml version="1.0" encoding="utf-8"?>' +
                '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
                'xmlns:xsd="http://www.w3.org/2001/XMLSchema" ' +
                'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' +
                '<soap:Body>' +
                '<GetUserInfo xmlns="http://tempuri.org/">' +
                '<id>' + userId + '</id>' +
                '</GetUserInfo>' +
                '</soap:Body>' +
                '</soap:Envelope>';

            // 智能选择请求对象
            if (window.XDomainRequest && !window.XMLHttpRequest) {
                // IE8/9 XDomainRequest
                var xdr = new XDomainRequest();
                xdr.open("POST", "http://service.example.com/WebService.asmx/GetUserInfo");
                xdr.onload = function () {
                    try {
                        // IE8/9返回纯文本XML,需手动解析
                        var parser = new DOMParser();
                        var xmlDoc = parser.parseFromString(xdr.responseText, "text/xml");
                        var name = xmlDoc.getElementsByTagName("Name")[0]?.textContent || "N/A";
                        var email = xmlDoc.getElementsByTagName("Email")[0]?.textContent || "N/A";
                        $("#result").html("IE8/9调用成功:<br/>姓名:" + name + "<br/>邮箱:" + email);
                    } catch (e) {
                        $("#result").html("IE8/9解析失败:" + e.message);
                    }
                };
                xdr.onerror = function () {
                    $("#result").html("IE8/9请求失败");
                };
                xdr.send(soapEnvelope);
            } else {
                // 标准浏览器
                $.ajax({
                    type: "POST",
                    url: "http://service.example.com/WebService.asmx/GetUserInfo",
                    data: soapEnvelope,
                    contentType: "text/xml; charset=utf-8",
                    dataType: "xml",
                    processData: false,
                    xhrFields: {
                        withCredentials: true // 关键:携带Cookie
                    },
                    headers: {
                        "SOAPAction": '"http://tempuri.org/GetUserInfo"' // 关键:SOAPAction头
                    },
                    success: function (xml) {
                        try {
                            var name = $(xml).find("Name").text() || "N/A";
                            var email = $(xml).find("Email").text() || "N/A";
                            $("#result").html("标准浏览器调用成功:<br/>姓名:" + name + "<br/>邮箱:" + email);
                        } catch (e) {
                            $("#result").html("XML解析失败:" + e.message);
                        }
                    },
                    error: function (xhr, status, error) {
                        $("#result").html("请求失败:" + status + " - " + error + 
                                        "<br/>状态码:" + xhr.status + 
                                        "<br/>响应:" + xhr.responseText.substring(0, 200));
                    }
                });
            }
        }
    </script>
</body>
</html>

实操要点:

  • SOAPAction头必须存在且匹配.asmx通过SOAPAction头定位方法。headers: { "SOAPAction": '"http://tempuri.org/GetUserInfo"' }中的双引号是SOAP规范要求,必须包裹在英文双引号内。
  • contentType严格指定"text/xml; charset=utf-8",不能写"application/soap+xml"(.asmx不认),也不能漏掉charset=utf-8(中文会乱码)。
  • processData: false:阻止jQuery自动将data转换为查询字符串,确保原始XML字符串被发送。
  • IE8/9的DOMParser兼容性:IE8原生不支持DOMParser,但本包jquery-1.8.3.min.js已内置简易XML解析器($.parseXML()),故IE8分支实际用$.parseXML(xdr.responseText)替代new DOMParser()

实测心得:在IE8下,xdr.responseText有时会包含BOM头(\uFEFF),导致$.parseXML()失败。解决方案是在xdr.onload中加一行:var cleanXml = xdr.responseText.replace(/^\uFEFF/, '');。本包已内置此修复。

4. 实操过程与核心环节实现

4.1 环境准备与解决方案加载

本包基于Visual Studio 2019 Community(免费)和IIS Express(随VS安装)验证,无需独立IIS服务器。操作步骤极简:

  1. 解压资源包到本地目录,如C:\asmx-cors-demo
  2. 双击ajax跨域请求调用webservice接口.sln,VS自动加载解决方案
  3. 检查解决方案资源管理器
    - WebService.asmx:服务入口文件,右键“查看标记”可见CodeBehind="App_Code/server.cs"
    - App_Code/server.cs:核心业务与CORS逻辑
    - Web.configWeb.Debug.config:双配置文件,VS会根据当前配置(Debug/Release)自动选用
    - test.html:前端测试页,位于项目根目录
    - jquery/文件夹:含jquery-1.8.3.min.js,专为IE8优化

  4. 启动调试(F5)
    - VS自动启动IIS Express,分配随机端口(如http://localhost:59234
    - 浏览器自动打开http://localhost:59234/test.html
    - 输入ID,点击按钮,观察#result区域反馈

关键验证点:
- 打开浏览器开发者工具(F12)→ Network标签 → 点击调用按钮
- 查看GetUserInfo请求:
- Request Headers:应有Origin: http://localhost:59234SOAPAction: "http://tempuri.org/GetUserInfo"
- Response Headers:应有Access-Control-Allow-Origin: http://localhost:59234Access-Control-Allow-Credentials: true
- Response:应为标准SOAP XML(现代浏览器)或纯文本XML(IE8/9)

注意:若VS提示“未找到Web服务器”,请右键解决方案 → “属性” → “Web”选项卡 → 确保“使用IIS Express”已勾选,并点击“创建虚拟目录”。

4.2 跨域配置生效验证:三步诊断法

配置不是写完就生效,必须逐层验证。我们用三步法定位问题:

第一步:验证IIS级别CORS头是否注入
用curl命令绕过浏览器,直击服务端:

# 发送OPTIONS预检请求
curl -I -X OPTIONS http://localhost:59234/WebService.asmx/GetUserInfo
# 应返回:Access-Control-Allow-Origin: http://localhost:59234

# 发送POST请求(模拟预检后的实际调用)
curl -H "Origin: http://localhost:59234" \
     -H "Content-Type: text/xml; charset=utf-8" \
     -H "SOAPAction: \"http://tempuri.org/GetUserInfo\"" \
     --data-binary @soap-request.xml \
     http://localhost:59234/WebService.asmx/GetUserInfo
# 应返回200及SOAP响应,且响应头含CORS头

第二步:验证ASP.NET管道是否执行CORS逻辑
server.csGetUserInfo方法开头加断点:

public string GetUserInfo(string id)
{
    System.Diagnostics.Debugger.Break(); // 断点在此
    CorsHelper.SetCorsHeaders(...);
    // ...
}

运行F5,点击调用按钮。若断点命中,说明请求已进入业务方法,CORS头注入逻辑可执行;若未命中,问题在IIS配置层(如Handler未注册)。

第三步:验证前端请求构造是否合规
test.htmlcallWebService()函数中,console.log(soapEnvelope)打印SOAP体,确认:
- <id>标签内容正确(无HTML编码,如&lt;
- SOAPAction头值与.asmx中[WebMethod]Namespace和方法名完全匹配(本包为http://tempuri.org/GetUserInfo
- contentType字符串精确为"text/xml; charset=utf-8"

实操心得:曾遇到客户环境因防火墙过滤SOAPAction头导致400错误。解决方案是在Web.config中添加<httpProtocol><customHeaders><add name="SOAPAction" value="*"/></customHeaders></httpProtocol>,但这违反HTTP规范,仅作最后手段。本包优先确保前端正确发送。

4.3 Credentials(Cookie)支持全流程演示

跨域带Cookie是高频需求,也是最容易出错的环节。本包提供完整演示:

  1. 服务端启用SessionWeb.config中确保<sessionState mode="InProc" timeout="20" />
  2. GetUserInfo中写入Session
    csharp HttpContext.Current.Session["LastCall"] = DateTime.Now.ToString();
  3. 前端调用时启用CredentialsxhrFields: { withCredentials: true }
  4. 验证Cookie传递
    - F12 → Application → Cookies,确认ASP.NET_SessionId已存在
    - 调用后,服务端可读取HttpContext.Current.Session["LastCall"]
    - 若读取为空,检查:
    • Access-Control-Allow-Origin是否为*(必须为具体域名)
    • Access-Control-Allow-Credentials是否为true
    • 前端withCredentials是否为true
    • 浏览器是否启用了“阻止第三方Cookie”(Chrome 80+默认开启,需在chrome://settings/cookies中关闭)

本包test.html中已内置Session验证按钮,点击后调用CheckSession方法,返回Session["LastCall"]值,直观证明Credentials链路畅通。

4.4 双配置文件(Web.config / Web.Debug.config)切换实战

本包的配置分离不是摆设,而是应对真实运维场景:

  • Web.Debug.config:用于开发阶段
  • Access-Control-Allow-Origin设为http://localhost:8080(前端Vue DevServer地址)
  • <compilation debug="true">开启调试
  • <trace enabled="true">记录请求详细信息
  • customHeaders中添加X-Debug-Info头,显示当前配置文件名

  • Web.config:用于生产部署

  • Access-Control-Allow-Origin设为正式域名(如https://app.corp.com
  • <compilation debug="false">关闭调试
  • 移除所有<trace>和调试头
  • customHeaders中添加X-Content-Security-Policy等安全头

切换方法:
- VS中右键解决方案 → “配置管理器” → 将活动解决方案配置从“Debug”改为“Release”
- 重新生成解决方案,VS自动将Web.Debug.config的内容合并到Web.config(通过<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">语法)
- 部署时,只需复制Web.configWebService.asmx等文件,无需Web.Debug.config

提示:本包已预置Transform语法。例如Web.Debug.config中:
xml <add name="Access-Control-Allow-Origin" value="http://localhost:8080" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
当切换为Release时,此行被忽略,Web.config中原始值https://app.corp.com生效。

5. 常见问题与排查技巧实录

5.1 典型问题速查表

问题现象可能原因排查步骤本包解决方案
OPTIONS预检请求返回405 Method Not AllowedIIS未注册.asmx处理器处理OPTIONS1. 检查Web.config<handlers>是否removeadd
2. 在server.cs中加断点,确认OPTIONS请求是否进入方法
Web.config已预置正确handler注册,server.csif (HttpMethod=="OPTIONS")短路处理
POST请求返回400 Bad Request,提示“Missing SOAPAction”前端未发送SOAPAction头,或值不匹配1. F12 Network中检查Request Headers
2. 确认SOAPAction值与.asmx中Namespace+方法名一致
test.htmlheaders.SOACTION已硬编码为"http://tempuri.org/GetUserInfo"server.cs[WebMethod]已声明相同Namespace
跨域成功但Cookie未发送(withCredentials=true无效)Access-Control-Allow-Origin*,或前端未设withCredentials1. 检查响应头Access-Control-Allow-Origin
2. 确认前端xhrFields.withCredentialstrue
CorsHelper.SetCorsHeaders()中强制校验:若allowCredentials==true,则allowOrigin必须为非*值;test.html中已显式设置
IE8/9调用后xdr.responseText为空字符串IE8/9的XDR对响应格式极其敏感,要求纯文本且无BOM1. 在server.csif (isIE89)分支,Response.ContentType设为text/plain
2. 响应XML字符串前移除BOM
server.cs中已用Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(xml))确保无BOM;test.htmlxdr.onload已内置replace(/^\uFEFF/, '')清理
部署到IIS后跨域失效,本地IIS Express正常IIS应用程序池.NET版本不匹配,或未启用ASP.NET功能1. IIS管理器 → 应用程序池 → .NET CLR版本设为v4.0
2. 控制面板 → 程序和功能 → 启用或关闭Windows功能 → 确保ASP.NET 4.8已勾选
本包sln文件已设目标框架为.NET Framework 4.7.2,部署文档强调应用池版本匹配

5.2 独家避坑技巧

技巧1:SOAP Envelope自动缩进导致IE8/9解析失败
ASP.NET默认序列化SOAP时会添加换行和缩进,而IE8/9的XDR要求响应是紧凑的单行XML。本包在server.cs中对IE8/9分支采用字符串拼接而非XmlSerializer,彻底规避此问题。若你需扩展其他方法,牢记:IE8/9分支永远用字符串拼接,永不调用XmlSerializerResponse.Write()带缩进的XML

技巧2:WSDL地址跨域被拦截
浏览器访问http://service.example.com/WebService.asmx?wsdl时,会触发跨域检查。虽然WSDL本身不参与AJAX调用,但客户常以此验证服务可用性。解决方案是在Web.config中为.asmx?wsdl路径单独配置CORS:

<location path="WebService.asmx">
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</location>

本包已内置此配置,确保?wsdl地址可被任意域名访问。

技巧3:IIS日志中看不到OPTIONS请求
IIS默认不记录OPTIONS请求,导致排查预检问题无日志可查。启用方法:IIS管理器 → 服务器节点 → 日志 → 选择“W3C”格式 → 点击“选择字段” → 勾选cs-methodsc-status。本包部署文档已注明此步骤。

技巧4:jQuery 1.8.3的IE8内存泄漏修复
原版jquery-1.8.3.min.js在IE8下频繁AJAX会导致内存泄漏。本包jquery/目录中文件已打补丁:在jQuery.ajaxTransport中添加if (window.XDomainRequest) { xdr = null; }释放引用。此补丁经3个月压力测试验证,内存占用稳定。

5.3 性能与安全加固建议

本包聚焦功能可用,但生产环境需加固:

  • 性能
  • .asmx默认启用SOAP日志(<webServices><diagnostics><traceEnabled>true</traceEnabled></diagnostics></webServices>),上线前务必设为false,否则每个请求写磁盘,I/O飙升。
  • server.cs中数据库查询应加缓存,本包为演示省略,实际项目建议用HttpContext.Cache.Insert()缓存用户信息5分钟。

  • 安全

  • Access-Control-Allow-Origin不应长期设为*,本包Web.config中已设为具体域名,部署时需替换为客户正式域名。
  • Access-Control-Allow-Headers中移除Authorization(除非真需Bearer Token),避免泄露认证信息。
  • Web.config中添加<security><requestFiltering><denyUrlSequences><add sequence=".." /><add sequence="web.config" /></denyUrlSequences></requestFiltering></security>,防配置文件下载。

最后分享一个小技巧:本包test.html中所有AJAX调用都封装在callWebService()函数内。若客户要求“禁止跨域”,只需注释掉xhrFields.withCredentialsheaders.SOAPAction,并把URL改为同域地址(如/WebService.asmx/GetUserInfo),其余代码零修改即可退化为同域调用——这就是设计时预留的“安全退路”。

这个包没有魔法,只有对二十万行遗留代码的敬畏,和对每一个IE8用户耐心的尊重。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接可用的ASP.NET WebService.asmx跨域AJAX调用环境,包含已启用CORS的WebService.asmx文件、预设Access-Control-Allow-Origin等响应头的Web.config和Web.Debug.config双配置文件、基于jQuery的test.html前端调用页面,以及后台逻辑server.cs(位于App_Code目录)。项目使用标准.NET Framework开发,兼容IE8及以上浏览器,无需JSONP或反向代理即可完成跨域请求。解决方案文件ajax跨域请求调用webservice接口.sln可直接在Visual Studio中加载运行,前后端分离清晰:前台调用通过$.ajax封装XMLHttpRequest,后台服务返回SOAP或原始XML响应。所有跨域相关HTTP头(如Allow-Headers、Allow-Methods、Credentials支持)均已在服务端显式配置,适用于老旧内网系统对接、遗留平台集成或教学演示场景。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
内容概要:本文档系统性地介绍了2024年最新提出的两种智能优化算法——青蒿素优化算法霜冰优化算法(RIME)的原理、现方法及其性能对比分析,并提供了完整的Matlab代码现。文档不仅聚焦于核心算法的仿真验证,还整合了大量前沿科研资源,涵盖微电网优化、风电功率预测、无人机三维路径规划、电动汽车调度、图像融合、负荷预测、通信信号处理、电力系统故障恢复等多个高价值应用场景。所有案例均基于Matlab/Simulink平台进行建模仿真,强调算法在复杂工程系统中的际应用能力,旨在为科研人员提供一套从理论到代码再到应用的完整复现体系。; 适合人群:具备一定编程基础和科研背景的研究生、高校教师及工程技术人员,尤其适合从事智能优化算法研究、新能源系统优化、自动化控制、电力系统调度、无人机导航路径规划等相关领的研究人员。; 使用场景及目标:①用于高水平学术论文的复现创新性研究,提升科研效率成果产出;②应用于复杂工程系统的建模仿真智能优化设计,如多能互补系统调度、无人机避障路径规划、微电网能量管理等;③作为智能优化算法的教学学习资料,深入理解现代元启发式算法的设计思想现机制。; 阅读建议:建议读者结合文档中提供的Matlab代码Simulink仿真模型,按照目录结构循序渐进地学习践,优先选择自身研究方向契合的案例进行代码复现,重点关注算法参数设置、收敛曲线分析多算法对比验部分,以全面提升算法应用科研创新能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值