javascript - How Can I Serve Static Content Alongside Dynamic Routes in A Deno Oak Server - Stack Overflow

I am used to working with NodeJS and Koa. I've been playing with Deno and have run the example of

I am used to working with NodeJS and Koa. I've been playing with Deno and have run the example of a static fileserver:


/* static_server.js */

import { Application } from '/x/oak/mod.ts'

const port  = 8080

const app = new Application()

// Error handler middleware
app.use(async (context, next) => {
  try {
    await next()
  } catch (err) {
        console.error(err)
    }
})

// Send static content
app.use(async (context) => {
    console.log(`${context.request.method} ${context.request.url.pathname}`)
  await context.send({
    root: `${Deno.cwd()}/static`,
    index: "index.html",
  })
})


await app.listen({ port })

I have also created a dynamic server using routes:


/* routes.js */

import { Application, Router } from '/x/oak/mod.ts'

const port = 8080

const app = new Application()
const router = new Router()

router.get('/', context => {
    context.response.body = 'Hello world!'
  })

router.get('/foo', context => {
    context.response.body = 'Book Page'
  })

router.get('/foo/:thing', context => {
    context.response.body = `Foo ${context.params.thing}`
})

app.use(router.routes())
app.use(router.allowedMethods())

await app.listen({ port })

How can I bine these so that I can serve dynamic content but also provide static files such as the stylesheet?

In my Koa code I use the koa-static package:

import serve from 'koa-static'
app.use(serve('public'))

What is the equivalent for an Oak server?

Adding suggested code (thanks Jonas Wilms)


/* static_content.js */

import { Application, Router } from '/x/oak/mod.ts'

const port = 8080

const app = new Application()
const router = new Router()

router.get('/', context => {
    context.response.body = 'Hello world!'
  })

router.get('/foo', context => {
    context.response.body = 'Book Page'
  })

router.get('/foo/:thing', context => {
    context.response.body = `Foo ${context.params.thing}`
})

router.get(context => context.send({ root: `${Deno.cwd()}/static` }))

app.use(router.routes())
app.use(router.allowedMethods())

await app.listen({ port })

but this still does not work...

I am used to working with NodeJS and Koa. I've been playing with Deno and have run the example of a static fileserver:


/* static_server.js */

import { Application } from 'https://deno.land/x/oak/mod.ts'

const port  = 8080

const app = new Application()

// Error handler middleware
app.use(async (context, next) => {
  try {
    await next()
  } catch (err) {
        console.error(err)
    }
})

// Send static content
app.use(async (context) => {
    console.log(`${context.request.method} ${context.request.url.pathname}`)
  await context.send({
    root: `${Deno.cwd()}/static`,
    index: "index.html",
  })
})


await app.listen({ port })

I have also created a dynamic server using routes:


/* routes.js */

import { Application, Router } from 'https://deno.land/x/oak/mod.ts'

const port = 8080

const app = new Application()
const router = new Router()

router.get('/', context => {
    context.response.body = 'Hello world!'
  })

router.get('/foo', context => {
    context.response.body = 'Book Page'
  })

router.get('/foo/:thing', context => {
    context.response.body = `Foo ${context.params.thing}`
})

app.use(router.routes())
app.use(router.allowedMethods())

await app.listen({ port })

How can I bine these so that I can serve dynamic content but also provide static files such as the stylesheet?

In my Koa code I use the koa-static package:

import serve from 'koa-static'
app.use(serve('public'))

What is the equivalent for an Oak server?

Adding suggested code (thanks Jonas Wilms)


/* static_content.js */

import { Application, Router } from 'https://deno.land/x/oak/mod.ts'

const port = 8080

const app = new Application()
const router = new Router()

router.get('/', context => {
    context.response.body = 'Hello world!'
  })

router.get('/foo', context => {
    context.response.body = 'Book Page'
  })

router.get('/foo/:thing', context => {
    context.response.body = `Foo ${context.params.thing}`
})

router.get(context => context.send({ root: `${Deno.cwd()}/static` }))

app.use(router.routes())
app.use(router.allowedMethods())

await app.listen({ port })

but this still does not work...

Share Improve this question edited Dec 28, 2020 at 16:17 Mark Tyers asked Dec 28, 2020 at 15:32 Mark TyersMark Tyers 3,2894 gold badges35 silver badges58 bronze badges 5
  • router.all(context => context.send({ root: .. })); should work (as the last route). – Jonas Wilms Commented Dec 28, 2020 at 15:50
  • Tried adding this but won't work. I'll try and post the current version of the code incorporating your suggestion. – Mark Tyers Commented Dec 28, 2020 at 16:15
  • What does "won't work" mean? Does the route match? – Jonas Wilms Commented Dec 28, 2020 at 18:05
  • screen is blank and I get a 404 error. – Mark Tyers Commented Dec 28, 2020 at 18:09
  • May be this will help:stackoverflow./questions/62443440/… – Chellappan வ Commented Dec 29, 2020 at 3:42
Add a ment  | 

5 Answers 5

Reset to default 4

After bining a lot of the information in the ments I managed to get things working:


/* static_content.js */

import { Application, Router, Status } from 'https://deno.land/x/oak/mod.ts'

const port = 8080

const app = new Application()
const router = new Router()

// error handler
app.use(async (context, next) => {
  try {
    await next()
  } catch (err) {
        console.log(err)
  }
})

// the routes defined here
router.get('/', context => {
    context.response.body = 'Hello world!'
})

router.get('/error', context => {
    throw new Error('an error has been thrown')
})

app.use(router.routes())
app.use(router.allowedMethods())

// static content
app.use(async (context, next) => {
    const root = `${Deno.cwd()}/static`
    try {
        await context.send({ root })
    } catch {
        next()
    }
})

// page not found
app.use( async context => {
    context.response.status = Status.NotFound
  context.response.body = `"${context.request.url}" not found`
})

app.addEventListener("listen", ({ port }) => console.log(`listening on port: ${port}`) )

await app.listen({ port })


I know I'm a bit late on the thread, but there are some things I would like to point out.

In Oak 10.1 (the current version at the time of this writing), the send function throws an error if the file it tired to load did not exist. Thus, our static+dynamic server can take on the following form.

import { oak, pathUtils } from './deps.ts'

const app = new oak.Application()
const router = new oak.Router()

app.use(async (ctx, next) => {
    try {
        await oak.send(ctx, ctx.request.url.pathname, {
            root: 'static',
            index: 'index.html',
        })
    } catch (_) {
        await next()
    }
})

router.get('/dynamic', ctx => {
    ctx.response.body = 'dynamic route worked'
})

app.use(router.allowedMethods())
app.use(router.routes())

app.listen({ port: 8000 })

If you want to serve your static files at a certain root path, change the static middleware so that it checks for the root and then omits that root path from the second argument of the send function.

function staticMiddleware(rootPath: string, staticDirectory: string) {
    return async (ctx, next) => {
        if (!ctx.request.url.pathname.startsWith(rootPath)) return await next()

        const newPath = ctx.request.url.pathname.slice(rootPath.length)
        if (!newPath.startsWith('/') && newPath.length) return await next()

        try {
            await oak.send(ctx, newPath, {
                root: staticDirectory,
                index: 'index.html',
            })
        } catch (_) {
            await next()
        }
   }
}

app.use(staticMiddleware('/assets', 'static'))

I think you should use the static router at last. Because when use static server first, dynamic router is nonreachable for static router error.

app.use(router.routes())
app.use(router.allowedMethods())
// move the static router down
app.use( async context => {
  context.response.status = Status.NotFound
  context.response.body = `"${context.request.url}" not found`
})

Not sure whether this is still relevant or already outdated, but as of now (August 2022), there seems to be no general answer to this.

Serving Static Files Alongside Your API Using Oak/Deno

When setting up OpenAPI for an oak-based REST service, I was ing across this issue as well. Requirements were:

  • Serve openapi.yml statically from /openapi/openapi.yml
  • Serve a HTML statically from /openapi for convenience
  • Serve prefixed routers unaffectedly

A straight-forward approach to serve static files from a certain directory under a sub-path of the application is using a middleware and checking the path:

import {
  Application, Context, Router
} from 'https://deno.land/x/[email protected]/mod.ts';

const app = new Application();
const port = 3000;

// Middleware only hooking in and sending static files if prefix matches
// the desired subpath:
const openapi = async (ctx: Context, next: () => Promise<unknown>) => {
  const prefix = '/openapi'; // Sub-path to react on
  if (ctx.request.url.pathname.startsWith(prefix)) {
    await ctx.send({
      root: `${Deno.cwd()}/src/openapi/`, // Local directory to serve from
      index: 'index.html',
      path: ctx.request.url.pathname.replace(prefix, ''), // Map to target path
    });
  } else {
    // If the request does not match the prefix, just continue serving from
    // whatever es next..
    await next();
  }
};

// This is a dummy API endpoint wrapped into a prefixed router for demo:
const statusRouter = new Router({ prefix: '/status' });
statusRouter.get('/', (ctx: Context) => {
  ctx.response.body = {
    healthy: true,
    ready: true,
  };
});

// Boilerplate..
app.use(openapi);
app.use(statusRouter.routes());
app.use(statusRouter.allowedMethods());

app.addEventListener('listen', () => {
  console.log(`Listening on localhost:${port}`);
});

await app.listen({ port });

Running this MWE using deno run --allow-net --allow-env --allow-read src/serve.ts, you'll

  • find the statically served /openapi/openapi.yml,
  • find the index.html from your local static path served under /openapi (resp. /openapi/ and /openapi/index.html)
  • find the /status API behaving just normally.

i'm using like that. In html you can provide a path to your file:

<script src="/js/app.js"></script>

then you can use routes to provide what do you want to use on path js/app.js:

import {RouterContext} from 'https://deno.land/x/oak/mod.ts'

const decoder = new TextDecoder("utf-8")// set doecoder
const fileCont = await Deno.readFile('./views/test.js') //getting file conetent
const fileJS = decoder.decode(fileCont) //decoding it

router.get('/js/app.js', (ctx: RouterContext) => { //yep, route can has defferents of real file location
        ctx.response.type = "application/javascript"
        ctx.response.body = fileJS
    })

and whatever you are providing this link somewhere it'll render you file.

Deno REST API

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744262266a4565693.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信