Android的消费Dynamics NAV的SOAP Web服务 [英] Android Consuming Dynamics NAV SOAP Web Service
问题描述
我想建立一个对我的Android应用程序,以动态导航2013 R2与SOAP的Web服务连接。
I am trying to establish a connection for my android app to dynamics nav 2013 r2 with SOAP web service.
从来就尝试在不同的论坛发现了几个教程,但未能建立连接。
I´ve tried several tutorials found in different forums but failed to establish the connection.
NTLM是在NST启用。
NTLM is enabled on the NST.
这是在Java类从来就试过:
This is are the java classes I´ve tried:
AndroidManifest.xml中
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="as.myapplication" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
MainActivity.java
MainActivity.java
package as.myapplication;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//ohneNTLM();
WithNTLM();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void WithNTLM()
{
String namespace = "urn:microsoft-dynamics-schemas/page/customercard";
String url = "http://172.16.1.15:7047/DynamicsNAV71/WS/CRONUS/Page/CustomerCard";
String soap_action = "urn:microsoft-dynamics-schemas/page/customercard:Read";
String method_name = "Read";
String great;
try
{
SoapObject request = new SoapObject(namespace, method_name);
String custID = "323111";
PropertyInfo custProp = new PropertyInfo();
custProp.setName("No");
custProp.setValue(custID);
custProp.setType(String.class);
request.addProperty(custProp);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
// HttpTransportSE transport = new HttpTransportSE(url);
NtlmTransport ntlm = new NtlmTransport();
ntlm.setCredentials(url, "USER", "PW", "DOMAIN","EMPTY??");
ntlm.call(soap_action, envelope); // Receive Error here!
SoapObject result = (SoapObject) envelope.getResponse();
great = result.toString();
}
catch (Exception e)
{
e.printStackTrace();
great = e.toString();
System.out.println(great);
Toast.makeText(this, great, Toast.LENGTH_LONG).show();
}
}
}
JCIFSEngine.java
JCIFSEngine.java
package as.myapplication;
import jcifs.ntlmssp.NtlmFlags;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;
import jcifs.util.Base64;
import org.apache.http.impl.auth.NTLMEngine;
import org.apache.http.impl.auth.NTLMEngineException;
import java.io.IOException;
/**
* Class taken from http://hc.apache.org/httpcomponents-client-ga/ntlm.html
*/
public final class JCIFSEngine implements NTLMEngine {
private static final int TYPE_1_FLAGS =
NtlmFlags.NTLMSSP_NEGOTIATE_56 |
NtlmFlags.NTLMSSP_NEGOTIATE_128 |
NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 |
NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NtlmFlags.NTLMSSP_REQUEST_TARGET;
public String generateType1Msg(final String domain, final String workstation)
throws NTLMEngineException {
final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
return Base64.encode(type1Message.toByteArray());
}
public String generateType3Msg(final String username, final String password,
final String domain, final String workstation, final String challenge)
throws NTLMEngineException {
Type2Message type2Message;
try {
type2Message = new Type2Message(Base64.decode(challenge));
} catch (final IOException exception) {
throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
}
final int type2Flags = type2Message.getFlags();
final int type3Flags = type2Flags
& (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
final Type3Message type3Message = new Type3Message(type2Message, password, domain,
username, workstation, type3Flags);
return Base64.encode(type3Message.toByteArray());
}
}
NTLMTransport.java
NTLMTransport.java
package as.myapplication;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeFactory;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.auth.NTLMScheme;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.ksoap2.HeaderProperty;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.ServiceConnection;
import org.ksoap2.transport.Transport;
import org.xmlpull.v1.XmlPullParserException;
/**
* A transport to be used with NTLM.
*
* Inspired by http://hc.apache.org/httpcomponents-client-ga/ntlm.html
* @author Lian Hwang lian_hwang@hotmail.com
* @author Manfred Moser <manfred@simpligity.com>
*/
public class NtlmTransport extends Transport {
static final String ENCODING = "utf-8";
private final DefaultHttpClient client = new DefaultHttpClient();
private final HttpContext localContext = new BasicHttpContext();
private String urlString;
private String user;
private String password;
private String ntDomain;
private String ntWorkstation;
public void setCredentials(String url, String user, String password,
String domain, String workStation) {
this.urlString = url;
this.user = user;
this.password = password;
this.ntDomain = domain;
this.ntWorkstation = workStation;
}
public List call(String targetNamespace, SoapEnvelope envelope, List headers)
throws IOException, XmlPullParserException {
return call(targetNamespace, envelope, headers, null);
}
public List call(String soapAction, SoapEnvelope envelope, List headers, File outputFile)
throws IOException, XmlPullParserException {
if (outputFile != null) {
// implemented in HttpTransportSE if you are willing to port..
throw new RuntimeException("Writing to file not supported");
}
HttpResponse resp = null;
setupNtlm(urlString, user, password);
try {
// URL url = new URL(urlString);
HttpPost httppost = new HttpPost(urlString);
// UrlEncodedFormEntity byteArrayEntity =
// new UrlEncodedFormEntity(new ArrayList<BasicNameValuePair>());
// httppost.setEntity(byteArrayEntity);
setHeaders(soapAction, envelope, httppost, headers);
resp = client.execute(httppost, localContext);
HttpEntity respEntity = resp.getEntity();
InputStream is = respEntity.getContent();
parseResponse(envelope, is);
} catch (Exception ex) {
// ex.printStackTrace();
}
if (resp != null) {
return Arrays.asList(resp.getAllHeaders());
} else {
return null;
}
}
private void setHeaders(String soapAction, SoapEnvelope envelope, HttpPost httppost, List headers) {
byte[] requestData = null;
try {
requestData = createRequestData(envelope);
} catch (IOException iOException) {
}
ByteArrayEntity byteArrayEntity = new ByteArrayEntity(requestData);
httppost.setEntity(byteArrayEntity);
httppost.addHeader("User-Agent", org.ksoap2.transport.Transport.USER_AGENT);
// SOAPAction is not a valid header for VER12 so do not add
// it
// @see "http://code.google.com/p/ksoap2-android/issues/detail?id=67
if (envelope.version != SoapSerializationEnvelope.VER12) {
httppost.addHeader("SOAPAction", soapAction);
}
if (envelope.version == SoapSerializationEnvelope.VER12) {
httppost.addHeader("Content-Type", Transport.CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8);
} else {
httppost.addHeader("Content-Type", Transport.CONTENT_TYPE_XML_CHARSET_UTF_8);
}
// Pass the headers provided by the user along with the call
if (headers != null) {
for (int i = 0; i < headers.size(); i++) {
HeaderProperty hp = (HeaderProperty) headers.get(i);
httppost.addHeader(hp.getKey(), hp.getValue());
}
}
}
// Try to execute a cheap method first. This will trigger NTLM authentication
public void setupNtlm(String dummyUrl, String userId, String password) {
try {
((AbstractHttpClient) client).getAuthSchemes().register("ntlm", new NTLMSchemeFactory());
NTCredentials creds = new NTCredentials(userId, password, ntWorkstation, ntDomain);
client.getCredentialsProvider().setCredentials(AuthScope.ANY, creds);
HttpGet httpget = new HttpGet(dummyUrl);
HttpResponse response1 = client.execute(httpget, localContext);
HttpEntity entity1 = response1.getEntity();
Header[] hArray = response1.getAllHeaders();
int size = hArray.length;
for (int i = 0; i < size; i ++) {
Header h = hArray[i];
if (h.getName().equals("WWW-Authenticate")) {
entity1.consumeContent();
throw new Exception("Failed Authentication");
}
}
entity1.consumeContent();
} catch (Exception ex) {
// swallow
}
}
//NTLM Scheme factory
private class NTLMSchemeFactory implements AuthSchemeFactory {
public AuthScheme newInstance(final HttpParams params) {
// see http://www.robertkuzma.com/2011/07/
// manipulating-sharepoint-list-items-with-android-java-and-ntlm-authentication/
return new NTLMScheme(new JCIFSEngine());
}
}
public ServiceConnection getServiceConnection() throws IOException
{
throw new IOException("Not using ServiceConnection in transport");
}
public String getHost() {
String retVal = null;
try {
retVal = new URL(url).getHost();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return retVal;
}
public int getPort() {
int retVal = -1;
try {
retVal = new URL(url).getPort();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return retVal;
}
public String getPath() {
String retVal = null;
try {
retVal = new URL(url).getPath();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return retVal;
}
}
运行此应用程序后,我得到了以下信息/错误:
After running this application I got following message/error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.ksoap2.serialization.SoapObject.toString()' on a null object reference
当我尝试使用Android浏览器打开网页的Web服务的接入工作。
The access to the web service works when I try to open the page with the android browser.
任何帮助将是AP preciated。
Any help would be appreciated.
推荐答案
好吧,我找到了一个解决方案。这里是谁需要连接到一个动态NAV(Navision的Web服务)。
Okay I found an solution. Here is the solution for everyone who needs to connect to an Dynamics NAV (Navision Web Service).
第1步 - >下载ksoap2-Android的组装XX.X-JAR与 - dependencies.jar的最后一个版本,它集成到应用中。
Step 1 --> Download the last version of ksoap2-android-assembly-xx.x-jar-with-dependencies.jar and integrate it in your app.
第2步 - >创建这2 Java类的NTLM身份验证
Step 2 --> Create this 2 Java classes for the NTLM-Authentication
JCIFSEngine.java
JCIFSEngine.java
import jcifs.ntlmssp.NtlmFlags;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;
import jcifs.util.Base64;
import org.apache.http.impl.auth.NTLMEngine;
import org.apache.http.impl.auth.NTLMEngineException;
import java.io.IOException;
/**
* Class taken from http://hc.apache.org/httpcomponents-client-ga/ntlm.html
*/
public final class JCIFSEngine implements NTLMEngine {
private static final int TYPE_1_FLAGS =
NtlmFlags.NTLMSSP_NEGOTIATE_56 |
NtlmFlags.NTLMSSP_NEGOTIATE_128 |
NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 |
NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NtlmFlags.NTLMSSP_REQUEST_TARGET;
public String generateType1Msg(final String domain, final String workstation)
throws NTLMEngineException {
final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
return Base64.encode(type1Message.toByteArray());
}
public String generateType3Msg(final String username, final String password,
final String domain, final String workstation, final String challenge)
throws NTLMEngineException {
Type2Message type2Message;
try {
type2Message = new Type2Message(Base64.decode(challenge));
} catch (final IOException exception) {
throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
}
final int type2Flags = type2Message.getFlags();
final int type3Flags = type2Flags
& (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
final Type3Message type3Message = new Type3Message(type2Message, password, domain,
username, workstation, type3Flags);
return Base64.encode(type3Message.toByteArray());
}
}
NTLMTransport.java
NTLMTransport.java
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeFactory;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.auth.NTLMScheme;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.ksoap2.HeaderProperty;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.ServiceConnection;
import org.ksoap2.transport.Transport;
import org.xmlpull.v1.XmlPullParserException;
/**
* A transport to be used with NTLM.
*
* Inspired by http://hc.apache.org/httpcomponents-client-ga/ntlm.html
* @author Lian Hwang lian_hwang@hotmail.com
* @author Manfred Moser <manfred@simpligity.com>
*/
public class NtlmTransport extends Transport {
static final String ENCODING = "utf-8";
private final DefaultHttpClient client = new DefaultHttpClient();
private final HttpContext localContext = new BasicHttpContext();
private String urlString;
private String user;
private String password;
private String ntDomain;
private String ntWorkstation;
public void setCredentials(String url, String user, String password,
String domain, String workStation) {
this.urlString = url;
this.user = user;
this.password = password;
this.ntDomain = domain;
this.ntWorkstation = workStation;
}
public List call(String targetNamespace, SoapEnvelope envelope, List headers)
throws IOException, XmlPullParserException {
return call(targetNamespace, envelope, headers, null);
}
public List call(String soapAction, SoapEnvelope envelope, List headers, File outputFile)
throws IOException, XmlPullParserException {
if (outputFile != null) {
// implemented in HttpTransportSE if you are willing to port..
throw new RuntimeException("Writing to file not supported");
}
HttpResponse resp = null;
setupNtlm(urlString, user, password);
try {
// URL url = new URL(urlString);
HttpPost httppost = new HttpPost(urlString);
// UrlEncodedFormEntity byteArrayEntity =
// new UrlEncodedFormEntity(new ArrayList<BasicNameValuePair>());
// httppost.setEntity(byteArrayEntity);
setHeaders(soapAction, envelope, httppost, headers);
resp = client.execute(httppost, localContext);
HttpEntity respEntity = resp.getEntity();
InputStream is = respEntity.getContent();
parseResponse(envelope, is);
} catch (Exception ex) {
// ex.printStackTrace();
}
if (resp != null) {
return Arrays.asList(resp.getAllHeaders());
} else {
return null;
}
}
private void setHeaders(String soapAction, SoapEnvelope envelope, HttpPost httppost, List headers) {
byte[] requestData = null;
try {
requestData = createRequestData(envelope);
} catch (IOException iOException) {
}
ByteArrayEntity byteArrayEntity = new ByteArrayEntity(requestData);
httppost.setEntity(byteArrayEntity);
httppost.addHeader("User-Agent", org.ksoap2.transport.Transport.USER_AGENT);
// SOAPAction is not a valid header for VER12 so do not add
// it
// @see "http://code.google.com/p/ksoap2-android/issues/detail?id=67
if (envelope.version != SoapSerializationEnvelope.VER12) {
httppost.addHeader("SOAPAction", soapAction);
}
if (envelope.version == SoapSerializationEnvelope.VER12) {
httppost.addHeader("Content-Type", Transport.CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8);
} else {
httppost.addHeader("Content-Type", Transport.CONTENT_TYPE_XML_CHARSET_UTF_8);
}
// Pass the headers provided by the user along with the call
if (headers != null) {
for (int i = 0; i < headers.size(); i++) {
HeaderProperty hp = (HeaderProperty) headers.get(i);
httppost.addHeader(hp.getKey(), hp.getValue());
}
}
}
// Try to execute a cheap method first. This will trigger NTLM authentication
public void setupNtlm(String dummyUrl, String userId, String password) {
try {
((AbstractHttpClient) client).getAuthSchemes().register("ntlm", new NTLMSchemeFactory());
NTCredentials creds = new NTCredentials(userId, password, ntWorkstation, ntDomain);
client.getCredentialsProvider().setCredentials(AuthScope.ANY, creds);
HttpGet httpget = new HttpGet(dummyUrl);
HttpResponse response1 = client.execute(httpget, localContext);
HttpEntity entity1 = response1.getEntity();
Header[] hArray = response1.getAllHeaders();
int size = hArray.length;
for (int i = 0; i < size; i ++) {
Header h = hArray[i];
if (h.getName().equals("WWW-Authenticate")) {
entity1.consumeContent();
throw new Exception("Failed Authentication");
}
}
entity1.consumeContent();
} catch (Exception ex) {
// swallow
}
}
//NTLM Scheme factory
private class NTLMSchemeFactory implements AuthSchemeFactory {
public AuthScheme newInstance(final HttpParams params) {
// see http://www.robertkuzma.com/2011/07/
// manipulating-sharepoint-list-items-with-android-java-and-ntlm-authentication/
return new NTLMScheme(new JCIFSEngine());
}
}
public ServiceConnection getServiceConnection() throws IOException
{
throw new IOException("Not using ServiceConnection in transport");
}
public String getHost() {
String retVal = null;
try {
retVal = new URL(url).getHost();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return retVal;
}
public int getPort() {
int retVal = -1;
try {
retVal = new URL(url).getPort();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return retVal;
}
public String getPath() {
String retVal = null;
try {
retVal = new URL(url).getPath();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return retVal;
}
}
第3步 - >对于调用WebService的请把这个code到的AsyncTask
String namespace = "urn:microsoft-dynamics-schemas/page/orderheader";
String url = "http://172.16.1.15:7047/DynamicsNAV71/WS/BILLY%20Deutschland/Page/OrderHeader";
String soap_action = "urn:microsoft-dynamics-schemas/page/orderheader:ReadMultiple";
String method_name = "ReadMultiple";
String great;
try {
SoapObject request = new SoapObject(namespace, method_name);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
NtlmTransport ntlm = new NtlmTransport();
ntlm.debug = true;
ntlm.setCredentials(url, "myuser", "mypasswort", "billy", "");
ntlm.call(soap_action, envelope); // Receive Error here!
SoapObject result = (SoapObject) envelope.getResponse();
great = result.toString();
System.out.println(great);
} catch (Exception e) {
e.printStackTrace();
great = e.toString();
System.out.println(great);
}
这篇关于Android的消费Dynamics NAV的SOAP Web服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!