java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java GitLab4J实现WebHook

Java使用GitLab4J实现WebHook的代码详解

作者:xixingzhe2

还在为手动解析GitLab Webhook的JSON发愁吗,本文教你用SpringBoot和gitlab4j轻松接收并处理GitLab推送的事件,包括合并请求、代码推送等,还附带了Webhook配置步骤和secret令牌校验方法,让你快速集成GitLab自动化流程,需要的朋友可以参考下

1、SpringBoot 接收 GitLab WebHook(核心)

GitLab 会 POST 请求你的服务接口,把事件 JSON 推过来;gitlab4j 提供GitLabWebHook工具类,直接解析各种事件对象,不用手写 JSON 解析。

GitLab WebHook 请求头:X-GitLab-Token,用于校验请求来源合法性。

2、代码实现

引入依赖

 github地址:https://github.com/gitlab4j/gitlab4j-api

<dependency>
    <groupId>org.gitlab4j</groupId>
    <artifactId>gitlab4j-api</artifactId>
    <version>${gitlab4j-api.version}</version>
</dependency>

配置信息

application.yaml配置

audit:
  gitlab:
    base-url: http://127.0.0.1:21031 # gitlab 服务器地址
    token: 'aaa-bbb-ccc-' # gitlab 访问 token
    webhook-secret: 'fffffffffffffffffffwa1901111' # webhook 密钥

配置类

package com.ybw.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "audit")
public record AuditProperties(
        GitLab gitlab
) {
    public record GitLab(String baseUrl, String token, String webhookSecret) {
    }
}
package com.ybw.config;

import org.gitlab4j.api.GitLabApi;
import org.gitlab4j.api.WebHookManager;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

/**
 * GitLabApi Bean 配置
 * 使用 gitlab4j-api 框架替代原有的 RestClient 手工 HTTP 调用
 */
@Configuration
@EnableConfigurationProperties(AuditProperties.class)
public class GitLabApiConfig {

    /**
     * 连接超时(毫秒)
     */
    private static final int CONNECT_TIMEOUT_MS = 10_000;

    /**
     * 读取超时(毫秒),对于返回大 diff 的 MR changes 接口,需要较长超时
     */
    private static final int READ_TIMEOUT_MS = 120_000;

    @Bean
    public GitLabApi gitLabApi(AuditProperties auditProperties) {
        GitLabApi gitLabApi = new GitLabApi(auditProperties.gitlab().baseUrl(),
                auditProperties.gitlab().token());
        gitLabApi.withRequestTimeout(CONNECT_TIMEOUT_MS, READ_TIMEOUT_MS);
        return gitLabApi;
    }

    /**
     * WebHookManager 用于解析 GitLab webhook 请求
     * 自动根据 X-Gitlab-Event 头将 JSON 反序列化为对应的事件对象
     *
     * @param auditProperties 审计配置(含 webhook secret token)
     */
    @Bean
    public WebHookManager webHookManager(AuditProperties auditProperties) {
        String secret = auditProperties.gitlab().webhookSecret();
        if (secret != null && !secret.isEmpty()) {
            return new WebHookManager(secret);
        }
        return new WebHookManager();
    }
}

Controller 接收接口

package com.ybw.controller;

import com.alibaba.fastjson2.JSON;
import com.ybw.service.AuditService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.gitlab4j.api.WebHookManager;
import org.gitlab4j.api.webhook.Event;
import org.gitlab4j.api.webhook.MergeRequestEvent;
import org.gitlab4j.api.webhook.PushEvent;
import org.gitlab4j.api.webhook.NoteEvent;
import org.gitlab4j.api.webhook.IssueEvent;
import org.gitlab4j.api.webhook.TagPushEvent;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * GitLab Webhook 事件处理
 * <p>
 * 使用 gitlab4j 的 WebHookManager 自动根据 X-Gitlab-Event 请求头
 * 将 JSON 反序列化为对应的事件对象(MergeRequestEvent、PushEvent 等),
 * 不同事件类型对应不同的实体类。
 *
 * @author ybw
 * @version V1.0
 * @className WebhookController
 * @date 2026/8/7
 **/
@Slf4j
@RestController
@RequestMapping("/api/webhook")
@RequiredArgsConstructor
public class WebhookController {

    private final AuditService auditService;
    private final WebHookManager webHookManager;

    /**
     * 接收 GitLab webhook 请求,自动按事件类型分发
     */
    @PostMapping("/gitlab")
    public ResponseEntity<String> handleGitlabWebhook(HttpServletRequest request) {
        // WebHookManager 自动做两件事:
        // 1. 校验 X-Gitlab-Token(如果配置了 secretToken)
        // 2. 根据 X-Gitlab-Event 头将 body 反序列化为对应的事件子类
        Event event;
        try {
            event = webHookManager.handleRequest(request);
        } catch (Exception e) {
            log.error("Webhook 请求处理异常", e);
            return ResponseEntity.badRequest().body("Invalid webhook request: " + e.getMessage());
        }

        if (event == null) {
            log.warn("WebHookManager 返回了 null 事件");
            return ResponseEntity.badRequest().body("Unable to parse webhook event");
        }

        log.info("接收到 Webhook 事件: eventType={}", event.getClass().getSimpleName());

        // 按事件类型分发,不同类型使用不同对象
        if (event instanceof MergeRequestEvent mrEvent) {
            return handleMergeRequest(mrEvent);
        } else if (event instanceof PushEvent) {
            log.debug("Push 事件暂不处理");
            return ResponseEntity.ok("Push event received (not processed)");
        } else if (event instanceof NoteEvent) {
            log.debug("Note 事件暂不处理");
            return ResponseEntity.ok("Note event received (not processed)");
        } else if (event instanceof IssueEvent) {
            log.debug("Issue 事件暂不处理");
            return ResponseEntity.ok("Issue event received (not processed)");
        } else if (event instanceof TagPushEvent) {
            log.debug("Tag Push 事件暂不处理");
            return ResponseEntity.ok("Tag Push event received (not processed)");
        } else {
            log.debug("忽略不支持的事件类型: {}", event.getClass().getSimpleName());
            return ResponseEntity.ok("Ignored: unsupported event type");
        }
    }

    /**
     * 处理 Merge Request 事件
     */
    private ResponseEntity<String> handleMergeRequest(MergeRequestEvent event) {
        log.info("handleMergeRequest event:{}", JSON.toJSONString(event));
        var attrs = event.getObjectAttributes();
        log.info("Merge Request 事件: projectId={}, mrIid={}, action={}",
                event.getProject() != null ? event.getProject().getId() : null,
                attrs != null ? attrs.getIid() : null,
                attrs != null ? attrs.getAction() : "unknown");

        String action = attrs != null ? attrs.getAction() : "unknown";
        if (!"open".equals(action) && !"reopen".equals(action)) {
            log.debug("忽略非 open/reopen 的 MR 事件: action={}", action);
            return ResponseEntity.ok("Ignored MR action: " + action);
        }

        auditService.processMrWebhook(event);
        return ResponseEntity.ok("OK");
    }
}

合并打印日志

{
    "object_kind": "merge_request",
    "changes": {},
    "eventType": "merge_request",
    "labels": [],
    "objectAttributes": {
        "action": "update",
        "assigneeIds": [],
        "authorId": 4,
        "blockingDiscussionsResolved": true,
        "createdAt": "2026-08-08 11:29:37",
        "description": "",
        "detailedMergeStatus": "unchecked",
        "firstContribution": false,
        "headPipelineId": 200,
        "id": 4301,
        "iid": 2,
        "labels": [],
        "lastCommit": {
            "author": {
                "email": "test@aaa.com",
                "name": "test"
            },
            "id": "daca0e0c40ab04c62a15d15315389b8a7dc12ef5",
            "message": "edit\n",
            "timestamp": "2026-08-08 11:42:16",
            "title": "edit",
            "url": "http://gitlab.example.com/test/git-audit/-/commit/daca0e0c40ab04c62a15d15315389b8a7dc12ef5"
        },
        "mergeParams": {
            "force_remove_source_branch": "1"
        },
        "mergeStatus": "unchecked",
        "mergeWhenPipelineSucceeds": false,
        "oldrev": "b1d98ac6079962de9760b009a21d3965827223f0",
        "reviewerIds": [],
        "source": {
            "defaultBranch": "main",
            "gitHttpUrl": "http://gitlab.example.com/test/git-audit.git",
            "gitSshUrl": "git@gitlab.example.com:test/git-audit.git",
            "homepage": "http://gitlab.example.com/test/git-audit",
            "httpUrl": "http://gitlab.example.com/test/git-audit.git",
            "id": 505,
            "name": "git-audit",
            "namespace": "test",
            "pathWithNamespace": "test/git-audit",
            "sshUrl": "git@gitlab.example.com:test/git-audit.git",
            "url": "git@gitlab.example.com:test/git-audit.git",
            "visibilityLevel": 0,
            "webUrl": "http://gitlab.example.com/test/git-audit"
        },
        "sourceBranch": "dev",
        "sourceProjectId": 505,
        "state": "opened",
        "stateId": 1,
        "target": {
            "defaultBranch": "main",
            "gitHttpUrl": "http://gitlab.example.com/test/git-audit.git",
            "gitSshUrl": "git@gitlab.example.com:test/git-audit.git",
            "homepage": "http://gitlab.example.com/test/git-audit",
            "httpUrl": "http://gitlab.example.com/test/git-audit.git",
            "id": 505,
            "name": "git-audit",
            "namespace": "test",
            "pathWithNamespace": "test/git-audit",
            "sshUrl": "git@gitlab.example.com:test/git-audit.git",
            "url": "git@gitlab.example.com:test/git-audit.git",
            "visibilityLevel": 0,
            "webUrl": "http://gitlab.example.com/test/git-audit"
        },
        "targetBranch": "main",
        "targetProjectId": 505,
        "timeChange": 0,
        "timeEstimate": 0,
        "title": "Dev",
        "totalTimeSpent": 0,
        "updatedAt": "2026-08-08 11:41:33",
        "url": "http://gitlab.example.com/test/git-audit/-/merge_requests/2",
        "workInProgress": false
    },
    "objectKind": "merge_request",
    "project": {
        "defaultBranch": "main",
        "gitHttpUrl": "http://gitlab.example.com/test/git-audit.git",
        "gitSshUrl": "git@gitlab.example.com:test/git-audit.git",
        "homepage": "http://gitlab.example.com/test/git-audit",
        "httpUrl": "http://gitlab.example.com/test/git-audit.git",
        "id": 505,
        "name": "git-audit",
        "namespace": "test",
        "pathWithNamespace": "test/git-audit",
        "sshUrl": "git@gitlab.example.com:test/git-audit.git",
        "url": "git@gitlab.example.com:test/git-audit.git",
        "visibilityLevel": 0,
        "webUrl": "http://gitlab.example.com/test/git-audit"
    },
    "repository": {
        "homepage": "http://gitlab.example.com/test/git-audit",
        "name": "git-audit",
        "url": "git@gitlab.example.com:test/git-audit.git"
    },
    "user": {
        "avatarUrl": "https://www.gravatar.com/avatar/077105529dfd283d0b36419f639754f51bc953a1a6af6f7119a2818edbd046b6?s=80&d=identicon",
        "email": "[REDACTED]",
        "id": 4,
        "name": "audit-test",
        "username": "audit-test"
    }
}

3、gitlab webhooks配置

  1. gitlab->设置->Webhooks->添加新的webhook。
  2. Secret令牌和配置信息中的webhook-secret相同。
  3. 勾选需要触发的事件:合并请求事件等
  4. 添加 webhook,点击【测试】发送测试请求验证接口。

4、常见事件类型

事件类说明
PushEvent代码 push 提交
MergeRequestEvent合并请求 MR
TagPushEventTag 推送
IssueEventIssue 事件
NoteEvent评论事件
PipelineEventCI 流水线事件
JobEventCI Job 任务事件

以上就是Java使用GitLab4J实现WebHook的代码详解的详细内容,更多关于Java GitLab4J实现WebHook的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文