问题
Is it possible to create a simple web server with Crystal that can serve HTML, CSS and JS pages?
My current code is:
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)
When I run compile and run the program, it initializes the server, but there are a couple of problems:
- In the Firefox browser's "Inspect Element" console, I see:
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
The server just shows only the content of index.html
.
The codes HTML, CSS and JS is perfectly valid when I run using WEBrick or load up the index.html directly to the browser.
- My server isn't accessible from any other devices on the local network.
回答1:
You might want to use HTTP::StaticFileHandler for this. It serves files from a local directory.
- In the handler you always read file
index.html
whatever the request is. This can't work. - HTTP::Server#listen listens on
127.0.0.1
, so it's only available from localhost. In order to be accessible from the network, you need to listen to an address that is available on the network. For exampleserver.listen("0.0.0.0", Port)
will listen on all interfaces.
回答2:
Many thanks to @Johannes Müller which solved my problem. Here, I am sharing the code for what I wanted exactly.
Code:
#!/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)
This code looks for a "index.html" file to the provided path (default Dir.current), if found, it shares the index.html file to the IP address (default 0.0.0.0) and port (default 8080) provided, else it just shares the current directory contents.
Running:
crystal code.cr /tmp/ 5020 127.0.0.1
The options can be shuffled. For example:
crystal code.cr 5020 /tmp/ 127.0.0.1
Or
crystal code.cr 5020 127.0.0.1 /tmp
This will start the server and share the /tmp direcotory. If the index.html file is found inside the /tmp/ directory, the requested browser will display the index.html content, or it will work similar to an FTP (although it's not).
Compiling and Running:
crystal build code.cr
./code [options]
回答3:
If you don't want to deep dive into native code & wants a simple solution - You should use any framework which provides file server.
You can use Shivneri framework for this. Shivneri has inbuilt file server which is easy to configure -
Shivneri.folders = [{
path: "/",
folder: File.join(Dir.current, "assets"),
}]
You can add as many folder as you want to use. Every folder will be mapped with provided path.
For more info read doc - https://shivneriforcrystal.com/tutorial/file-server/
来源:https://stackoverflow.com/questions/58602639/a-webrick-like-server-with-crystal