topone233

导航

ApiScan

用java帮我做一个工具,对于指定的java web目录,识别出API清单(mvc、jax-rs之类的restful api)
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JavaWebAPIScanner {
    
    // MVC注解模式
    private static final Pattern[] MVC_PATTERNS = {
        Pattern.compile("@RequestMapping\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@GetMapping\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@PostMapping\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@PutMapping\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@DeleteMapping\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@PatchMapping\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@GetMapping\\s*\\(\\s*value\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@PostMapping\\s*\\(\\s*value\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@PutMapping\\s*\\(\\s*value\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@DeleteMapping\\s*\\(\\s*value\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@PatchMapping\\s*\\(\\s*value\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@RequestMapping\\s*\\(\\s*value\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@RequestMapping\\s*\\(\\s*path\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@GetMapping\\s*\\(\\s*path\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@PostMapping\\s*\\(\\s*path\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@PutMapping\\s*\\(\\s*path\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@DeleteMapping\\s*\\(\\s*path\\s*=\\s*[\"']([^\"']+)"),
        Pattern.compile("@PatchMapping\\s*\\(\\s*path\\s*=\\s*[\"']([^\"']+)")
    };
    
    // JAX-RS注解模式
    private static final Pattern[] JAXRS_PATTERNS = {
        Pattern.compile("@Path\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@GET[^}]*@Produces\\s*\\(.*?\\)[^}]*@Path\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@POST[^}]*@Produces\\s*\\(.*?\\)[^}]*@Path\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@PUT[^}]*@Produces\\s*\\(.*?\\)[^}]*@Path\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@DELETE[^}]*@Produces\\s*\\(.*?\\)[^}]*@Path\\s*\\(\\s*[\"']([^\"']+)"),
        Pattern.compile("@PATCH[^}]*@Produces\\s*\\(.*?\\)[^}]*@Path\\s*\\(\\s*[\"']([^\"']+)")
    };
    
    // 类级别注解模式
    private static final Pattern[] CLASS_PATTERNS = {
        Pattern.compile("@RestController\\s+class\\s+(\\w+)"),
        Pattern.compile("@Controller\\s+class\\s+(\\w+)"),
        Pattern.compile("@Path\\s*\\(\\s*[\"']([^\"']+)\\s*\\)\\s+public\\s+class\\s+(\\w+)"),
        Pattern.compile("@Path\\s*\\(\\s*[\"']([^\"']+)\\s*\\)\\s+class\\s+(\\w+)")
    };
    
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Usage: java JavaWebAPIScanner <web_directory>");
            return;
        }
        
        String directoryPath = args[0];
        scanDirectory(directoryPath);
    }
    
    public static void scanDirectory(String directoryPath) {
        Path start = Paths.get(directoryPath);
        List<APIInfo> apis = new ArrayList<>();
        
        try {
            Files.walk(start)
                .filter(path -> path.toString().endsWith(".java"))
                .forEach(file -> {
                    try {
                        String content = new String(Files.readAllBytes(file));
                        List<APIInfo> fileApis = extractAPIs(content, file.toString());
                        apis.addAll(fileApis);
                    } catch (IOException e) {
                        System.err.println("Error reading file: " + file + " - " + e.getMessage());
                    }
                });
            
            System.out.println("Found " + apis.size() + " API endpoints:");
            System.out.println("=".repeat(80));
            
            for (int i = 0; i < apis.size(); i++) {
                APIInfo api = apis.get(i);
                System.out.println((i + 1) + ". [" + api.getMethod() + "] " + api.getPath());
                System.out.println("   File: " + api.getFile());
                System.out.println("   Class: " + api.getClassName());
                System.out.println("   Method: " + api.getMethodName());
                System.out.println();
            }
            
        } catch (IOException e) {
            System.err.println("Error walking directory: " + e.getMessage());
        }
    }
    
    private static List<APIInfo> extractAPIs(String content, String filePath) {
        List<APIInfo> apis = new ArrayList<>();
        
        // 提取类级别信息
        String className = extractClassName(content);
        String classPath = extractClassPath(content);
        
        // 查找所有方法级别的注解
        String[] lines = content.split("\\n");
        for (int i = 0; i < lines.length; i++) {
            String line = lines[i].trim();
            
            // 查找MVC注解
            for (Pattern pattern : MVC_PATTERNS) {
                Matcher matcher = pattern.matcher(line);
                if (matcher.find()) {
                    String method = extractMethodType(line);
                    String path = matcher.group(1);
                    String methodName = extractMethodName(lines, i);
                    
                    // 构建完整路径
                    String fullPath = buildFullPath(classPath, path);
                    
                    apis.add(new APIInfo(method, fullPath, className, methodName, filePath));
                }
            }
            
            // 查找JAX-RS注解
            for (Pattern pattern : JAXRS_PATTERNS) {
                Matcher matcher = pattern.matcher(content.substring(0, 
                    Math.min(content.length(), Math.max(0, content.indexOf(line) + line.length() + 500))));
                if (matcher.find()) {
                    String method = extractJaxRsMethodType(matcher.group(0));
                    String path = matcher.group(1);
                    String methodName = extractMethodName(lines, i);
                    
                    // 构建完整路径
                    String fullPath = buildFullPath(classPath, path);
                    
                    apis.add(new APIInfo(method, fullPath, className, methodName, filePath));
                }
            }
        }
        
        return apis;
    }
    
    private static String extractClassName(String content) {
        Pattern pattern = Pattern.compile("public\\s+class\\s+(\\w+)");
        Matcher matcher = pattern.matcher(content);
        return matcher.find() ? matcher.group(1) : "Unknown";
    }
    
    private static String extractClassPath(String content) {
        for (Pattern pattern : CLASS_PATTERNS) {
            Matcher matcher = pattern.matcher(content);
            if (matcher.find()) {
                // 对于@RestController和@Controller,返回空字符串
                if (content.contains("@RestController") || content.contains("@Controller")) {
                    return "";
                }
                // 对于@Path,返回匹配的第一组
                return matcher.group(1);
            }
        }
        return "";
    }
    
    private static String extractMethodType(String line) {
        if (line.contains("@GetMapping")) return "GET";
        if (line.contains("@PostMapping")) return "POST";
        if (line.contains("@PutMapping")) return "PUT";
        if (line.contains("@DeleteMapping")) return "DELETE";
        if (line.contains("@PatchMapping")) return "PATCH";
        if (line.contains("@RequestMapping")) {
            // 尝试从method参数中提取HTTP方法
            Pattern methodPattern = Pattern.compile("method\\s*=\\s*RequestMethod\\.(\\w+)");
            Matcher matcher = methodPattern.matcher(line);
            if (matcher.find()) {
                return matcher.group(1).toUpperCase();
            }
            // 默认返回GET(如果未指定method,默认为GET)
            return "GET";
        }
        return "UNKNOWN";
    }
    
    private static String extractJaxRsMethodType(String content) {
        if (content.contains("@GET")) return "GET";
        if (content.contains("@POST")) return "POST";
        if (content.contains("@PUT")) return "PUT";
        if (content.contains("@DELETE")) return "DELETE";
        if (content.contains("@PATCH")) return "PATCH";
        return "UNKNOWN";
    }
    
    private static String extractMethodName(String[] lines, int index) {
        // 向上查找直到找到方法声明
        for (int i = index; i >= 0 && i > index - 10; i--) {
            String line = lines[i].trim();
            // 查找方法签名(包含括号但不以分号结尾)
            if (line.matches(".*\\w+\\s+\\w+\\s*\\([^)]*\\)\\s*[^;{]*$")) {
                // 提取方法名
                Pattern methodPattern = Pattern.compile("\\w+\\s+(\\w+)\\s*\\(");
                Matcher matcher = methodPattern.matcher(line);
                if (matcher.find()) {
                    return matcher.group(1);
                }
            }
            // 直接匹配方法定义
            Pattern directMethodPattern = Pattern.compile("(?:public|private|protected)?\\s*(?:static)?\\s*\\w+\\s+(\\w+)\\s*\\(.*\\)");
            Matcher directMatcher = directMethodPattern.matcher(line);
            if (directMatcher.find()) {
                return directMatcher.group(1);
            }
        }
        return "unknownMethod";
    }
    
    private static String buildFullPath(String classPath, String methodPath) {
        StringBuilder fullPath = new StringBuilder();
        
        if (classPath != null && !classPath.isEmpty()) {
            if (!classPath.startsWith("/")) {
                fullPath.append("/");
            }
            fullPath.append(classPath);
        }
        
        if (methodPath != null && !methodPath.isEmpty()) {
            if (!methodPath.startsWith("/")) {
                fullPath.append("/");
            }
            fullPath.append(methodPath);
        }
        
        if (fullPath.length() == 0) {
            fullPath.append("/");
        }
        
        return fullPath.toString();
    }
    
    static class APIInfo {
        private String method;
        private String path;
        private String className;
        private String methodName;
        private String file;
        
        public APIInfo(String method, String path, String className, String methodName, String file) {
            this.method = method;
            this.path = path;
            this.className = className;
            this.methodName = methodName;
            this.file = file;
        }
        
        public String getMethod() { return method; }
        public String getPath() { return path; }
        public String getClassName() { return className; }
        public String getMethodName() { return methodName; }
        public String getFile() { return file; }
    }
}
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SimpleUrlScanner {

    // 类级 Path / RequestMapping
    private static final Pattern CLASS_PATH = Pattern.compile(
            "@(RequestMapping|Path)\\s*\\(\\s*\"([^\"]+)\"\\s*\\)"
    );

    // 方法级 Mapping / Path
    private static final Pattern METHOD_PATH = Pattern.compile(
            "@(RequestMapping|GetMapping|PostMapping|PutMapping|DeleteMapping|Path)\\s*\\(\\s*\"([^\"]+)\"\\s*\\)"
    );

    // JAX-RS HTTP Method(无路径,也算 URL)
    private static final Pattern JAXRS_METHOD = Pattern.compile(
            "@(GET|POST|PUT|DELETE|HEAD|OPTIONS)"
    );

    public static void main(String[] args) throws IOException {
        if (args.length == 0) {
            System.out.println("Usage: java SimpleUrlScanner <java-source-root>");
            return;
        }

        Path root = Paths.get(args[0]);
        Set<String> urls = new LinkedHashSet<>();

        Files.walk(root)
                .filter(p -> p.toString().endsWith(".java"))
                .forEach(p -> scanJavaFile(p, urls));

        System.out.println("==== URL LIST ====");
        urls.forEach(System.out::println);
    }

    private static void scanJavaFile(Path javaFile, Set<String> urls) {
        try {
            List<String> lines = Files.readAllLines(javaFile);

            String classPrefix = "";

            // 先找类级 Path
            for (String line : lines) {
                Matcher m = CLASS_PATH.matcher(line);
                if (m.find()) {
                    classPrefix = m.group(2);
                    break;
                }
            }

            // 再扫方法级
            for (int i = 0; i < lines.size(); i++) {
                String line = lines.get(i).trim();

                Matcher methodPath = METHOD_PATH.matcher(line);
                if (methodPath.find()) {
                    String path = methodPath.group(2);
                    urls.add(normalize(classPrefix + "/" + path));
                    continue;
                }

                // JAX-RS:@GET 这种,没路径也记
                Matcher jaxrs = JAXRS_METHOD.matcher(line);
                if (jaxrs.find()) {
                    urls.add(normalize(classPrefix));
                }
            }

        } catch (Exception e) {
            System.err.println("Failed to parse: " + javaFile);
        }
    }

    private static String normalize(String path) {
        if (path == null || path.isEmpty()) return "/";
        return path.replaceAll("//+", "/");
    }
}

posted on 2026-02-08 21:20  你们真的很严格啊  阅读(13)  评论(0)    收藏  举报