C#调⽤webservicewsdl⽂件
经常遇到C#调⽤webservice的情况,通常来说如果webservice是⽤vs+c#来开发的,问题⼀般来说不⼤,直接web引⽤,然后调⽤就OK了。流程如下:
下⾯就是进⾏调⽤,就这么简单。
但如果webservice是⽤JAVA或者其它语⾔或者其它⼯具⽣成的话,使⽤vs+c#来调⽤就经常遇到问题;就是使⽤上⾯的⽅法显得很不好使,经常是使⽤SOAP UI调⽤没有问题,但使⽤上⾯的⽅法却调⽤报错,经常是500的错误。当你联系webservice提供商时通常会说SOAP UI都能调⽤得到,你们⽤代码为啥⼦调⽤不到,问题出在你们的调⽤⽅法上。
在我们向其它公司提供webservice的时候,经常也会出现这样的问题,以前我们⼀直都以为SOAP UI能够调⽤,那么代码也就⼀定能够调⽤得通,但经过实践,我们⾃⼰写DEMO调⽤⾃⼰的webservice时才发现,并不是别⼈的调⽤代码写的有问题,因为我们⾃⼰也⽆法将⾃⼰写的webservice调⽤得通,或者说没有到正确的⽅法调⽤得通。这时我们就要思考是否是SOAP UI能够调⽤得通的webservice就代码⼀定调⽤没有问题呢?或者说SOAP UI调⽤webservice和代码调⽤webservice的原理区别到底在哪⾥呢?
总结⼀下:
(1)SOAP UI能够调⽤成功,代码不⼀定能够调⽤成功,代码调⽤成功并且得到返回结果的前提是webservice可以按标准返回结果,但SOAP UI是只要按信封返回就可以收到结果⽽不管结果是否标准;
(2)如果webservice的header有⽤户名和密码的校验,使⽤SOAP UI可以调⽤成功并且得到返回结果,但使⽤上⾯web引⽤的⽅式却不⾏。对于这种情况,有以下⽅法可以调⽤成功:
重复上⾯web引⽤的⽅式,另外引⽤Microsoft.Web.Services3(这个DLL可以CSDN上⾯下载)。
⼿⼯修改Reference.cs
以下是调⽤⽅法,重点在传⼊⽤户名和密码,修改继承类的重点也在于此;
引⽤完成,调⽤就会成功了,结果如下:
从上⾯可以看出web引⽤真的是简单粗暴,基本不⽤写⼏句代码就可以搞定绝⼤部分的webservice调⽤。
但是web引⽤却有在现实开发中常遇到的缺点:
(1)需要开发环境可以访问到的wsdl地址;
(2)如果webservice有变化需要更新web引⽤;
那有没有其它⽅法呢?答案是有的。⼀种是直接封装信封,使⽤WebRequest,代码如下:
private void button2_Click(object sender, EventArgs e)
{
StringBuilder soap = new StringBuilder();
soap.Append("<soap:Envelope xmlns:soap=\"/2003/05/soap-envelope\" xmlns:zls=\"www.zlsoft\">");
soap.Append("<soap:Header>");
soap.Append("<wsse:Security soap:mustUnderstand=\"true\" xmlns:wsse=\"/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"/wss/2004/01/oasis-200401-wss- soap.Append("<wsse:UsernameToken wsu:Id=\"UsernameToken-4\">");
soap.Append("<wsse:Username>hjq</wsse:Username>");//⽤户名
soap.Append("<wsse:Password Type=\"/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">123</wsse:Password>");//⼝令
soap.Append("<wsse:Nonce EncodingType=\"/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">gC6dDGKjxo0IaRc5KpQU3w==</wsse:Nonce>");
soap.Append("<wsu:Created>2017-11-01T05:11:22.805Z</wsu:Created>");
soap.Append("</wsse:UsernameToken>");
soap.Append("</wsse:Security>");
soap.Append("</soap:Header>");
soap.Append("<soap:Body>");
soap.Append("<zls:getUserInfo>");
soap.Append("<zls:appsys_id>trwetre</zls:appsys_id>");
soap.Append("<zls:appsys_token>sdafdsf</zls:appsys_token>");
soap.Append("<zls:sid>fdsafds</zls:sid>");
soap.Append("</zls:getUserInfo>");
soap.Append("</soap:Body>");
soap.Append("</soap:Envelope>");
string url = "127.0.0.1:6998/services/HIPService?wsdl";
var result = GetSOAPReSource(url, soap.ToString());
}
public static string GetSOAPReSource(string url, string datastr)
{
try
{
//request
Uri uri = new Uri(url);
WebRequest webRequest = WebRequest.Create(uri);
webRequest.ContentType = "application/soap+xml; charset=utf-8";
webRequest.Method = "POST";
using (Stream requestStream = webRequest.GetRequestStream())
{
byte[] paramBytes = Encoding.UTF8.GetBytes(datastr.ToString());
requestStream.Write(paramBytes, 0, paramBytes.Length);
}
//response
WebResponse webResponse = webRequest.GetResponse();
using (StreamReader myStreamReader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8))
{
string result = "";
return result = myStreamReader.ReadToEnd();
}
}
catch (Exception ex)
{
throw ex;
}
}
但是上述⽅式还是存在问题,如果webservice需要校验header⾥⾯的⽤户名与密码时,虽然信封中封装了⽤户名和密码,但是⼀样调⽤不成功,同样的信封内容,使⽤SOAP UI
却能成功。可见代码调⽤和SOAP UI调⽤还是有⼀定区别的。
另外⼀种⽅式是使⽤动态编译:
Uri uri = new Uri(txt_url.Text);
WebRequest webRequest = WebRequest.Create(uri);
webRequest.Credentials = new NetworkCredential("hjq", "123");
System.IO.Stream requestStream = webRequest.GetResponse().GetResponseStream();
// Get a WSDL file describing a service
ServiceDescription sd = ServiceDescription.Read(requestStream);
string sdName = sd.Services[0].Name;
// Add in tree view
// Initialize a service description servImport
ServiceDescriptionImporter servImport = new ServiceDescriptionImporter();
servImport.AddServiceDescription(sd, String.Empty, String.Empty);
servImport.ProtocolName = "Soap";
servImport.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;
CodeNamespace nameSpace = new CodeNamespace();
CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
codeCompileUnit.Namespaces.Add(nameSpace);
// Set Warnings
ServiceDescriptionImportWarnings warnings = servImport.Import(nameSpace, codeCompileUnit);
if (warnings == 0)
{
StringWriter stringWriter = new StringWriter(System.Globalization.CultureInfo.CurrentCulture);
Microsoft.CSharp.CSharpCodeProvider prov = new Microsoft.CSharp.CSharpCodeProvider();
prov.GenerateCodeFromNamespace(nameSpace, stringWriter, new CodeGeneratorOptions());
// Compile the assembly with the appropriate references
//string[] assemblyReferences = new string[2] { "System.Web.Services.dll", "System.Xml.dll" };
string[] assemblyReferences = new string[3] {"System.dll", "System.dll", "System.dll" };
CompilerParameters param = new CompilerParameters(assemblyReferences);
param.GenerateExecutable = false;
param.GenerateInMemory = true;
param.TreatWarningsAsErrors = false;
param.WarningLevel = 4;
CompilerResults results = new CompilerResults(new TempFileCollection());
results = prov.CompileAssemblyFromDom(param, codeCompileUnit);
Assembly assembly = results.CompiledAssembly;
Type service = assembly.GetType(sdName);
MethodInfo[] methodInfo = service.GetMethods();
foreach (MethodInfo t in methodInfo)
{
if (t.Name == "Discover")
break;
if (t.Name == cbMethods.Text)
{
/
/MessageBox.Show(t.ToString());
//Invoke Method
Object obj = Activator.CreateInstance(service);
webservice用户名密码调用Object response = t.Invoke(obj, new object[] { "1","2","3"});
MessageBox.Show("Result = " + response.ToString());
break;
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
从这种⽅式可以看出,⾸先是访问wsdl的地址取回webservice的结构,然后采取动态编译⽣成本地程序集的⽅式去调⽤webservice,和web引⽤⾃动⽣成Reference.cs的原理类似;但是明显这种⽅式也存在问题,如果webservice的header中需要校验⽤户名与密码怎么处理呢?
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论