java - 使用 JSONConfiguration.FEATURE_POJO_MAPPING 的 HTTP 错误代码 422

标签 java jackson jersey-client

我尝试为 Akeneo PIM 编写 Java Jersey 客户端。

当尝试获取访问 token 时,出现 422 响应状态:

{"code":422,"message":"Parameter \"grant_type\", \"username\" or \"password\" is missing, empty or invalid"}

我认为 Body 中对象的编码存在问题。

当我删除 ClientConfig 时,我遇到了另一个问题:

com.sun.jersey.api.client.ClientHandlerException:com.sun.jersey.api.client.ClientHandlerException:Java 类型、类 com.omb.akeneo.akeneoclient.json.UserAuthenticationInfoJson 和 MIME 的消息正文编写器找不到媒体类型 application/json

pom.xml:

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.omb.akeneo</groupId>
    <artifactId>akeneo-client</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>akeneo-client</name>
    <description></description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Jersy dependencies -->
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
            <version>1.19.4</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-json</artifactId>
            <version>1.19.4</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-bundle</artifactId>
            <version>1.19.4</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.10.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.10.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider -->
        **<dependency>
            <groupId>com.fasterxml.jackson.jaxrs</groupId>
            <artifactId>jackson-jaxrs-json-provider</artifactId>
            <version>2.10.1</version>
        </dependency>**


        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20190722</version>
        </dependency>


        <!-- Apache Commons lib -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.13</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <classifier>exec</classifier>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

帖子正文对象

包 com.omb.akeneo.akeneoclient.json;

导入com.fasterxml.jackson.annotation.JsonCreator; 导入 com.fasterxml.jackson.annotation.JsonProperty;

公共(public)类 UserAuthenticationInfoJson {

private String username;
private String password;
private String grantType;

public UserAuthenticationInfoJson() {
}

@JsonCreator
public UserAuthenticationInfoJson(
        @JsonProperty("username") String username,
        @JsonProperty("password")String password,
        @JsonProperty("grant_type") String grantType) {
    super();
    this.username = username;
    this.password = password;
    this.grantType = grantType;
}

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

public String getGrantType() {
    return grantType;
}

public void setGrantType(String grantType) {
    this.grantType = grantType;
}

}

响应对象

package com.omb.akeneo.akeneoclient.json;

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.*;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
        "access_token",
        "expires_in",
        "token_type",
        "scope",
        "refresh_token"
})
public class UserTokenJson {

    @JsonProperty("access_token")
    private String accessToken;
    @JsonProperty("expires_in")
    private Integer expiresIn;
    @JsonProperty("token_type")
    private String tokenType;
    @JsonProperty("scope")
    private Object scope;
    @JsonProperty("refresh_token")
    private String refreshToken;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();

    @JsonProperty("access_token")
    public String getAccessToken() {
        return accessToken;
    }

    @JsonProperty("access_token")
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    @JsonProperty("expires_in")
    public Integer getExpiresIn() {
        return expiresIn;
    }

    @JsonProperty("expires_in")
    public void setExpiresIn(Integer expiresIn) {
        this.expiresIn = expiresIn;
    }

    @JsonProperty("token_type")
    public String getTokenType() {
        return tokenType;
    }

    @JsonProperty("token_type")
    public void setTokenType(String tokenType) {
        this.tokenType = tokenType;
    }

    @JsonProperty("scope")
    public Object getScope() {
        return scope;
    }

    @JsonProperty("scope")
    public void setScope(Object scope) {
        this.scope = scope;
    }

    @JsonProperty("refresh_token")
    public String getRefreshToken() {
        return refreshToken;
    }

    @JsonProperty("refresh_token")
    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }

}

我的客户:

    package com.omb.akeneo.akeneoclient.client;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.client.filter.LoggingFilter;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;

import java.security.InvalidParameterException;

/**
 * Jersy client to acces Akeneo web api
 */
public class MyAkeneoClient {

    /**
     * credentials to acces at Akeneo api
     */
    private Credentials credential;

    /**
     * Endpoint of Akeneo api
     */
    private String endpoint;

    /**
     * Jersy client
     */
    private Client client;

    /**
     * Initialise a new Akeneo client
     * @param username
     * @param password
     * @param endpoint
     */
    public KAkeneoClient(String clientId, String secret, String username, String password, String endpoint) {

        // Check input params
        checkParams(clientId, secret, username, password, endpoint);

        this.credential = new Credentials(clientId, secret, username, password);
        this.endpoint = endpoint;

        **ClientConfig clientConfig = new DefaultClientConfig();
        clientConfig.getClasses().add(JacksonJsonProvider.class);
        this.client = Client.create(clientConfig);**

        this.client.addFilter(new LoggingFilter(System.out));

    }

    private void checkParams(String clientId, String secret, String username, String password, String endpoint) throws InvalidParameterException {
        if(StringUtils.isBlank(username)) {
            throw new InvalidParameterException("The username parameter is not valid");
        }

        if(StringUtils.isBlank(username)) {
            throw new InvalidParameterException("The username parameter is not valid");
        }

        if(StringUtils.isBlank(password)) {
            throw new InvalidParameterException("The password parameter is not valid");
        }

        if(StringUtils.isBlank(endpoint)) {
            throw new InvalidParameterException("The endpoint parameter is not valid");
        }
    }

    public Client getClient() {
        return client;
    }

    public String getEndpoint() {
        return endpoint;
    }
}

Jersey 客户端:

package com.omb.akeneo.akeneoclient.utils;

import com.omb.akeneo.akeneoclient.Constants;
import com.omb.akeneo.akeneoclient.client.MyAkeneoClient ;
import com.omb.akeneo.akeneoclient.json.UserAuthenticationInfoJson;
import com.omb.akeneo.akeneoclient.json.UserTokenJson;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import org.apache.commons.codec.binary.Base64;

import javax.ws.rs.core.MediaType;

public class AuthUtil {

    /**
     *
     * @param clientId
     * @param secret
     * @param endpoint
     * @param authenticationInfoJson
     * @return
     */
    public static String getAccesToken(final String clientId, final String secret, final String endpoint, final UserAuthenticationInfoJson authenticationInfoJson) {

        MyAkeneoClient  akeneoClient = new MyAkeneoClient (clientId, secret, authenticationInfoJson.getUsername(), authenticationInfoJson.getPassword(), endpoint);

        StringBuilder url = new StringBuilder(akeneoClient.getEndpoint()).append(Constants.GET_ACCES_TOKEN_ENDPOINT);
        WebResource webResource = akeneoClient.getClient().resource(url.toString());

        StringBuilder autorization = new StringBuilder("Basic ").append(AuthUtil.getBase64(clientId, secret));
        ClientResponse response = webResource
                .header("Authorization", autorization.toString())
                .type(MediaType.APPLICATION_JSON)
                .post(ClientResponse.class, authenticationInfoJson);
        if (response.getStatus() != 200) {
            throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
        }

        UserTokenJson userTokenJson = response.getEntity(UserTokenJson.class);

        return userTokenJson.getAccessToken();
    }

    /**
     *
     * get Base64 encode of clientId et secret
     * @param clientId
     * @param secret
     * @return
     */
    public static String getBase64(String clientId, String secret) {
        StringBuilder key = new StringBuilder(clientId).append(":").append(secret);
        String rs = clientId + ":" + secret;
        return Base64.encodeBase64String(rs.getBytes());
    }
}

最佳答案

我解决了我的问题,Apache 默认情况下不会将授权 header 传递给 PHP。

您应该在 VirtualHost 定义中添加以下行:SetEnvIf Authorization "(.*)"HTTP_AUTHORIZATION=$1 :

/etc/apache2/sites-available/your_application.conf

<VirtualHost>
    .... other config....
    SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
    .... other config
</VirtualHost>

对于感兴趣的人,我通过使用 Jersey spring-boot-starter 和 json spring-boot-starter 更改了我的实现。

所以现在我有了

pom

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jersey</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-json</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- Apache Commons lib -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.9</version>
    </dependency>
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.13</version>
    </dependency>

</dependencies>

客户端

package com.omb.akeneo.akeneoclient.client;

import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.client.ClientConfig;
import org.springframework.web.filter.AbstractRequestLoggingFilter;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import java.security.InvalidParameterException;

/**
 * Jersy client to acces Akeneo web api
 */
public class MyAkeneoClient {

    /**
     * credentials to acces at Akeneo api
     */
    private Credentials credential;

    /**
     * Endpoint of Akeneo api
     */
    private String endpoint;

    /**
     * Jersy client
     */
    private Client client;

    /**
     * Initialise a new Akeneo client for omb
     * @param username
     * @param password
     * @param endpoint
     */
    public KAkeneoClient(String clientId, String secret, String username, String password, String endpoint) {

        // Check input params
        checkParams(clientId, secret, username, password, endpoint);

        this.credential = new Credentials(clientId, secret, username, password);
        this.endpoint = endpoint;

        client = ClientBuilder.newClient( new ClientConfig().register( AbstractRequestLoggingFilter.class ) );

    }

    private void checkParams(String clientId, String secret, String username, String password, String endpoint) throws InvalidParameterException {
        if(StringUtils.isBlank(username)) {
            throw new InvalidParameterException("The username parameter is not valid");
        }

        if(StringUtils.isBlank(username)) {
            throw new InvalidParameterException("The username parameter is not valid");
        }

        if(StringUtils.isBlank(password)) {
            throw new InvalidParameterException("The password parameter is not valid");
        }

        if(StringUtils.isBlank(endpoint)) {
            throw new InvalidParameterException("The endpoint parameter is not valid");
        }
    }

    public Client getClient() {
        return client;
    }

    public String getEndpoint() {
        return endpoint;
    }
}

泽西客户端

package com.omb.akeneo.akeneoclient.utils;

import com.omb.akeneo.akeneoclient.Constants;
import com.omb.akeneo.akeneoclient.client.MyAkeneoClient;
import com.omb.akeneo.akeneoclient.json.UserAuthenticationInfoJson;
import com.omb.akeneo.akeneoclient.json.UserTokenJson;
import org.apache.commons.codec.binary.Base64;

import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

public class AuthUtil {

    /**
     *
     * @param clientId
     * @param secret
     * @param endpoint
     * @param authenticationInfoJson
     * @return
     */
    public static String getAccesToken(final String clientId, final String secret, final String endpoint, final UserAuthenticationInfoJson authenticationInfoJson) {

        MyAkeneoClient akeneoClient = new MyAkeneoClient(clientId, secret, authenticationInfoJson.getUsername(), authenticationInfoJson.getPassword(), endpoint);

        StringBuilder url = new StringBuilder(akeneoClient.getEndpoint()).append(Constants.GET_ACCES_TOKEN_ENDPOINT);
        WebTarget webTarget = akeneoClient.getClient().target(url.toString());

        StringBuilder autorization = new StringBuilder("Basic ").append(AuthUtil.getBase64(clientId, secret));
        Invocation.Builder invocationBuilder =  webTarget.request(MediaType.APPLICATION_JSON);
        invocationBuilder.header("Authorization", autorization.toString());

        Response response = invocationBuilder.post(Entity.entity(authenticationInfoJson, MediaType.APPLICATION_JSON));
        if (response.getStatus() != 200) {
            throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
        }

        UserTokenJson userTokenJson = response.readEntity(UserTokenJson.class);

        return userTokenJson.getAccessToken();
    }

    /**
     *
     * get Base64 encode of clientId et secret
     * @param clientId
     * @param secret
     * @return
     */
    public static String getBase64(String clientId, String secret) {
        StringBuilder key = new StringBuilder(clientId).append(":").append(secret);
        String rs = clientId + ":" + secret;
        return Base64.encodeBase64String(rs.getBytes());
    }
}

关于java - 使用 JSONConfiguration.FEATURE_POJO_MAPPING 的 HTTP 错误代码 422,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59502882/

相关文章:

java - 通过json转成Gson Map的Map是什么类型的?

java - 使用 iText 将 HTML 转换为 PDF

Java+MySQL : Read Date as String

java - 如何使用 Jackson 反序列化对象数组

java - Spring Boot Jackson 占用大量内存

web-services - 带有 jersey 2.0 且没有 maven 的 Restful Web 服务

java - 任务成功后附加参数未存储在 Firebase 数据库中?

java - Jackson 总是序列化完整对象或总是只序列化 id

java - AsyncInvoker 不释放线程

java - 在 spring 托管 bean 的构造函数中初始化 Jersey 客户端