无法在Java中使用Apache Olingo访问Dynamics 365 Business Central



我正在尝试使用Apache Olingo客户端通过REST api和OAuth2连接Dynamics 365 Business Central,我得到以下错误:

Exception in thread "main" org.apache.olingo.client.core.http.OAuth2Exception: java.lang.IllegalStateException: Target host is null
at com.plmc.AzureADOAuth2HttpClientFactoryForDBC.init(AzureADOAuth2HttpClientFactoryForDBC.java:149)
at org.apache.olingo.client.core.http.AbstractOAuth2HttpClientFactory.create(AbstractOAuth2HttpClientFactory.java:78)
at org.apache.olingo.client.core.communication.request.AbstractODataRequest.getHttpClient(AbstractODataRequest.java:363)
at org.apache.olingo.client.core.communication.request.AbstractODataRequest.<init>(AbstractODataRequest.java:105)
at org.apache.olingo.client.core.communication.request.AbstractODataBasicRequest.<init>(AbstractODataBasicRequest.java:57)
at org.apache.olingo.client.core.communication.request.retrieve.AbstractODataRetrieveRequest.<init>(AbstractODataRetrieveRequest.java:47)
at org.apache.olingo.client.core.communication.request.retrieve.ODataEntitySetIteratorRequestImpl.<init>(ODataEntitySetIteratorRequestImpl.java:49)
at org.apache.olingo.client.core.communication.request.retrieve.RetrieveRequestFactoryImpl.getEntitySetIteratorRequest(RetrieveRequestFactoryImpl.java:101)
at com.plmc.AccessDBC.main(AccessDBC.java:35)
Caused by: java.lang.IllegalStateException: Target host is null
at org.apache.http.util.Asserts.notNull(Asserts.java:52)
at org.apache.http.impl.conn.DefaultHttpRoutePlanner.determineRoute(DefaultHttpRoutePlanner.java:100)
at org.apache.http.impl.client.DefaultRequestDirector.determineRoute(DefaultRequestDirector.java:756)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:375)
at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at com.plmc.AzureADOAuth2HttpClientFactoryForDBC.init(AzureADOAuth2HttpClientFactoryForDBC.java:112)
... 8 more

我的主要类如下:

import java.net.URI;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetIteratorRequest;
import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
import org.apache.olingo.client.api.domain.ClientEntity;
import org.apache.olingo.client.api.domain.ClientEntitySet;
import org.apache.olingo.client.api.domain.ClientEntitySetIterator;
import org.apache.olingo.client.core.ODataClientFactory;
public class AccessDBC {
public static void main(final String[] args) throws Exception {
String authority ="https://login.windows.net/<company domain>";
String clientId = "<client ID>";
String redirectURI = "http://localhost:8080/login";
String resourceURI = "https://api.businesscentral.dynamics.com/v2.0";
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(
"user name",
"password");

final AzureADOAuth2HttpClientFactoryForDBC oauth2HCF = new AzureADOAuth2HttpClientFactoryForDBC(authority, clientId, redirectURI, resourceURI, creds);

String serviceUrl = "https://api.businesscentral.dynamics.com/v2.0/<ID>/Sandbox/ODataV4/Company('My%20Company')/";
ODataClient client = ODataClientFactory.getClient();
client.getConfiguration()
.setHttpClientFactory(oauth2HCF);

URI absoluteUri = client.newURIBuilder(serviceUrl).build();    
System.out.println(absoluteUri);
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request = 
client.getRetrieveRequestFactory().getEntitySetIteratorRequest(absoluteUri);
request.setAccept("application/json;odata.metadata=minimal");

ODataRetrieveResponse<ClientEntitySetIterator<ClientEntitySet, ClientEntity>> response = request.execute(); 
System.out.println("Reponse status is code: " + response.getStatusCode());
}
}

My AzureADOAuth2HttpClientFactoryForDBC如下:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.olingo.client.core.http.AbstractOAuth2HttpClientFactory;
import org.apache.olingo.client.core.http.OAuth2Exception;
public class AzureADOAuth2HttpClientFactoryForDBC extends AbstractOAuth2HttpClientFactory {
private final String clientId;
private final String redirectURI;
private final String resourceURI;
private final UsernamePasswordCredentials creds;
private ObjectNode token;
public AzureADOAuth2HttpClientFactoryForDBC (final String authority, final String clientId,
final String redirectURI, final String resourceURI, final UsernamePasswordCredentials creds) {
super(URI.create(authority + "/oauth2/authorize"), URI.create(authority + "/oauth2/token"));
this.clientId = clientId;
this.redirectURI = redirectURI;
this.resourceURI = resourceURI;
this.creds = creds;
}
@Override
protected boolean isInited() throws OAuth2Exception {
return token != null;
}
private void fetchAccessToken(final DefaultHttpClient httpClient, final List<BasicNameValuePair> data) {
token = null;
InputStream tokenResponse = null;
try {
final HttpPost post = new HttpPost(oauth2TokenServiceURI);
post.setEntity(new UrlEncodedFormEntity(data, "UTF-8"));
final HttpResponse response = httpClient.execute(post);
tokenResponse = response.getEntity().getContent();
token = (ObjectNode) new ObjectMapper().readTree(tokenResponse);
} catch (Exception e) {
throw new OAuth2Exception(e);
} finally {
IOUtils.closeQuietly(tokenResponse);
}
}
@Override
protected void init() throws OAuth2Exception {
final DefaultHttpClient httpClient = wrapped.create(null, null);
// 1. access the OAuth2 grant service (with authentication)
String code = null;
try {
final URIBuilder builder = new URIBuilder(oauth2GrantServiceURI).
addParameter("response_type", "code").
addParameter("client_id", clientId).
addParameter("redirect_uri", redirectURI);
HttpResponse response = httpClient.execute(new HttpGet(builder.build()));
System.out.println(response.getStatusLine().getStatusCode());
final String loginPage = EntityUtils.toString(response.getEntity());

String postURL = StringUtils.substringBefore(
StringUtils.substringAfter(loginPage, "<form id="credentials" method="post" action=""),
"">");
final String ppsx = StringUtils.substringBefore(
StringUtils.substringAfter(loginPage, "<input type="hidden" id="PPSX" name="PPSX" value=""),
""/>");
final String ppft = StringUtils.substringBefore(
StringUtils.substringAfter(loginPage, "<input type="hidden" name="PPFT" id="i0327" value=""),
""/>");
List<BasicNameValuePair> data = new ArrayList<BasicNameValuePair>();
data.add(new BasicNameValuePair("login", creds.getUserName()));
data.add(new BasicNameValuePair("passwd", creds.getPassword()));
data.add(new BasicNameValuePair("PPSX", ppsx));
data.add(new BasicNameValuePair("PPFT", ppft));
HttpPost post = new HttpPost(postURL);

post.setEntity(new UrlEncodedFormEntity(data, "UTF-8"));
response = httpClient.execute(post);

final String samlPage = EntityUtils.toString(response.getEntity());
postURL = StringUtils.substringBefore(
StringUtils.substringAfter(samlPage, "<form name="fmHF" id="fmHF" action=""),
"" method="post" target="_top">");
final String wctx = StringUtils.substringBefore(
StringUtils.substringAfter(samlPage, "<input type="hidden" name="wctx" id="wctx" value=""),
"">");
final String wresult = StringUtils.substringBefore(StringUtils.substringAfter(samlPage,
"<input type="hidden" name="wresult" id="wresult" value=""), "">");
final String wa = StringUtils.substringBefore(
StringUtils.substringAfter(samlPage, "<input type="hidden" name="wa" id="wa" value=""),
"">");
data = new ArrayList<BasicNameValuePair>();
data.add(new BasicNameValuePair("wctx", wctx));
data.add(new BasicNameValuePair("wresult", wresult.replace("&quot;", """)));
data.add(new BasicNameValuePair("wa", wa));
post = new HttpPost(postURL);
post.setEntity(new UrlEncodedFormEntity(data, "UTF-8"));
response = httpClient.execute(post);
final Header locationHeader = response.getFirstHeader("Location");
if (response.getStatusLine().getStatusCode() != 302 || locationHeader == null) {
throw new OAuth2Exception("Unexpected response from server");
}
final String[] oauth2Info = StringUtils.split(
StringUtils.substringAfter(locationHeader.getValue(), "?"), '&');
code = StringUtils.substringAfter(oauth2Info[0], "=");
EntityUtils.consume(response.getEntity());
} catch (Exception e) {
throw new OAuth2Exception(e);
}
if (code == null) {
throw new OAuth2Exception("No OAuth2 grant");
}
// 2. ask the OAuth2 token service
final List<BasicNameValuePair> data = new ArrayList<BasicNameValuePair>();
data.add(new BasicNameValuePair("grant_type", "authorization_code"));
data.add(new BasicNameValuePair("code", code));
data.add(new BasicNameValuePair("client_id", clientId));
data.add(new BasicNameValuePair("redirect_uri", redirectURI));
data.add(new BasicNameValuePair("resource", resourceURI));
fetchAccessToken(httpClient, data);
if (token == null) {
throw new OAuth2Exception("No OAuth2 access token");
}
}
@Override
protected void accessToken(final DefaultHttpClient client) throws OAuth2Exception {
client.addRequestInterceptor(new HttpRequestInterceptor() {
@Override
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
request.removeHeaders(HttpHeaders.AUTHORIZATION);
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token.get("access_token").asText());
}
});
}
@Override
protected void refreshToken(final DefaultHttpClient client) throws OAuth2Exception {
final List<BasicNameValuePair> data = new ArrayList<BasicNameValuePair>();
data.add(new BasicNameValuePair("grant_type", "refresh_token"));
data.add(new BasicNameValuePair("refresh_token", token.get("refresh_token").asText()));
fetchAccessToken(wrapped.create(null, null), data);
if (token == null) {
throw new OAuth2Exception("No OAuth2 refresh token");
}
}
}
谁能帮帮我,我哪里做错了?基本上,我希望能够使用OAuth2连接到Dynamics 365 Business Central,并使用Olingo框架进行一些REST调用。目前我正试图连接Dynamics 365 Business Central,但我得到了错误。我很感激你的帮助。
  1. 从以下POST请求的结果中获取access_token值:
HttpResponse<String> response = Unirest.post("https://YOUR_DOMAIN/oauth/token")
.header("content-type", "application/x-www-form-urlencoded")
.body("grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret=YOUR_CLIENT_SECRET&audience=https%3A%2F%2F%24%7Baccount.namespace%7D%2Fapi%2Fv2%2F")
.asString();
  1. 将请求(GET,POST等)头中的访问令牌设置为"承载">

最新更新