Я чрезвычайно новичок в программировании и в эликсире.
Я не думаю, что было бы разумно начинать обучение программированию с эликсира.Я бы начал с python или ruby, а затем через год или два я бы попробовал эликсир.
Первое, что вам нужно выучить, это как разместить код.Ищите в Google информацию о том, как разместить код в stackoverflow.Затем вы должны выровнять все отступы.Вы используете текстовый редактор компьютерного программирования?Если нет, то вы должны получить один.Есть много бесплатных.Я использую vim, который устанавливается на Unix как компьютеры.Вы можете узнать, как использовать vim, набрав vimtutor
в окне терминала.
Далее, у вас есть синтаксическая ошибка в вашем коде:
Agent.start_link(fn -> %{} end, name: :tmp_storage
end)
Это должно быть:
Agent.start_link(fn -> %{} end, name: :tmp_storage)
Предупреждение, которое вы получили, заключается в том, что ваш код пытается сделать эквивалент:
def show do
IO.puts x
end
Elixir и любой другой, читающий этот код, спросит: "Что за хрень?"Переменной x никогда нигде не назначается значение, и поэтому переменная x не существует, и вы не можете вывести то, что не существует.Вы делаете то же самое здесь:
with {:ok, _} <- Storage.set(key, value) do
send_resp(conn, 200, "getting the value")
else
_->
send_resp(conn, 404, "nothing")
end
Вы вызываете функцию:
Storage.set(key, value)
, но переменным key
и value
никогда не присваивалось значение, и эликсир (икто-нибудь еще, читающий этот код), задается вопросом: «Какого черта ключ и значение?»
Так работают функции:
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
В 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)>
Итак, в вашем коде вам нужно написать что-то вроде этого:
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
Однако это будет хранить один и тот же ключ / значение для каждого запроса на публикацию.Предположительно, вы хотите хранить все, что отправлено в теле почтового запроса.Знаете ли вы разницу между запросом на получение и запросом по почте?Запрос get прикрепляет данные в конец URL, а запрос post отправляет данные в «теле запроса», поэтому существуют разные процедуры для извлечения данных в зависимости от типа запроса.
Какой учебник ты читаешь?В этом руководстве: https://www.jungledisk.com/blog/2018/03/19/tutorial-a-simple-http-server-in-elixir/, показано, как извлечь данные из тела почтового запроса.Данные в теле почтового запроса - это просто строка.Если строка в формате JSON, вы можете преобразовать строку в карту эликсира, используя 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
Затем вы можете использовать следующую команду curl в другом окне терминала для отправки запроса на публикацию по этому маршруту:
$ curl -v -H 'Content-Type: application/json' "http://localhost:8085/storage/set" -d '{"message": "hello world" }'
(-v
=> подробный вывод, -H
=> заголовок запроса, -d
=> data)
Теперь, основываясь на том, что я сказал неправильно с вашим кодом выше, вы должны задаться вопросом об этой строке:
{:ok, body_string, conn} = read_body(conn)
Эта строка вызывает:
read_body(conn)
, но переменной conn
нигде не присвоено значение.Тем не менее, Plug незаметно создает переменную conn и присваивает ей значение.
Вот полный пример использования агента для хранения данных запроса поста (в соответствии с указанным выше учебником):
simple_server
config/
lib/
simple_server/
application.ex
router.ex
storage.ex
test/
Соглашение с эликсиром - иметь каталог в каталоге 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
Первое, на что нужно обратить внимание в приведенном выше коде, это маршрут:
get "/storage/:key" do
, который будет соответствовать пути, например:
/storage/x
и plug создаст переменную с именем key и присвоит ей значение «x», например:
key = "x"
Также обратите внимание, что при вызове функции:
width = 10
height = 20
show(width, height)
эликсир просматривает определение функции:
def show(x, y) do
IO.puts x
IO.puts y
end
и сопоставляет вызов функции с определением следующим образом:
show(width, height)
| |
V V
def show( x , y) do
...
end
и выполняет назначения:
x = width
y = height
Затем внутри функции вы можете использовать переменные x и y.В этой строке:
Enum.each(
body_map,
# | | | | |
# V V V V V
fn {key, val} -> SimpleServer.Storage.set(key,val) end
)
Elixir будет вызывать анонимную функцию, передавая значения для key
и val
, например:
func("x", "10")
Следовательно, в теле анонимногоВы можете использовать переменные key
и val
:
SimpleServer.Storage.set(key,val)
, поскольку переменным 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
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
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
Обратите внимание: если вы используете зависимости и версии, указанные в руководстве, вы будетеполучите несколько предупреждений, включая предупреждение:
~/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.
..., которое является проблемой с Plug.Вот зависимости и версии, которые я использовал, чтобы избавиться от всех предупреждений:
{:poison, "~> 4.0"},
{:plug_cowboy, "~> 2.0"}
Кроме того, когда вы указываете приложение в качестве зависимости, вам больше не нужно вводить его в список :extra_applications
,Elixir автоматически запустит все приложения, перечисленные как зависимости, перед запуском приложения.См. : приложения 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
>
строки - это запрос, а <
- ответ.Также проверьте вывод в окне терминала, где работает сервер.
~$ 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
.
~$ 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