具有 Crystal 的类似 WEBrick 的服务器

A WEBrick like server with Crystal

是否可以使用 Crystal 创建一个简单的 Web 服务器,可以服务 HTML、CSS 和 JS 页面?

我当前的代码是:

require "http/server"
Port = 8080
Mime = "text/html"

server = HTTP::Server.new([HTTP::ErrorHandler.new, HTTP::LogHandler.new]) do |context|
    req = context.request
    if req.method == "GET"
        filename = File.join(Dir.current, "index.html")
        context.response.content_type = Mime
        context.response.content_length = File.size(filename)
        File.open(filename) { |file| IO.copy(file, context.response) }
        next
    end
    context.response.content_type = Mime

end

puts "\e[1;33mStarted Listening on Port #{Port}\e[0m"
server.listen(Port)

当我 运行 编译和 运行 程序时,它会初始化服务器,但有几个问题:

  1. 在 Firefox 浏览器的 "Inspect Element" 控制台中,我看到:
The stylesheet http://127.0.0.1:8080/styling.css was not loaded because its MIME type, "text/html", is not "text/css". 127.0.0.1:8080

The script from “http://127.0.0.1:8080/javascript.js” was loaded even though its MIME type (“text/html”) is not a valid JavaScript MIME type. 127.0.0.1:8080

SyntaxError: expected expression, got '<' javascript.js:1

服务器只显示index.html的内容。

代码HTML、CSS和JS在我运行使用WEBrick或直接加载index.html到浏览器时完全有效。

  1. 无法从本地网络上的任何其他设备访问我的服务器。

您可能需要为此使用 HTTP::StaticFileHandler。它提供本地目录中的文件。

  1. 在处理程序中,无论请求是什么,您总是读取文件 index.html。这不行。
  2. HTTP::Server#listen127.0.0.1 上侦听,因此它只能从本地主机获得。为了可以从网络访问,您需要侦听网络上可用的地址。例如 server.listen("0.0.0.0", Port) 将监听所有接口。

非常感谢@Johannes Müller 解决了我的问题。在这里,我正在分享我想要的代码。

代码:

#!/usr/bin/env crystal
require "http/server"

# Get the Address
ADDR = (ARGV.find { |x| x.split(".").size == 4 } || "0.0.0.0").tap { |x| ARGV.delete(x) }
        .split(".").map { |x| x.to_i { 0 } }.join(".")

# Get the Port
PORT = ARGV.find { |x| x.to_i { 0 } > 0 }.tap { |x| ARGV.delete(x) }.to_s.to_i { 8080 }

# Get the path
d = Dir.current
dir = ARGV[0] rescue d
path = Dir.exists?(dir) ? dir : Dir.exists?(File.join(d, dir)) ? File.join(d, dir) : d
listing = !!Dir.children(path).find { |x| x == "index.html" }
actual_path = listing ? File.join(path, "index.html") : path

server = HTTP::Server.new([
        HTTP::ErrorHandler.new,
        HTTP::LogHandler.new,
        HTTP::StaticFileHandler.new(path, directory_listing: !listing)
    ]) do |context|
        context.response.content_type = "text/html"
        File.open(actual_path) { |file| IO.copy(file, context.response) }
end

puts "\e[1;33m:: Starting Sharing \e[38;5;75m#{actual_path}\e[1;31m on \e[38;5;226mhttp://#{ADDR}:#{PORT}\e[0m"
server.listen(::ADDR, ::PORT)

此代码查找 "index.html" 文件到提供的路径(默认 Dir.current),如果找到,它会将 index.html 文件共享到 IP 地址(默认 0.0.0.0 ) 和端口(默认 8080),否则它只共享当前目录内容。

运行:

crystal code.cr /tmp/ 5020 127.0.0.1

选项可以随机排列。例如:

crystal code.cr 5020 /tmp/ 127.0.0.1

crystal code.cr 5020 127.0.0.1 /tmp

这将启动服务器并共享 /tmp 目录。如果在 /tmp/ 目录中找到 index.html 文件,请求的浏览器将显示 index.html 内容,或者它的工作方式类似于 FTP(尽管它不是)。

编译和运行:

crystal build code.cr
./code [options]

如果您不想深入研究本机代码并且想要一个简单的解决方案 - 您应该使用任何提供文件服务器的框架。

您可以为此使用 Shivneri 框架。 Shivneri 内置文件服务器,易于配置 -

Shivneri.folders = [{
    path: "/",
    folder:  File.join(Dir.current, "assets"),
}]

您可以根据需要添加任意数量的文件夹。每个文件夹都将映射到提供的路径。

有关更多信息,请阅读文档 - https://shivneriforcrystal.com/tutorial/file-server/