For API endpoints, serve the JSON-encoded Problem object directly
authorNick Downing <nick@ndcode.org>
Tue, 25 Jan 2022 23:31:17 +0000 (10:31 +1100)
committerNick Downing <nick@ndcode.org>
Tue, 25 Jan 2022 23:51:58 +0000 (10:51 +1100)
Server.mjs

index 2abad5a..7d516b2 100644 (file)
@@ -224,45 +224,39 @@ class Server {
   }
 
   async respond(request, response, protocol) {
-    let parsed_url = url.parse(
-      protocol + '//' + (request.headers.host || 'localhost') + request.url,
-      true
-    )
-    //console.log('parsed_url', parsed_url)
+    let env = {request, response, server: this}
     try {
+      env.parsed_url = url.parse(
+        protocol + '//' + (request.headers.host || 'localhost') + request.url,
+        true
+      )
+
       if (
         !Object.prototype.hasOwnProperty.call(
           this.options.hosts,
-          parsed_url.hostname
+          env.parsed_url.hostname
         )
       )
         throw new Problem(
           'Not found',
-          `Site "${parsed_url.hostname}" not found.`,
+          `Site "${env.parsed_url.hostname}" not found.`,
           404
         )
 
-      let host = this.options.hosts[parsed_url.hostname]
+      let host = this.options.hosts[env.parsed_url.hostname]
       switch (host.type) {
       case 'redirect':
         let new_host = host.host
-        if (parsed_url.port !== null)
-          new_host += ':' + parsed_url.port
+        if (env.parsed_url.port !== null)
+          new_host += ':' + env.parsed_url.port
         this.redirect(
           response,
-          `${parsed_url.protocol}//${new_host}${request.url}`,
-          `redirecting ${parsed_url.host} to ${new_host}`
+          `${env.parsed_url.protocol}//${new_host}${request.url}`,
+          `redirecting ${env.parsed_url.host} to ${new_host}`
         )
         break
       case 'site':
-        await this.site_roots[host.root].site.respond(
-          {
-            parsed_url: parsed_url,
-            response: response,
-            request: request,
-            server: this
-          }
-        )
+        await this.site_roots[host.root].site.respond(env)
         break
       default:
         assert(false)
@@ -270,14 +264,39 @@ class Server {
     }
     catch (error) {
       let problem = Problem.from(error)
-      console.log(`${parsed_url.host} ${problem.status} ${problem.title}`)
+      console.log(
+        env.parsed_url === undefined ?
+          `${problem.status} ${problem.title}` :
+          `${env.parsed_url.host} ${problem.status} ${problem.title}`
+      )
       console.log(`  ${problem.detail}`)
-      this.serve_internal(
-        response,
-        problem.status,
-        'text/html; charset=utf-8',
-        Buffer.from(
-          `<html>
+
+      // for API endpoints, serve the JSON-encoded Problem object directly
+      if (env.mime_type === 'application/json; charset=utf-8')
+        this.serve_internal(
+          response,
+          problem.status,
+          'application/problem+json; charset=utf-8',
+          Buffer.from(
+            JSON.stringify(
+              {
+                title: problem.title,
+                detail: problem.detail,
+                status: problem.status
+              },
+              null,
+              2
+            ) + '\n',
+            'utf-8'
+          )
+        )
+      else
+        this.serve_internal(
+          response,
+          problem.status,
+          'text/html; charset=utf-8',
+          Buffer.from(
+            `<html>
   <head>
     <meta http-equiv="content-type" content="text/html; charset=utf-8">
     <title>${problem.status} ${problem.title}</title>
@@ -288,9 +307,9 @@ class Server {
   </body>
 </html>
 `,
-          'utf-8'
+            'utf-8'
+          )
         )
-      )
     }
   }
 }