前言
最近有项目需求是通过gitlab的api获取其中的数据。来记录一下我的踩坑的过程。
首先先尝试了获取users数据,即所有用户的数据。
这里是gitlab官方API文档 https://docs.gitlab.com/ee/ap...
这里说一下我刚开始的做法和老师建议的做法。
最开始的做法
1. 获取Admin access token
首先我们需要获取gitlab的Admin access token, 并且在请求的时候附带这个token,gitlab才能认证我们的身份并返回数据。
1.登录 GitLab。 2.登录后,点击右上角的头像图标然后选择 Preferences。
3.在 access token 界面就可以新建token了。
当你是group的管理员之后,就可以通过该token获取users了。
2.使用spring boot单元测试发起请求
这里我使用了apache发起http请求,并使用了fastjson处理请求后所返回的json数据。
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
以下是我测试的代码。来解释一下:
- 首先定义你的要请求的url,格式如代码。也可以看官方文档。
- 构建Http请求类,并设置参数。参数必须要有private_token。这里我加的 without_project_bots可以过滤机器人user。
- 使用execute函数发起get请求,并接收返回的json数据。
- 从json数组转换成实体数组
这样就获得了我们需要的数据。
@Test
void getUsers() throws IOException, URISyntaxException {
String url = "https://your gitlab address/api/v4/users";
CloseableHttpClient httpclients = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
// 设置参数信息
URI uri = new URIBuilder(httpGet.getURI())
.addParameter("private_token", "your amind token")
.addParameter("without_project_bots", "true")
.build();
((HttpRequestBase) httpGet).setURI(uri);
// 发起请求
CloseableHttpResponse response = httpclients.execute(httpGet);
// 处理数据
HttpEntity httpEntity = (HttpEntity) response.getEntity();
String result = EntityUtils.toString(httpEntity,"UTF-8");
// 从json转换成实体
final ObjectMapper objectMapper = new ObjectMapper();
User[] langList = objectMapper.readValue(result, User[].class);
logger.debug(String.valueOf(langList));
response.close();
}
期间我遇到的问题
1. java需要添加证书
由于我们发起的请求地址是HTTPS的,而https是对链接加了安全证书SSL的。
对方的接口需要特定的证书才能请求。如果服务器中没有相关链接的SSL证书,就会报sun.security.validator.ValidatorException错误。
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
最开始我是把证书添加到本地中的,添加方法大概参考了如下博客
https://www.cnblogs.com/zoro-...
这样就把证书添加到了本地的java密钥库中。
但是这样只有本地能够正常使用这个项目。因为在并没有把证书添加到项目中。所以这也说明了这种方法并不普适。
所以我们需要把证书放在src/main/resourse中,部署https。下面我会说一下如何添加。
2. 没有配置数据源
Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]
由于我只是在单元测试中测试,项目刚初始化,并没有连接数据库,也就是并没有配置数据源。
所以因为没有向 Spring Boot 传递足够的数据来配置数据源,导致发生了该报错。
所以,如果我们暂时不需要数据源,就可以使用exclude={DataSourceAutoConfiguration.class}自动配置。 这样就可以解决报错
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
public class NonlycodeApplication {
}
3. json数据的属性多于实体的属性
在我请求到数据后,可以看到gitlab给我们返回来的数据有很多。有theme_id, note,is_admin等信息。
但是, 我定义的实体中目前只定义了几条需要的信息,比如id ,name,username等信息。
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
private String email = "";
private Integer following = 0;
private Integer followers = 0;
private String name = "";
private String password = "yunzhi";
@Column(nullable = false, unique = true)
private String username;
在json转化数据的时候,遇到这种情况就会报错。并且报错说:not marked as ignorable。
所以我们需要添加注释,把不需要的数据给忽略掉。
我的解决方法是添加 @JsonIgnoreProperties(ignoreUnknown = true)
。
最后报错消失。
@JsonIgnoreProperties(ignoreUnknown = true)
@Entity
public class User implements Serializable {
使用RestTemplate访问https
访问https需要认证。又分为单向认证和双向认证。
而访问gitlab API单向认证就可以了,也就是只要认证gitlab的CA证书就可以了。
而只要我们使用的HTTP请求库支持HTTPS,就可以自动做到单向认证。
传统情况下在java代码里访问restful服务,一般使用Apache的HttpClient。如同我上面的代码。不过此种方法使用起来太过繁琐。spring提供了一种简单便捷的模板类来进行操作,这就是RestTemplate。
由于RestTemplate 不支持HTTPS, 所以我们需要封装一下。
参考了这篇文章https://blog.csdn.net/MyronCh...
1. 新建工厂类
2. 配置工厂类
public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {
@Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
try {
if (!(connection instanceof HttpsURLConnection)) {
throw new RuntimeException("An instance of HttpsURLConnection is expected");
}
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
httpsConnection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
super.prepareConnection(httpsConnection, httpMethod);
} catch (Exception e) {
e.printStackTrace();
}
}
private static class MyCustomSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
// 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。
// 这些默认的服务的最低质量要求保密保护和服务器身份验证
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
// 返回的密码套件可用于SSL连接启用的名字
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(final Socket socket, final String host, final int port,
final boolean autoClose) throws IOException {
final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress,
final int localPort) throws
IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final InetAddress host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress,
final int localPort) throws
IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
private Socket overrideProtocol(final Socket socket) {
if (!(socket instanceof SSLSocket)) {
throw new RuntimeException("An instance of SSLSocket is expected");
}
//((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.2"});
((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"});
return socket;
}
}
3. 编写代码
User 实体:
@JsonIgnoreProperties(ignoreUnknown = true)
@Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@JsonView
private Long id;
@JsonView
private String email = "";
@JsonView
private Integer following = 0;
@JsonView
private Integer followers = 0;
@JsonView
private String name = "";
private String password = "yunzhi";
@JsonView
private String username;
public void setId(Long id) {this.id = id;}
public Long getId() {return this.id;}
public void setEmail(String email) {this.email = email;}
public String getEmail() {return this.email;}
public void setName(String name) {this.name = name;}
public String getName() {return this.name;}
public void setUsername(String username) {this.username = username;}
public String getUsername() {return this.username;}
public void setFollowing(Integer following) {this.following = following;}
public Integer getFollowing() {return this.following;}
public void setFollowers(Integer followers) {this.followers = followers;}
public Integer getFollowers() {return this.followers;}
}
controller层:
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
UserService userService;
@GetMapping("/getUsers")
@JsonView(GetUsersJsonView.class)
public User[] getUsers() throws JsonProcessingException {
return this.userService.getUsers();
}
public class GetUsersJsonView {
}
}
service层:
注意填写自己的url和token。 逻辑跟之前的代码差不多。
@Override
public User[] getUsers() throws JsonProcessingException {
String url = "https://your gitlab address /api/v4/users/" +
"?private_token={private_token}&without_project_bots={without_project_bots}";
final Map<String, String> variables = new HashMap<>();
variables.put("private_token", "your token");
variables.put("without_project_bots", "true");
RestTemplate restTemplate = new RestTemplate(new HttpsClientRequestFactory());
String response = restTemplate.getForObject(url, String.class, variables);
// 从json转换成实体
final ObjectMapper objectMapper = new ObjectMapper();
User[] userList = objectMapper.readValue(response, User[].class);
return userList;
}
4. url访问
5. 结果
成功获取到信息。
总结
经过这次对gitlab API的请求尝试,感觉自己对如何进行请求、如何处理返回的json数据有了更深的理解。同时也感觉自己对https,以及SSL证书等的理解还不足,之后还需要补一下相关知识。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。