C#.Net不添加服务引用调用asmx服务遇403禁止错误排查
我正在实现无需添加服务引用即可调用asmx WebService方法的功能,该asmx未提供WSDL。通过Fiddler和Postman均可正常访问该服务,但使用C#代码调用时却抛出403禁止异常。使用环境为VS2022控制台应用(.Net Framework 4.6.2)。
我的C#代码如下:
internal class Class3 { string _soapEnvelope = @"<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></soap:Body></soap:Envelope>"; private string CreateSoapEnvelope() { Dictionary<string, string> Params = new Dictionary<string, string>(); Params.Add("aId", "11"); // Add parameterName & Value to dictionary Params.Add("cId", "22"); Params.Add("lId", "20"); string MethodCall = "<" + "GetSettings" + @" xmlns=""http://tempuri.org/"">"; string StrParameters = string.Empty; foreach (var param in Params) { StrParameters += string.Format("<{0}>{1}</{0}>", param.Key, param.Value); } MethodCall = MethodCall + StrParameters + "</" + "GetSettings" + ">"; StringBuilder sb = new StringBuilder(_soapEnvelope); sb.Insert(sb.ToString().IndexOf("</soap:Body>"), MethodCall); return sb.ToString(); } private HttpWebRequest CreateWebRequest() { HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("sample.asmx"); webRequest.Headers.Add("SOAPAction", "\"http://tempuri.org/" + "GetSettings" + "\""); webRequest.Headers.Add("To", "sample.asmx"); webRequest.Credentials = CredentialCache.DefaultCredentials; webRequest.ContentType = "application/x-www-form-urlencoded"; webRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"; webRequest.Host = "sample.com"; webRequest.Headers.Set(HttpRequestHeader.CacheControl, "max-age=0"); webRequest.Headers.Add("Upgrade-Insecure-Requests: 1"); webRequest.Headers.Add("Origin", "null"); webRequest.Headers.Add("Accept-Encoding", "gzip, deflate"); webRequest.Headers.Add("Accept-Language", "en-US,en;q=0.9,zh-CN;q=0.8,zh-TW;q=0.7,zh;q=0.6"); webRequest.Method = "POST"; return webRequest; } public string InvokeService() { try { WebResponse response = null; string strResponse = ""; //Create the request HttpWebRequest req = this.CreateWebRequest(); //write the soap envelope to request stream using (Stream stm = req.GetRequestStream()) { using (StreamWriter stmw = new StreamWriter(stm)) { stmw.Write(this.CreateSoapEnvelope()); } } //get the response from the web service response = req.GetResponse(); Stream str = response.GetResponseStream(); StreamReader sr = new StreamReader(str); strResponse = sr.ReadToEnd(); return HttpUtility.HtmlDecode(strResponse); } catch (Exception ex) { return ""; } } } internal class Program { static void Main(string[] args) { new Class3().InvokeService(); } }
尝试添加以下请求头后问题仍未解决:
webRequest.ContentLength = 600000; webRequest.ProtocolVersion = HttpVersion.Version11; webRequest.KeepAlive = false; webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36";
以下是代码中存在的核心问题及修正方案:
1. ContentType 错误
ASMX SOAP请求的ContentType必须为 text/xml; charset=utf-8(SOAP 1.1)或application/soap+xml; charset=utf-8(SOAP 1.2),你当前设置的application/x-www-form-urlencoded是表单提交类型,服务端无法识别SOAP XML结构,这是导致403的核心原因之一。
修正:
webRequest.ContentType = "text/xml; charset=utf-8";
2. SOAP信封转义错误
你的_soapEnvelope中使用了<和>转义标签,但直接写入请求流时应该使用原始XML标签,转义后的内容会被服务端当作纯文本而非XML结构,导致SOAP格式无效。
修正:
string _soapEnvelope = @"<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></soap:Body></soap:Envelope>";
同时修正CreateSoapEnvelope中的插入位置:
sb.Insert(sb.ToString().IndexOf("</soap:Body>"), MethodCall);
3. 不必要的请求头干扰
移除服务端不需要的请求头,避免触发校验逻辑:
- 移除
Upgrade-Insecure-Requests: 1:该头用于HTTP升级HTTPS,SOAP服务无需此设置 - 移除
Origin: null:控制台应用通常不需要设置Origin,若Postman中没有该头则直接删除;若服务端要求Origin,应设置为Postman中使用的有效值 - 简化
Accept头:仅保留服务端支持的类型,比如application/xml, text/xml
4. ContentLength设置错误
硬编码ContentLength = 600000会导致请求内容长度与实际不符,服务端可能拒绝请求。应计算SOAP信封的实际字节长度:
string soapContent = this.CreateSoapEnvelope(); byte[] contentBytes = Encoding.UTF8.GetBytes(soapContent); webRequest.ContentLength = contentBytes.Length; // 写入流时改用字节数组,避免编码问题 using (Stream stm = req.GetRequestStream()) { stm.Write(contentBytes, 0, contentBytes.Length); }
5. 请求URL完整性
确保WebRequest.Create的参数是完整的HTTP/HTTPS URL,比如https://sample.com/sample.asmx,而非仅sample.asmx。
6. SOAPAction格式校验
确认SOAPAction的值与Postman中使用的完全一致,注意引号和路径是否正确,比如"http://tempuri.org/GetSettings"是否符合服务端要求。
修正后的完整CreateWebRequest方法示例
private HttpWebRequest CreateWebRequest() { // 替换为完整的服务URL HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("https://sample.com/sample.asmx"); webRequest.Headers.Add("SOAPAction", "\"http://tempuri.org/GetSettings\""); webRequest.Credentials = CredentialCache.DefaultCredentials; // 正确设置SOAP ContentType webRequest.ContentType = "text/xml; charset=utf-8"; // 简化Accept头 webRequest.Accept = "application/xml, text/xml"; webRequest.Host = "sample.com"; webRequest.Headers.Add("Accept-Encoding", "gzip, deflate"); webRequest.Headers.Add("Accept-Language", "en-US,en;q=0.9,zh-CN;q=0.8,zh-TW;q=0.7,zh;q=0.6"); // 添加UserAgent匹配Postman webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"; webRequest.Method = "POST"; webRequest.ProtocolVersion = HttpVersion.Version11; return webRequest; }
额外建议
- 用Fiddler捕获Postman的请求和你的代码请求,对比两者的所有请求头、请求体,找出差异点
- 在catch块中捕获具体的WebException,获取服务端返回的详细错误信息,比如:
catch (WebException ex) { if (ex.Response is HttpWebResponse response) { using (var reader = new StreamReader(response.GetResponseStream())) { string error = reader.ReadToEnd(); // 输出错误信息用于排查 Console.WriteLine(error); } } return ""; }
内容的提问来源于stack exchange,提问作者Abhijit

