js请求路径模式匹配 匹配“/**“和“/xx/*/

需求描述: 要对路径进行白名单过滤,满足通配符**

     /**
     * 路径匹配
     * @param pattern
     * @param url
     * @return true : 能匹配 false: 不匹配
     */
    static pathMatch(pattern: string, url: string) {
        const path = new URL(url).pathname;
        if (pattern === path) {
            return true;
        }
        // 判断开头字符
        if (pattern.startsWith(this.pathSeparator) !== path.startsWith(this.pathSeparator)) {
            return false;
        }
        const pattDirs = pattern.split(this.pathSeparator);
        const pathDirs = path.split(this.pathSeparator);
        let pattIdxStart = 0;
        let pattIdxEnd = pattDirs.length - 1;
        let pathIdxStart = 0;
        let pathIdxEnd = pathDirs.length - 1;
        while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            let pattDir = pattDirs[pattIdxStart];
            if ("**" === pattDir) {
                break;
            }
            if ("*" === pattDir) {
                pattIdxStart++;
                pathIdxStart++;
                continue;
            }
            if (pattDir !== pathDirs[pathIdxStart]) {
                return false;
            }
            pattIdxStart++;
            pathIdxStart++;
        }
        if (pathIdxStart > pathIdxEnd) {
            // Path is exhausted, only match if rest of pattern is * or **'s
            if (pattIdxStart > pattIdxEnd) {
                return (pattern.endsWith(this.pathSeparator) == path.endsWith(this.pathSeparator));
            }
            if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart] === "*" && path.endsWith(this.pathSeparator)) {
                return true;
            }
            for (let i = pattIdxStart; i <= pattIdxEnd; i++) {
                if (pattDirs[i] !== "**") {
                    return false;
                }
            }
            return true;
        } else if (pattIdxStart > pattIdxEnd) {
            // String not exhausted, but pattern is. Failure.
            return false;
        } else if ("**" === pattDirs[pattIdxStart]) {
            // Path start definitely matches due to "**" part in pattern.
            return true;
        }
        while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            let pattDir = pattDirs[pattIdxEnd];
            if (pattDir === "**") {
                break;
            }
            if ("*" === pattDir) {
                pattIdxStart++;
                pathIdxStart++;
                continue;
            }
            if (pattDir !== pathDirs[pathIdxEnd]) {
                return false;
            }
            pattIdxEnd--;
            pathIdxEnd--;
        }
        if (pathIdxStart > pathIdxEnd) {
            // String is exhausted
            for (let i = pattIdxStart; i <= pattIdxEnd; i++) {
                if (pattDirs[i] !== "**") {
                    return false;
                }
            }
            return true;
        }

        while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            let patIdxTmp = -1;
            for (let i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
                if (pattDirs[i] === "**") {
                    patIdxTmp = i;
                    break;
                }
            }
            if (patIdxTmp == pattIdxStart + 1) {
                // '**/**' situation, so skip one
                pattIdxStart++;
                continue;
            }
            // Find the pattern between padIdxStart & padIdxTmp in str between
            // strIdxStart & strIdxEnd
            let patLength = (patIdxTmp - pattIdxStart - 1);
            let strLength = (pathIdxEnd - pathIdxStart + 1);
            let foundIdx = -1;

            strLoop:
                for (let i = 0; i <= strLength - patLength; i++) {
                    for (let j = 0; j < patLength; j++) {
                        let subPat = pattDirs[pattIdxStart + j + 1];
                        let subStr = pathDirs[pathIdxStart + i + j];
                        if (subPat !== subStr) {
                            continue strLoop;
                        }
                    }
                    foundIdx = pathIdxStart + i;
                    break;
                }

            if (foundIdx == -1) {
                return false;
            }

            pattIdxStart = patIdxTmp;
            pathIdxStart = foundIdx + patLength;
        }
        for (let i = pattIdxStart; i <= pattIdxEnd; i++) {
            if (pattDirs[i] !== "**") {
                return false;
            }
        }
        return true;
    }

使用示例

pathMatch("/**", "http://aaa.bb.com/aaa/bbb") // true
pathMatch("/a/*/b", "http://aaa.bb.com/a/ccc/b") // true
pathMatch("/a/b/c", "http://aaa.bb.com/a/b/c") // true





<think>好的,我现在要解决用户遇到的API接口返回404错误的问题,特别是针对/api/shop/status这个端点。首先,我需要回忆一下常见的导致404错误的原因,并逐一排查。 首先,404错误通常表示客户端能够与服务器通信,但服务器找不到请求的资源。这可能由多种原因引起,比如URL拼写错误、路由配置问题、服务未正确部署或网络配置问题等。接下来,我需要根据用户提供的信息现有的引用内容来寻找可能的解决方案。 用户提到的引用中,有关于生成TypeScript客户端代码的配置,比如在cybtans.json中添加Clients选项,这可能涉及到前端服务是否正确生成了对应的接口。如果前端调用的API路径与后端实际提供的路径不一致,就会导致404错误。例如,生成的TypeScript服务可能指向了错误的URL或者路由配置有误,需要检查生成的代码中的basePath或路径是否正确。 另外,引用[3]提到了使用dotnet sln添加项目到解决方案中,这可能涉及到后端服务的项目结构是否正确部署。如果相关的API项目没有被正确添加到解决方案中,或者部署时没有包含对应的控制器,也可能导致404错误。需要确认后端项目是否成功构建并部署,以及对应的路由是否在启动时正确注册。 引用[4]提到淘宝的高并发反爬机制,虽然用户的问题不直接涉及爬虫,但高并发可能导致服务暂时不可用,但这种情况可能返回5xx错误而非404。不过,如果用户的环境存在反向代理或负载均衡器配置错误,导致请求未能正确路由到后端服务,也可能出现404。需要检查Nginx或Apache等服务器的配置,确认代理设置是否正确,特别是路径重写规则是否匹配/api/shop/status。 接下来,按照步骤来排查: 1. **确认URLHTTP方法是否正确**:用户需检查请求的URL是否有拼写错误,比如多余的斜杠、大小写不一致等。同时,确认使用的HTTP方法(GET、POST等)是否与后端接口定义的方法一致。 2. **检查后端路由配置**:查看后端代码中是否存在对应的路由处理程序。例如,在ASP.NET Core中,检查Controller是否有[Route("api/shop")]属性,以及是否有对应的Action方法处理/status请求。如果是其他框架如Spring Boot或Express.js,同样需要确认路由注解或配置是否正确。 3. **验证服务部署状态**:通过dotnet sln list确认所有相关项目都已正确添加到解决方案并构建成功。检查部署后的目录结构,确保DLL文件或可执行文件存在,并且运行时没有报错。查看应用的启动日志,确认路由注册成功,没有异常抛出。 4. **检查中间件过滤器**:某些中间件可能会拦截请求并返回404,比如身份验证或授权中间件。如果用户没有正确提供认证信息,可能会被重定向或拒绝,但通常会返回401或403。需要检查是否有自定义的中间件或过滤器错误地处理了请求。 5. **网络代理配置**:如果使用了反向代理,检查代理的配置是否正确将请求转发到后端服务的正确端口路径。例如,Nginx的location /api/配置是否代理到了后端的地址,是否存在路径重写的问题,导致/api/shop/status未被正确映射。 6. **跨域问题(CORS)**:虽然CORS问题通常会导致403错误,但某些配置错误可能引发意外行为。不过,404更可能与资源不存在有关,所以这一步可能优先级较低。 7. **查看API文档生成代码**:根据引用[1],如果使用了代码生成工具,可能需要检查生成的TypeScript服务中的API路径是否正确。例如,base URL是否配置为后端的正确地址,路径是否为api/shop/status,而非其他路径。同时,确保生成的后端接口代码与前端调用匹配。 8. **测试直接访问API**:使用Postman或curl直接请求后端服务的地址,绕过任何前端代理,确认是否仍然返回404。如果直接访问成功,则问题可能出在代理配置;如果仍然404,则问题在后端服务本身。 9. **检查版本控制**:有时候API可能有版本前缀,比如/v1/api/shop/status,而用户可能遗漏了版本部分,导致路径匹配。 10. **查看日志监控**:检查后端服务的访问日志错误日志,查看是否有该端点的请求记录。如果日志中没有记录,说明请求根本没有到达后端,可能被代理或防火墙拦截;如果有记录但返回404,则需要检查路由处理逻辑。 结合用户的引用内容,特别是引用[3]中的dotnet sln操作,如果用户使用的是.NET Core,可能需要检查Startup.cs中的路由配置,是否使用了正确的UseEndpoints配置,确保控制器被正确发现路由。例如: ```csharp app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); ``` 同时,检查控制器类是否具有[ApiController][Route]属性: ```csharp [ApiController] [Route("api/shop")] public class ShopController : ControllerBase { [HttpGet("status")] public IActionResult GetStatus() { // 逻辑处理 return Ok(); } } ``` 如果用户的路由配置正确,但依然404,可能需要检查是否项目未正确引用或编译,导致控制器未被加载。此外,检查是否有拼写错误,比如将status拼写为stats等。 另外,引用[2]提到直接使用求解器的API需要查阅文档,这里同样适用:用户需要确认API的文档,确保路径、方法、参数都正确无误。例如,是否有查询参数或请求体要求,导致路由不匹配。 总结可能的问题点: - URL拼写错误或HTTP方法不正确 - 后端路由未正确配置 - 服务未正确部署或项目未包含在解决方案中 - 反向代理配置错误 - 生成的客户端代码路径不正确 - 控制器未被正确注册或存在编译错误 接下来,用户需要按照这些步骤逐一排查,确认每个环节是否正确。例如,先通过Postman直接调用后端服务的IP端口,确认接口是否存在;然后检查代理配置;再查看后端代码的路由设置等。</think>### 解决API接口返回404错误的步骤分析 #### 1. 验证请求URLHTTP方法 - **URL准确性**:确认请求的URL `$http://example.com/api/shop/status$` 是否与服务端定义完全一致(包括大小写、斜杠、路径层级)。例如,`/api/shop/status/` 多一个斜杠可能导致路由不匹配。 - **HTTP方法匹配**:检查是否使用正确的HTTP方法(如GET、POST),需与后端接口定义的注解或配置一致[^2]。 #### 2. 检查后端路由配置 - **路由注解验证**:若使用ASP.NET Core,确保控制器类标注了 `[Route("api/shop")]`,且Action方法包含 `[HttpGet("status")]` 等属性。例如: ```csharp [ApiController] [Route("api/shop")] public class ShopController : ControllerBase { [HttpGet("status")] public IActionResult GetStatus() => Ok(); } ``` - **路由注册检查**:在 `Startup.cs` 中确认已启用控制器路由映射(`endpoints.MapControllers()`)[^3]。 #### 3. 服务部署与项目结构 - **项目包含性验证**:通过 `$ dotnet sln list$` 确认相关项目(如`identity`、`user`)已正确添加到解决方案。 - **构建与部署日志**:检查构建输出运行时日志,确认无编译错误或控制器加载失败信息。 #### 4. 反向代理配置排查 - **路径重写规则**:若使用Nginx,检查代理配置是否将 `/api` 路径正确转发到后端服务。例如: ```nginx location /api/ { proxy_pass http://backend-service:5000; proxy_set_header Host $host; } ``` - **测试直连后端**:绕过代理直接访问后端服务的IP端口(如 `http://localhost:5000/api/shop/status`),确认是否返回404。 #### 5. 客户端代码生成问题 - **生成的路径检查**:若通过工具生成TypeScript客户端(如引用[1]),检查生成代码中的 `basePath` 接口路径是否与后端匹配。例如: ```typescript // Angular服务类 @Injectable() export class ShopService { constructor(private http: HttpClient) {} getStatus() { return this.http.get('/api/shop/status'); // 需与后端实际路径一致 } } ``` #### 6. 日志与监控分析 - **服务端访问日志**:查看后端服务的请求日志,确认是否有 `/api/shop/status` 的访问记录。若无记录,可能是请求未到达后端(被代理拦截或DNS问题)。 - **错误日志排查**:检查是否有路由解析失败的异常信息(如ASP.NET Core的路由匹配失败日志)。 #### 7. 版本控制与中间件干扰 - **API版本前缀**:确认是否需要添加版本号(如 `/v1/api/shop/status`)。 - **中间件顺序**:检查认证、授权中间件是否在路由中间件之后,导致请求被提前拦截。 --- ### 常见解决方案 1. **修复路由配置**:确保控制器Action方法的路由属性与请求路径完全匹配。 2. **重新部署服务**:清理并重新构建项目,确保所有依赖项控制器被正确加载。 3. **代理配置修正**:调整反向代理规则,确保路径转发无误。 4. **客户端同步更新**:重新生成TypeScript服务代码,确保路径一致性[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值