手写一个微型Spring框架(三):NHttpContext的详解

54 阅读8分钟

在Javelin框架中,NHttpContext类在HTTP请求处理流水线中扮演着至关重要的角色。它是一个封装了请求-响应生命周期各个方面的对象,管理着HTTP请求的各个环节。下面我们将深入探讨NHttpContext的功能、组成部分及其设计思路。


NHttpContext概述

NHttpContext是一个容器,保存了处理HTTP请求所需的所有必要元素。它不仅代表当前HTTP交换的状态,还管理流水线上下文、日志记录、错误处理等内容。其设计使其成为HTTP请求生命周期中的主要上下文对象,提供了一种有组织的方式来管理请求和响应。

image.png 以下是NHttpContext中主要属性的介绍:

  1. HttpExchange exchange
    这是实际的HTTP交换对象,代表了HTTP请求和响应。它是Java HTTP服务器提供的标准HttpExchange对象,但在Javelin中,它被封装在NHttpContext中,以便提供更简洁和定制化的处理流程。

  2. HttpPipelineContext pipelineContext
    pipelineContext负责管理HTTP请求处理流水线的各个阶段。这个流水线包括路由、认证、日志记录等步骤。它确保HTTP请求在每个阶段都能被正确处理。

  3. OprLogScope oprLogScope
    该对象用于在请求处理过程中管理日志记录的作用域。它允许Javelin在特定的作用域内创建和处理日志,确保与特定HTTP请求相关的日志能被准确捕捉。

  4. Exception lastException
    如果在请求处理过程中发生任何异常,这个字段存储最后的异常。这个功能对调试非常有用,因为它允许开发者访问可能干扰HTTP请求处理的异常信息。

  5. boolean skipAuthentication
    该布尔标志控制是否跳过当前请求的认证。对于某些请求,不需要认证时可以设置为true,这样请求就不需要经过认证步骤。

public class NHttpContext {
    public HttpExchange exchange;
    public HttpPipelineContext pipelineContext;
    public OprLogScope oprLogScope;
    public Exception lastException;
    public boolean skipAuthentication;

    public NHttpContext(HttpExchange exchange, RouteDefinition routeDefinition) {
        this.exchange = exchange;
        this.pipelineContext = HttpPipelineContext.start(this);
        this.pipelineContext.routeDefinition = routeDefinition;
    }

    void setOprLogScope(OprLogScope oprLogScope) {
        this.oprLogScope = oprLogScope;
    }

    public void httpReply(int statusCode, String message) {
        try {
            this.exchange.sendResponseHeaders(statusCode, 0);
            this.exchange.getResponseBody().write(message.getBytes());
            this.exchange.close(); 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


HttpPipelineContext 类

HttpPipelineContext 类主要负责管理与请求处理流水线相关的信息。其功能包括:

  • processId:每个请求都应该有一个唯一的 ID,方便跟踪和调试。
  • startTimeendTime:记录请求的开始和结束时间,用于性能分析和日志记录。
  • routeDefinition:该字段关联当前请求处理的路由定义,确保请求按正确的路由规则进行处理。
  • setException:设置请求处理过程中发生的异常,便于在整个流程中传递和记录错误。
public class HttpPipelineContext {
    public String processId;
    public Date startTime;
    public Date endTime;
    public NHttpContext httpContext;
    public RouteDefinition routeDefinition;
    public Exception lastException;

    public static HttpPipelineContext start(NHttpContext httpContext) {
        HttpPipelineContext pipelineContext = new HttpPipelineContext(httpContext);

        OprLogScope oprLogScope = OprLogScope.start(httpContext.pipelineContext);
        pipelineContext.httpContext.setOprLogScope(oprLogScope);

        return pipelineContext;
    }

    public void setRouteDefinition(RouteDefinition routeDefinition) {
        this.routeDefinition = routeDefinition;
    }

    private HttpPipelineContext(NHttpContext httpContext) {
        if (httpContext == null) {
            throw new IllegalArgumentException("httpContext cannot be null");
        }
        this.httpContext = httpContext;
        httpContext.pipelineContext = this;
    }

    public void setException(Exception ex) {
        if (ex != null) {
            httpContext.lastException = ex;
        }
    }

    public void completeRequest() {
        throw new AbortRequestException();
    }

    public void dispose() {
        this.httpContext = null;
    }
}

OprLogScope 类

OprLogScope 类负责在请求的生命周期内进行操作日志的记录和异常的处理。它的设计包括:

  • steps:记录操作的每个步骤。
  • oprlog:操作日志对象,记录请求处理过程中的各项操作。
  • start:启动操作日志范围的方法,负责创建和初始化操作日志。
public class OprLogScope {
    private List<StepItem> steps;
    public OprLog oprlog;

    public static OprLogScope start(HttpPipelineContext pipelineContext) {
        OprLogScope scope = new OprLogScope();
        scope.oprlog = OprLog.create(pipelineContext);
        return scope;
    }

    public int setException(Exception ex) {
        return oprlog.setException(ex); 
    }

    public int saveOprLog(HttpPipelineContext pipelineContext) {
        LogHelper.Write(this.oprlog);
        return 1;
    }
}

NHttpContext的设计思路

在设计NHttpContext时,重点考虑了模块化、扩展性、易用性以及与Javelin框架中的其他组件的集成。作为Javelin HTTP请求处理流水线的一部分,NHttpContext的设计思路围绕着以下几个核心原则展开:

单一责任原则(Single Responsibility Principle)

NHttpContext的设计遵循单一责任原则,它主要负责管理和维护一个HTTP请求生命周期中的各种上下文信息。通过将HTTP交换的相关信息(如请求、响应、路由信息、日志记录等)集中到一个对象中,NHttpContext避免了将过多责任分散到多个地方,从而提升了代码的可读性和可维护性。

责任划分:

  • HttpExchange:处理请求和响应。
  • HttpPipelineContext:管理HTTP请求处理的流水线上下文,确保请求按顺序经过不同阶段的处理。
  • OprLogScope:处理日志记录的作用域,确保所有操作的日志能够被正确追踪。
  • Exception:捕捉并存储在请求处理过程中出现的异常信息。

灵活的流水线处理(Flexible Pipeline Handling)

NHttpContext将HTTP请求处理流程分为多个阶段,每个阶段由HttpPipelineContext负责。这使得Javelin的请求处理流水线具有很高的灵活性和可定制性,开发者可以根据具体需求在不同阶段插入中间件、路由逻辑、认证步骤等。

  • 流水线的动态性HttpPipelineContext会根据具体的路由定义和配置动态调整请求的处理流程。每次请求的流水线都可以根据具体的业务需求进行配置,使得请求处理过程更加高效和灵活。

高度模块化(Modularization)

NHttpContext的设计中,各个功能模块之间的耦合度较低。例如,HttpExchangeHttpPipelineContext之间并没有强依赖,而是通过NHttpContext进行解耦,使得每个模块都能够独立工作并实现其特定功能。

  • 分离关注点:日志处理、异常处理、HTTP请求和响应的处理等不同的关注点被封装在不同的对象中,使得每个对象都专注于其核心功能。
  • 扩展性:这种设计使得开发者可以根据需求向NHttpContext中添加新的处理模块,例如,新的认证方式、日志记录策略等,保持了较高的扩展性。

集成式错误处理(Integrated Error Handling)

NHttpContext内建了异常处理机制,通过lastException字段记录发生的异常,使得开发者能够快速定位和处理错误。错误信息被集中管理,有助于后续的调试和问题追踪。

  • 集中管理异常NHttpContext会捕获并存储处理过程中发生的异常,避免了异常分散在各个模块中。这样开发者可以通过lastException迅速获取异常详情,并进行统一的处理。
public class ActionExecutor {
    public void execute(NHttpContext context) throws Exception {
        try{
            // ...
            handlerRequest(context);
        } catch ( AbortRequestException e) {
            // 提前结束请求,啥也不干了
        } catch (Exception e) {
            Throwable cause = e.getCause();
            // 记录原始异常
            context.pipelineContext.setException((Exception)cause);
            app.onError(context);
        } finally {
            app.endRequest(context);
        }

    }

响应处理简化(Simplified Response Handling)

NHttpContext提供了一个简单的接口httpReply,用于发送响应。通过该接口,开发者无需过多关注底层的HttpExchange细节,而只需传递状态码和响应体。这样简化了响应处理流程,提高了开发效率。

  • 简洁的APIhttpReply方法为响应发送提供了统一的接口,使得请求的响应处理变得直观和高效。
  • 自动化的关闭连接:在异常发生后,调用httpReply方法自动关闭HttpExchange连接,无需手动干预。

面向操作的日志记录(Operation-Oriented Logging)

NHttpContext设计了OprLogScope来管理每个HTTP请求的操作日志。这使得每个请求的日志记录都能与当前操作(例如,路由、认证等)相关联,方便进行精准的日志追踪。

  • 作用域管理OprLogScope允许每个请求在特定的操作上下文内生成日志。这种方式增强了日志的可读性,帮助开发者清晰地追踪每个请求在不同阶段的执行情况。
  • 日志隔离:通过为每个请求提供独立的日志作用域,避免了不同请求之间日志的混淆,提高了日志的准确性和可操作性。
public static HttpPipelineContext start(NHttpContext httpContext) {
    HttpPipelineContext pipelineContext = new HttpPipelineContext(httpContext);
    // 为每个请求创建独立的日志作用域
    OprLogScope oprLogScope = OprLogScope.start(httpContext.pipelineContext);
    pipelineContext.httpContext.setOprLogScope(oprLogScope);

    return pipelineContext;
}

可配置的认证机制(Configurable Authentication Mechanism)

NHttpContext通过skipAuthentication标志允许开发者灵活地控制是否需要认证。这使得在某些场景下(如公共API或不需要身份验证的请求)可以跳过认证过程,减少了不必要的性能开销。

  • 认证跳过机制:开发者可以根据业务需求灵活地跳过认证步骤,而无需修改底层认证逻辑。
  • 灵活性:认证机制的可配置性增加了框架的灵活性,使得它能够适应不同类型的Web应用。

在认证中间件里面我们可以这样使用:

public void authenticateRequest(NHttpContext httpContext) throws Exception {
    // 如果skipAuthentication为true,则跳过认证
    if (httpContext.skipAuthentication) 
        return;        

    for (NHttpModule module : modules) {
        module.authenticateRequest(httpContext);
    }
}

结论

NHttpContext类是Javelin框架中处理HTTP请求的关键组件之一。它帮助封装了请求-响应管理的核心元素,提供了一个有组织的方式来处理HTTP交换,包括认证、日志记录、异常处理和响应生成。

通过理解并使用NHttpContext,开发者可以更好地管理和定制HTTP请求的处理行为,确保应用保持模块化、可维护且高效。当然目前这个类还有很多缺点,后续我会在过程中继续完善。中间很多的细节没有展开,大家可以自行查看源码。

完整代码请参考GitHub仓库。