mirror of
https://github.com/TheWhiteDog9487/WebAPI.git
synced 2026-08-11 23:01:30 +08:00
为接口增加OpenAPI注解
注解满天飞,原始代码可读性下降
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
# 简介
|
# 简介
|
||||||
是[https://api.thewhitedog9487.xyz](https://api.thewhitedog9487.xyz)的源代码
|
是[https://api.thewhitedog9487.xyz](https://api.thewhitedog9487.xyz)的源代码
|
||||||
|
|
||||||
|
# 接口文档
|
||||||
|
[这里](https://api.thewhitedog9487.xyz/swagger-ui/index.html)
|
||||||
|
|
||||||
# 目前状态
|
# 目前状态
|
||||||
能用,但缺的东西非常多。
|
能用,但缺的东西非常多。
|
||||||
就像这个文档一样。
|
就像这个文档一样。
|
||||||
@@ -11,7 +14,6 @@
|
|||||||
|
|
||||||
# 下一步
|
# 下一步
|
||||||
- systemd服务自动安装和卸载
|
- systemd服务自动安装和卸载
|
||||||
- Swagger自动生成API接口文档
|
|
||||||
|
|
||||||
# 许可授权
|
# 许可授权
|
||||||
WTFPL
|
WTFPL
|
||||||
+1
-1
@@ -7,7 +7,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "xyz.thewhitedog9487"
|
group = "xyz.thewhitedog9487"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
package xyz.thewhitedog9487.WebAPI.Controller;
|
package xyz.thewhitedog9487.WebAPI.Controller;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -12,10 +20,23 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name="请求客户端IP地址相关")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/ip")
|
@RequestMapping("/ip")
|
||||||
class IP {
|
class IP {
|
||||||
|
@Operation(summary = "获取请求客户端的IP地址", description = """
|
||||||
|
优先级:
|
||||||
|
1. CF-Connecting-IP
|
||||||
|
2. X-Forwarded-For
|
||||||
|
3. 直接使用请求的远程地址
|
||||||
|
""")
|
||||||
|
@ApiResponse(responseCode = "200",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN_VALUE,
|
||||||
|
schema = @Schema(implementation = String.class),
|
||||||
|
examples = @ExampleObject(value = "78.141.226.247")),
|
||||||
|
description = "成功获取到IP地址,内容为纯文本格式的IP地址")
|
||||||
@GetMapping("/ip")
|
@GetMapping("/ip")
|
||||||
ResponseEntity<String> GetIP(@RequestHeader Map<String, String> HttpHeader, HttpServletRequest Request) {
|
ResponseEntity<String> GetIP(@RequestHeader Map<String, String> HttpHeader, HttpServletRequest Request) {
|
||||||
if ( HttpHeader.get("CF-Connecting-IP".toLowerCase() ) instanceof String IP) {
|
if ( HttpHeader.get("CF-Connecting-IP".toLowerCase() ) instanceof String IP) {
|
||||||
@@ -38,6 +59,24 @@ class IP {
|
|||||||
MultiValueMap.fromSingleValue(Map.of("Content-Type", "text/plain;charset=UTF-8")),
|
MultiValueMap.fromSingleValue(Map.of("Content-Type", "text/plain;charset=UTF-8")),
|
||||||
HttpStatus.OK); } }
|
HttpStatus.OK); } }
|
||||||
|
|
||||||
|
@Operation(summary = "获取请求客户端的ISO 3166-1 alpha-2国家代码", description = """
|
||||||
|
依赖Cloudflare的CF-IPCountry头部
|
||||||
|
<br>
|
||||||
|
如果请求没有经过Cloudflare,则会返回"未找到CF-IPCountry头部"
|
||||||
|
""")
|
||||||
|
@ApiResponses(value = {
|
||||||
|
@ApiResponse(responseCode = "200",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN_VALUE,
|
||||||
|
schema = @Schema(implementation = String.class),
|
||||||
|
examples = @ExampleObject(value = "HK") ),
|
||||||
|
description = "成功获取到ISO 3166-1 alpha-2国家代码,内容为纯文本格式的国家代码"),
|
||||||
|
@ApiResponse(responseCode = "404",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN_VALUE,
|
||||||
|
schema = @Schema(implementation = String.class),
|
||||||
|
examples = { @ExampleObject(value = "未找到CF-IPCountry头部") } ),
|
||||||
|
description = "未找到CF-IPCountry头部,内容为纯文本格式的错误信息") } )
|
||||||
@GetMapping("iso3166")
|
@GetMapping("iso3166")
|
||||||
ResponseEntity<String> GetISO3166(@RequestHeader Map<String, String> HttpHeader) {
|
ResponseEntity<String> GetISO3166(@RequestHeader Map<String, String> HttpHeader) {
|
||||||
if (HttpHeader.get("CF-IPCountry".toLowerCase()) instanceof String CountryCode) {
|
if (HttpHeader.get("CF-IPCountry".toLowerCase()) instanceof String CountryCode) {
|
||||||
|
|||||||
@@ -3,10 +3,19 @@ package xyz.thewhitedog9487.WebAPI.Controller;
|
|||||||
import discord4j.common.util.Snowflake;
|
import discord4j.common.util.Snowflake;
|
||||||
import discord4j.core.GatewayDiscordClient;
|
import discord4j.core.GatewayDiscordClient;
|
||||||
import discord4j.rest.http.client.ClientException;
|
import discord4j.rest.http.client.ClientException;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.MissingRequestHeaderException;
|
import org.springframework.web.bind.MissingRequestHeaderException;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -14,16 +23,88 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name="远程消息处理相关")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/message")
|
@RequestMapping("/message")
|
||||||
class Message {
|
class Message {
|
||||||
@Autowired GatewayDiscordClient DiscordBotClient;
|
@Autowired GatewayDiscordClient DiscordBotClient;
|
||||||
@Autowired List<String> ApiKeyList;
|
@Autowired List<String> ApiKeyList;
|
||||||
record PostMessageData(Long ChannelID, String Content) {}
|
|
||||||
|
|
||||||
|
@Schema(description = "包含了发送消息的数据包")
|
||||||
|
record PostMessageData(
|
||||||
|
@Schema(description = "Discord频道ID", example = "1398192763845214239") Long ChannelID,
|
||||||
|
@Schema(description = "要发送的消息内容", example = "成功完成备份,最新文件时间为2025 08 21") String Content) {}
|
||||||
|
|
||||||
|
@Operation(summary = "通过Discord Bot向指定频道发送消息", description = """
|
||||||
|
这是一个私有API,需要在请求头中提供X-API-Key以进行身份验证
|
||||||
|
<br>
|
||||||
|
需要在请求体中提供频道ID和消息内容
|
||||||
|
""")
|
||||||
|
@ApiResponses(value = {
|
||||||
|
@ApiResponse(responseCode = "201",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.APPLICATION_JSON_VALUE,
|
||||||
|
schema = @Schema(implementation = ResponseData.class),
|
||||||
|
examples = @ExampleObject(value = "{\n" +
|
||||||
|
" \"code\": 201,\n" +
|
||||||
|
" \"message\": \"消息发送成功\",\n" +
|
||||||
|
" \"data\": {\n" +
|
||||||
|
" \"新ID\": \"1407848258813825135\",\n" +
|
||||||
|
" \"内容\": \"写点什么好呢\",\n" +
|
||||||
|
" \"频道ID\": \"1398192763845214239\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}") ),
|
||||||
|
description = "消息发送成功,响应体中包含新消息的ID、频道ID和内容"),
|
||||||
|
@ApiResponse(responseCode = "400",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.APPLICATION_JSON_VALUE,
|
||||||
|
schema = @Schema(implementation = ResponseData.class),
|
||||||
|
examples = @ExampleObject(value = "{\n" +
|
||||||
|
" \"code\": 400,\n" +
|
||||||
|
" \"message\": \"请求缺少必要的头部信息\",\n" +
|
||||||
|
" \"data\": {\n" +
|
||||||
|
" \"缺失的头部\": \"X-API-Key\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}") ),
|
||||||
|
description = "缺少必须的请求头,请查看响应中的“缺失的头部”以诊断问题"),
|
||||||
|
@ApiResponse(responseCode = "401",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.APPLICATION_JSON_VALUE,
|
||||||
|
schema = @Schema(implementation = ResponseData.class),
|
||||||
|
examples = @ExampleObject(value = "{\n" +
|
||||||
|
" \"code\": 401,\n" +
|
||||||
|
" \"message\": \"API密钥验证失败\",\n" +
|
||||||
|
" \"data\": {\n" +
|
||||||
|
" \"提供的密钥\": \"123456\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}") ),
|
||||||
|
description = "API密钥验证失败"),
|
||||||
|
@ApiResponse(responseCode = "500",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.APPLICATION_JSON_VALUE,
|
||||||
|
schema = @Schema(implementation = ResponseData.class),
|
||||||
|
examples = @ExampleObject(value = "{\n" +
|
||||||
|
" \"code\": 500,\n" +
|
||||||
|
" \"message\": \"消息发送失败\",\n" +
|
||||||
|
" \"data\": {\n" +
|
||||||
|
" \"内容\": \"芝士异常\",\n" +
|
||||||
|
" \"错误信息\": \"POST /channels/1398192763845214/messages returned 404 Not Found with response {code=10003, message=Unknown Channel}\",\n" +
|
||||||
|
" \"频道ID\": \"1398192763845214\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}")),
|
||||||
|
description = "消息发送失败,可能是由于Discord服务器问题或其他内部错误,请查看响应Body的“内容”子项以确定原因") } )
|
||||||
@PostMapping("/discord")
|
@PostMapping("/discord")
|
||||||
ResponseEntity<ResponseData> DiscordPush(@RequestHeader(value = "X-API-Key", required = true) String ApiKey, @RequestBody PostMessageData RequestBody){
|
ResponseEntity<ResponseData> DiscordPush(
|
||||||
|
@Parameter(description = "用于身份验证的API密钥", required = true, example = "ds1858dscc8745sfwe")
|
||||||
|
@RequestHeader(value = "X-API-Key", required = true) String ApiKey,
|
||||||
|
@io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||||
|
description = "包含频道ID和消息内容的JSON对象",
|
||||||
|
required = true,
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.APPLICATION_JSON_VALUE,
|
||||||
|
schema = @Schema(implementation = PostMessageData.class) ) )
|
||||||
|
@RequestBody PostMessageData RequestBody){
|
||||||
if ( ApiKeyList.contains(ApiKey) == false ) {
|
if ( ApiKeyList.contains(ApiKey) == false ) {
|
||||||
log.warn("API密钥验证失败,密钥:{}", ApiKey);
|
log.warn("API密钥验证失败,密钥:{}", ApiKey);
|
||||||
return new ResponseEntity<>(new ResponseData(
|
return new ResponseEntity<>(new ResponseData(
|
||||||
|
|||||||
@@ -2,11 +2,19 @@ package xyz.thewhitedog9487.WebAPI.Controller;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.springframework.http.HttpStatus;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record ResponseData(int code, Object message, Object data) {
|
@Schema(description = "所有API通用的标准响应格式")
|
||||||
|
public record ResponseData(
|
||||||
|
@Schema(description = "内部用响应代码" ,example = "201") int code,
|
||||||
|
@Schema(description = "人类可读说明信息", example = "消息发送成功") Object message,
|
||||||
|
@Schema(description = "具体的响应数据", example = "{\n" +
|
||||||
|
" \"新ID\": \"1407848258813825135\",\n" +
|
||||||
|
" \"内容\": \"写点什么好呢\",\n" +
|
||||||
|
" \"频道ID\": \"1398192763845214239\"\n" +
|
||||||
|
" }") Object data) {
|
||||||
static ObjectMapper JsonMapper = new ObjectMapper();
|
static ObjectMapper JsonMapper = new ObjectMapper();
|
||||||
public ResponseData(int code) {
|
public ResponseData(int code) {
|
||||||
this(code, null, null); }
|
this(code, null, null); }
|
||||||
|
|||||||
Reference in New Issue
Block a user