Как сделать простой POST-запрос с использованием Jersey (JAX-RS) к серверу с проверкой подлинности NTLM - PullRequest
0 голосов
/ 09 ноября 2018

Я посмотрел в Интернете и нашел такие статьи, как:

NonRepeatableRequestException с использованием джерси и apache httpClient для вызова REST

и

Как отправить аутентифицированный почтовый запрос NTLM с использованием джерси?

Ответ для второго дает решение только для запросов GET. Я настраиваю своего Клиента следующим образом:

private void init() {
    if (client == null) {
        client = ClientBuilder.newClient(prepareClientConfig());
    }
}

private ClientConfig prepareClientConfig() {
    ClientConfig config = new ClientConfig();
    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    credentialsProvider.setCredentials(AuthScope.ANY, new NTCredentials(userId, password, null, null));
    config.property(ApacheClientProperties.CREDENTIALS_PROVIDER, credentialsProvider);
    config.connectorProvider(new ApacheConnectorProvider());
    return config;
}

Здесь я делаю GET-запрос:

@Override
public Response get(String uri, List<HttpUrlParameter> params) throws WebCallException {
    init();
    String url = baseUrl + restUri + apiVersion + uri;
    WebTarget webTarget = client.target(url);
    for (HttpUrlParameter param : params) {
        webTarget = webTarget.queryParam(param.getKey(), param.getValue());
    }
    Response response = webTarget.request(MediaType.APPLICATION_JSON).get();
    return response;
}

Классы HttpUrlParameter и WebCallException определены мной. Запрос GET выше прекрасно работает. Затем я пытаюсь вызвать POST:

@Override
public Response post(String uri, Object entity, List<HttpUrlParameter> params) throws WebCallException {
    init();
    String url = baseUrl + restUri + apiVersion + uri;
    WebTarget webTarget = client.target(url);
    for (HttpUrlParameter param : params) {
        webTarget = webTarget.queryParam(param.getKey(), param.getValue());
    }
    Response response = webTarget
              .request(MediaType.APPLICATION_JSON)
              .post(Entity.json(entity));
    return response;
}

Сущность, которую я передаю для этого конкретного теста, довольно проста:

import java.util.Date;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_NULL)
public class CrmAccount {

    @JsonProperty("accountid")
    private String accountId;
    @JsonProperty("accountnumber")
    private String accountNumber;
    private String name;
    @JsonProperty("emailaddress1")
    private String emailAddress1;
    @JsonProperty("telephone1")
    private String phoneNumber;
    @JsonProperty("address1_addressid")
    private String address1Id;
    @JsonProperty("address1_line1")
    private String address1Line1;
    @JsonProperty("address1_line2")
    private String address1Line2;
    @JsonProperty("address1_city")
    private String address1City;
    @JsonProperty("address1_stateorprovince")
    private String address1StateOrProvince;
    @JsonProperty("address1_postalcode")
    private String address1PostalCode;
    @JsonProperty("address1_county")
    private String address1Country;

    public CrmAccount() {
        super();
    }

    public String getAccountId() {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    public String getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmailAddress1() {
        return emailAddress1;
    }

    public void setEmailAddress1(String emailAddress1) {
        this.emailAddress1 = emailAddress1;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public String getAddress1Id() {
        return address1Id;
    }

    public void setAddress1Id(String address1Id) {
        this.address1Id = address1Id;
    }

    public String getAddress1Line1() {
        return address1Line1;
    }

    public void setAddress1Line1(String address1Line1) {
        this.address1Line1 = address1Line1;
    }

    public String getAddress1Line2() {
        return address1Line2;
    }

    public void setAddress1Line2(String address1Line2) {
        this.address1Line2 = address1Line2;
    }

    public String getAddress1City() {
        return address1City;
    }

    public void setAddress1City(String address1City) {
        this.address1City = address1City;
    }

    public String getAddress1StateOrProvince() {
        return address1StateOrProvince;
    }

    public void setAddress1StateOrProvince(String address1StateOrProvince) {
        this.address1StateOrProvince = address1StateOrProvince;
    }

    public String getAddress1PostalCode() {
        return address1PostalCode;
    }

    public void setAddress1PostalCode(String address1PostalCode) {
        this.address1PostalCode = address1PostalCode;
    }

    public String getAddress1Country() {
        return address1Country;
    }

    public void setAddress1Country(String address1Country) {
        this.address1Country = address1Country;
    }
}

Я могу сделать POST-вызов через Postman без каких-либо проблем, поэтому я знаю, что этот вызов (за пределами моего Java-кода) работает нормально. Когда я выполняю POST-вызов, используя Джерси, я получаю следующее исключение:

javax.ws.rs.ProcessingException: org.apache.http.client.ClientProtocolException
        at org.glassfish.jersey.apache.connector.ApacheConnector.apply(ApacheConnector.java:481)
        at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:252)
        at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:684)
        at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:681)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
        at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
        at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:681)
        at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:437)
        at org.glassfish.jersey.client.JerseyInvocation$Builder.post(JerseyInvocation.java:343)
        at com.harpercollinschristian.integration.util.rest.crm.RestApiClientCrmImpl.post(RestApiClientCrmImpl.java:119)
        at com.harpercollinschristian.integration.service.crm.CrmRestfulApi$4.request(CrmRestfulApi.java:127)
        at com.harpercollinschristian.integration.util.rest.crm.CrmRestApiRequest.exec(CrmRestApiRequest.java:53)
        at com.harpercollinschristian.integration.service.crm.CrmRestfulApi.createAccount(CrmRestfulApi.java:132)
        at com.harpercollinschristian.integration.service.crm.CrmRestfulApiTest.testCreateAccount(CrmRestfulApiTest.java:80)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
        at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
        at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:254)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
        at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.apache.http.client.ClientProtocolException
        at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:186)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:71)
        at org.glassfish.jersey.apache.connector.ApacheConnector.apply(ApacheConnector.java:435)
        ... 44 more
Caused by: org.apache.http.client.NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity.
        at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:225)
        at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
        at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
        at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
        at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
        ... 46 more

Мне кажется, что объект JSON, передаваемый методу post (), возможно, настроен как поток, который может быть вызван только один раз? Сообщения JSON, которые мне нужно отправлять для всех моих вызовов POST и PATCH, не являются длинными и могут быть легко представлены в виде строки с помощью библиотеки FasterXML / Jackson - так что мы легко сможем «повторить» сущность. Я знаю, что есть кое-что, очевидно, довольно очевидное, что мне не хватает, но я не могу найти решение В проекте используется Jersey Client для вызовов REST API для двух других служб, не принадлежащих Microsoft (и, следовательно, не NTLM), поэтому мне нужно использовать Jersey для них (другими словами, я не хочу вытаскивать Jersey, если смогу помочь) .

Любая помощь будет принята с благодарностью!

1 Ответ

0 голосов
/ 09 ноября 2018

Я получил фрагмент кода от нашего партнера по Microsoft CRM, на который был дан ответ. Наш метод, который устанавливает конфигурацию, должен быть обновлен до:

private ClientConfig prepareClientConfig() {
    ClientConfig config = new ClientConfig();
    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    final AuthScope ntlmAuthScope = new AuthScope(null, -1, AuthScope.ANY_REALM, "NTLM");
    credentialsProvider.setCredentials(ntlmAuthScope, new NTCredentials(userId, password, null, null));
    config.property(ApacheClientProperties.CREDENTIALS_PROVIDER, credentialsProvider);
    config.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED);
    config.connectorProvider(new ApacheConnectorProvider());
    return config;
}

Обратите внимание, что свойство config для REQUEST_ENTITY_PROCESSING - установлено в BUFFERED. Я полагаю, это было то, что нам было необходимо, чтобы объект был буферизован и, следовательно, повторно использован для последующих запросов (для удовлетворения протокола NTLM). Также есть пара других дополнений (например, конкретный AuthScope).

...