我对编程和灵丹妙药极其陌生.因此,我尽可能多地学习.但我遇到了麻烦.我正在寻找如何在另一个模块中使用我的函数的方法.我正在构建web-server,它将键值映射存储在内存中.为了保持地图临时,我决定使用Agent.这是我的代码的一部分:
defmodule Storage do
use Agent
def start_link do
Agent.start_link(fn -> %{} end, name: :tmp_storage)
end
def set(key, value) do
Agent.update(:tmp_storage, fn map -> Map.put_new(map, key, value) end)
end
def get(key) do
Agent.get(:tmp_storage, fn map -> Map.get(map, key) end)
end
end
Run Code Online (Sandbox Code Playgroud)
所以我试图将这些功能放到Web服务器的路由上:
defmodule Storage_router do
use Plug.Router
use Plug.Debugger
require Logger
plug(Plug.Logger, log: :debug)
plug(:match)
plug(:dispatch)
post "/storage/set" do
with {:ok, _} <- Storage.set(key, value) do
send_resp(conn, 200, "getting the value")
else
_ ->
send_resp(conn, 404, "nothing")
end
end
end
Run Code Online (Sandbox Code Playgroud)
我接受:
warning: variable "key" does not exist and is being expanded to "key()", please use parentheses to remove the ambiguity or change the variable name
lib/storage_route.ex:12
warning: variable "value" does not exist and is being expanded to "value()", please use parentheses to remove the ambiguity or change the variable name
lib/storage_route.ex:12
寻找任何建议\帮助
我对编程和灵丹妙药极其陌生.
我不认为开始用elixir学习编程是明智的.我会从python或ruby开始,然后在一两年后我会尝试elixir.
您需要学习的第一件事是如何发布代码.搜索谷歌如何在stackoverflow上发布代码.然后,你必须让你的缩进全部排成一列.您使用的是计算机编程文本编辑器吗?如果没有,那么你必须得到一个.有很多免费的.我使用vim,它安装在Unix上,就像计算机一样.您可以通过vimtutor
在终端窗口中输入来学习如何使用vim .
接下来,代码中出现语法错误:
Agent.start_link(fn -> %{} end, name: :tmp_storage
end)
Run Code Online (Sandbox Code Playgroud)
那应该是:
Agent.start_link(fn -> %{} end, name: :tmp_storage)
Run Code Online (Sandbox Code Playgroud)
您收到的警告是因为您的代码尝试执行以下操作:
def show do
IO.puts x
end
Run Code Online (Sandbox Code Playgroud)
Elixir和阅读该代码的任何人都会问:"x到底是什么?" 变量x永远不会在任何地方赋值,因此变量x不存在,并且您不能输出不存在的内容.你在这做同样的事情:
with {:ok, _} <- Storage.set(key, value) do
send_resp(conn, 200, "getting the value")
else
_->
send_resp(conn, 404, "nothing")
end
Run Code Online (Sandbox Code Playgroud)
你调用这个函数:
Storage.set(key, value)
Run Code Online (Sandbox Code Playgroud)
但变量key
并value
没有被分配一个值,而elixir(以及其他任何阅读该代码的人)都在怀疑,"关键和价值究竟是什么?"
这是函数的工作方式:
b.ex:
defmodule MyFuncs do
def show(x, y) do
IO.puts x
IO.puts y
end
end
defmodule MyWeb do
def go do
height = 10
width = 20
MyFuncs.show(height, width)
end
end
Run Code Online (Sandbox Code Playgroud)
在iex中:
~/elixir_programs$ iex b.ex
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> MyWeb.go
10
20
:ok
iex(2)>
Run Code Online (Sandbox Code Playgroud)
所以,在你的代码中你需要写这样的东西:
post "/storage/set" do
key = "hello"
value = 10
with {:ok, _} <- Storage.set(key, value) do
send_resp(conn, 200, "Server saved the key and value.")
else
_->
send_resp(conn, 404, "nothing")
end
end
Run Code Online (Sandbox Code Playgroud)
但是,这将为每个发布请求存储相同的键/值.据推测,您希望存储发布请求正文中发送的任何内容.你知道get请求和post请求之间的区别吗?get请求将数据粘贴到url的末尾,而post请求在"请求的主体"中发送数据,因此根据请求的类型提取数据有不同的过程.
你在读什么教程?本教程:https://www.jungledisk.com/blog/2018/03/19/tutorial-a-simple-http-server-in-elixir/,向您展示如何从帖子请求的正文中提取数据.post请求正文中的数据只是一个字符串.如果字符串是JSON格式,那么您可以使用字符串将字符串转换为elixir映射Poison.decode!()
,这将允许您轻松提取与您感兴趣的键相关联的值.例如:
post "/storage/set" do
{:ok, body_string, conn} = read_body(conn)
body_map = Poison.decode!(body_string)
IO.inspect(body_map) #This outputs to terminal window where server is running
message = get_in(body_map, ["message"])
send_resp(
conn,
201,
"Server received: #{message}\n"
)
end
Run Code Online (Sandbox Code Playgroud)
然后,您可以在另一个终端窗口中使用以下curl命令向该路由发送发布请求:
$ curl -v -H 'Content-Type: application/json' "http://localhost:8085/storage/set" -d '{"message": "hello world" }'
Run Code Online (Sandbox Code Playgroud)
(-v
=>详细输出,-H
=>请求标头,-d
=>数据)
现在,根据我上面说的代码错误,你应该想知道这一行:
{:ok, body_string, conn} = read_body(conn)
Run Code Online (Sandbox Code Playgroud)
该行称:
read_body(conn)
Run Code Online (Sandbox Code Playgroud)
但是变量conn
没有在任何地方赋值.但是,Plug会无形地创建conn变量并为其赋值.
以下是使用Agent存储发布请求数据的完整示例(按照上面链接的教程):
simple_server
config/
lib/
simple_server/
application.ex
router.ex
storage.ex
test/
Run Code Online (Sandbox Code Playgroud)
elixir约定是在目录中有一个lib/
与项目同名的目录,在本例中是simple_server,然后为模块定义反映目录结构的名称.因此,在router.ex中,您将定义一个名为SimpleServer.Router的模块,在storage.ex中,您将定义一个名为SimpleServer.Storage的模块.但是,.
模块名称对elixir没有任何特殊意义,因此如果您决定F.R.O.G.S
在文件中命名模块,则不会出错lib/rocks.ex
- 并且您的代码可以正常工作.
router.ex:
defmodule SimpleServer.Router do
use Plug.Router
use Plug.Debugger
require Logger
plug(Plug.Logger, log: :debug)
plug(:match)
plug(:dispatch)
get "/storage/:key" do
resp_msg = case SimpleServer.Storage.get(key) do
nil -> "The key #{key} doesn't exist!\n"
val -> "The key #{key} has value #{val}.\n"
end
send_resp(conn, 200, resp_msg)
end
post "/storage/set" do
{:ok, body_string, conn} = read_body(conn)
body_map = Poison.decode!(body_string)
IO.inspect(body_map) #This outputs to terminal window where server is running
Enum.each(
body_map,
fn {key, val} -> SimpleServer.Storage.set(key,val) end
)
send_resp(
conn,
201,
"Server stored all key-value pairs\n"
)
end
match _ do
send_resp(conn, 404, "not found")
end
end
Run Code Online (Sandbox Code Playgroud)
在上面的代码中要注意的第一件事是路线:
get "/storage/:key" do
Run Code Online (Sandbox Code Playgroud)
这将匹配如下路径:
/storage/x
Run Code Online (Sandbox Code Playgroud)
和plug将创建一个名为key的变量,并为其赋值"x",如下所示:
key = "x"
Run Code Online (Sandbox Code Playgroud)
另外,请注意当您调用函数时:
width = 10
height = 20
show(width, height)
Run Code Online (Sandbox Code Playgroud)
elixir查看函数定义:
def show(x, y) do
IO.puts x
IO.puts y
end
Run Code Online (Sandbox Code Playgroud)
并将函数调用与def匹配如下:
show(width, height)
| |
V V
def show( x , y) do
...
end
Run Code Online (Sandbox Code Playgroud)
并执行任务:
x = width
y = height
Run Code Online (Sandbox Code Playgroud)
然后,在函数内部,您可以使用x和y变量.在这一行:
Enum.each(
body_map,
# | | | | |
# V V V V V
fn {key, val} -> SimpleServer.Storage.set(key,val) end
)
Run Code Online (Sandbox Code Playgroud)
药剂将调用匿名函数传递值key
和val
,就像这样:
func("x", "10")
Run Code Online (Sandbox Code Playgroud)
因此,在匿名函数的主体中,您可以使用变量key
和val
:
SimpleServer.Storage.set(key,val)
Run Code Online (Sandbox Code Playgroud)
因为变量key
和val
已经分配的值.
storage.ex:
defmodule SimpleServer.Storage do
use Agent
def start_link(_args) do #<*** Note the change here
Agent.start_link(fn -> %{} end, name: :tmp_storage)
end
def set(key, value) do
Agent.update(
:tmp_storage,
fn(map) -> Map.put_new(map, key, value) end
)
end
def get(key) do
Agent.get(
:tmp_storage,
fn(map) -> Map.get(map, key) end
)
end
end
Run Code Online (Sandbox Code Playgroud)
application.ex:
defmodule SimpleServer.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
# List all child processes to be supervised
children = [
Plug.Adapters.Cowboy.child_spec(scheme: :http, plug: SimpleServer.Router, options: [port: 8085]),
{SimpleServer.Storage, []}
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: SimpleServer.Supervisor]
Supervisor.start_link(children, opts)
end
end
Run Code Online (Sandbox Code Playgroud)
mix.exs:
defmodule SimpleServer.MixProject do
use Mix.Project
def project do
[
app: :simple_server,
version: "0.1.0",
elixir: "~> 1.6",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger],
mod: {SimpleServer.Application, []}
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:poison, "~> 4.0"},
{:plug_cowboy, "~> 2.0"}
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
]
end
end
Run Code Online (Sandbox Code Playgroud)
请注意,如果您使用本教程中指定的依赖项和版本,您将收到一些警告,包括警告:
~/elixir_programs/simple_server$ iex -S mix
...
...
12:48:57.767 [warn] Setting Ranch options together
with socket options is deprecated. Please use the new
map syntax that allows specifying socket options
separately from other options.
Run Code Online (Sandbox Code Playgroud)
...这是Plug的一个问题.以下是我用来摆脱所有警告的依赖项和版本:
{:poison, "~> 4.0"},
{:plug_cowboy, "~> 2.0"}
Run Code Online (Sandbox Code Playgroud)
此外,当您将应用程序列为依赖项时,您不必再在:extra_applications
列表中输入它.在开始申请之前,Elixir将自动启动列为依赖项的所有应用程序.请参阅:applications v.:extra_applications.
服务器启动后,您可以使用另一个终端窗口发送邮件请求curl
(或者您可以使用其他程序):
~$ curl -v -H 'Content-Type: application/json' "http://localhost:8085/storage/set" -d '{"x": "10", "y": "20" }
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8085 (#0)
> POST /storage/set HTTP/1.1
> Host: localhost:8085
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 23
>
* upload completely sent off: 23 out of 23 bytes
< HTTP/1.1 201 Created
< server: Cowboy
< date: Fri, 30 Nov 2018 19:22:23 GMT
< content-length: 34
< cache-control: max-age=0, private, must-revalidate
<
Server stored all key-value pairs
* Connection #0 to host localhost left intact
Run Code Online (Sandbox Code Playgroud)
这些>
行是请求,<
行是响应.另外,检查运行服务器的终端窗口中的输出.
~$ curl -v http://localhost:8085/storage/z
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8085 (#0)
> GET /storage/z HTTP/1.1
> Host: localhost:8085
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< server: Cowboy
< date: Fri, 30 Nov 2018 19:22:30 GMT
< content-length: 25
< cache-control: max-age=0, private, must-revalidate
<
The key z doesn't exist!
* Connection #0 to host localhost left intact
Run Code Online (Sandbox Code Playgroud)
.
~$ curl -v http://localhost:8085/storage/x
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8085 (#0)
> GET /storage/x HTTP/1.1
> Host: localhost:8085
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< server: Cowboy
< date: Fri, 30 Nov 2018 19:22:37 GMT
< content-length: 24
< cache-control: max-age=0, private, must-revalidate
<
The key x has value 10.
* Connection #0 to host localhost left intact
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
424 次 |
最近记录: |