您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

5.7KB

WSC-MVC — Architecture (as implemented through M2)

Request flow

GET /hello
  -> IIS URL Rewrite rule "WscMvc-Hello" (^hello/?$)
  -> /Default.asp?route=/hello
  -> Server.CreateObject("WscMvc.RequestContext"); ctx.Initialize path, httpMethod, logDir
  -> Server.CreateObject("WscMvc.Application")
  -> Application.Run(ctx, statusLine, contentType, body)         [Framework/Application.wsc]
       -> CreateObject("WscMvc.HomeController")
       -> HomeController.Hello(body)                             [Controllers/HomeController.wsc]
       -> LogOutcome ctx, statusLine  (best-effort append to logs/app.log)
  -> Default.asp sets Response.Status/ContentType, writes body

Component boundary contract

  • Default.asp is the only file that touches ASP intrinsic objects (Request, Response, Server). It contains no business logic — only reading the route query parameter and REQUEST_METHOD, resolving logs/'s physical path via Server.MapPath, invoking WscMvc.RequestContext and WscMvc.Application, and writing the response.
  • Framework/RequestContext.wsc, Framework/Application.wsc, and Controllers/HomeController.wsc never reference Request/Response/Server/Session. All data crosses the ASP-to-WSC and WSC-to-WSC boundaries as either VBScript scalars (strings) or our own RequestContext COM object (not an ASP intrinsic) — never an ASP host object. See docs/DECISIONS.md for why the original SPEC §15 open question about passing ASP intrinsics into a WSC never needed a direct experiment: the architecture never crosses that boundary by design.
  • WSC public members are exposed as plain <method> entries backed by ordinary Sub/Function procedures, never <property>/Property Get. VBScript's Property Get/Let/Set requires a Class...End Class block and cannot appear at a WSC's top-level script scope — confirmed experimentally (see docs/DECISIONS.md), not assumed from general WSC documentation.
  • Routing in M1/M2 is still a single hardcoded If path = "/hello" check inside Application.Run. This is intentionally minimal and will be replaced by the explicit allowlisted route table in M3 (Router.wsc); do not extend it ad hoc before that milestone.
  • Application.Run is the single central point that decides expected (404, ordinary control flow) vs. unexpected (COM/method failure, 500) outcomes, and the single point that logs every outcome. Default.asp still independently guards its own three sequential calls (RequestContext creation, Initialize, Application creation, Run) since a WSC failing to even instantiate happens outside Application.Run's reach.

Per-request lifetime and diagnostics

  • RequestContext and Application are both created fresh per request via Server.CreateObject in Default.asp and dropped (Set ... = Nothing) at the end of the page; neither is ever cached in ASP Session/Application scope, per SPEC §10.
  • RequestContext.Initialize generates a correlation id from Fix(Timer) plus Scripting.FileSystemObject.GetTempName() — not Randomize/Rnd(), which was tried first and shown experimentally to collide when two contexts are created within the same clock tick (see docs/DECISIONS.md).
  • Application.LogOutcome appends one line per request (timestamp, correlation id, method, path, status line, elapsed ms) to logs/app.log, guarded end-to-end by On Error Resume Next so a logging failure can never affect the HTTP response. Concurrent writers are serialized with a manual lock-file mutex (logs/app.log.lock, via CreateTextFile(..., OverwriteExisting:=False)) since no ASP intrinsic locking primitive (Application.Lock) is available to code that must not reference ASP intrinsics. The retry budget is deliberately short (see docs/DECISIONS.md): logging is explicitly best-effort and may drop lines under heavy concurrency without affecting correctness of the response.

COM identity

Component ProgID CLSID
Framework/RequestContext.wsc WscMvc.RequestContext {1C36FA55-34DF-4974-94B9-D657389362B2}
Framework/Application.wsc WscMvc.Application {851C7763-1638-42FE-A166-BF3DD3A96A88}
Controllers/HomeController.wsc WscMvc.HomeController {87488446-60BE-4068-8368-0B709BB68F3F}

CLSIDs are fixed at creation and must never be recycled for a different component (AGENTS.md). Application's public Run signature changed between M1 and M2 (added a leading ctx parameter) under the same CLSID; acceptable because this is active pre-release (v0.1) development with exactly one caller (Default.asp, updated in lockstep) — not a claim that live interface changes are safe for a published/external client.

IIS site (test host: win2025test, 100.127.62.31)

  • Site: WscMvc, binding *:8090, physical path C:\Projects\wsc-mvc.
  • App pool: WscMvc, 64-bit, no managed code. Anonymous authentication identity: IUSR.
  • web.config hides Framework/, Controllers/, tests/, tools/, docs/, logs/ (path-segment based, anywhere in the tree) and denies .wsc/.vbs/.ps1/.md by extension.
  • logs/ has an explicit, scoped icacls ... /grant "IIS_IUSRS:(OI)(CI)M" so classic ASP (impersonating IUSR for anonymous requests) can write app.log. No other project directory grants IUSR/IIS_IUSRS write access — verified with a recursive icacls /T scan, see docs/DECISIONS.md.
  • Default document is Default.asp; /hello is served via a rewrite rule, not the default document.

Deferred to later milestones (do not implement early)

Per SPEC §3 non-goals and IMPLEMENTATION_PLAN M3+: explicit route table, HTML views/templates, ADODB, auth. A more robust logging mechanism (if complete coverage under concurrent load is ever required) is deferred to M6 — see the concurrency finding in docs/DECISIONS.md.

Powered by TurnKey Linux.