Skip to content

Commit 381351a

Browse files
committed
Merge pull request astaxie#545 from zgordan-vv/ru
Ru
2 parents 8196f9d + 89e968f commit 381351a

File tree

4 files changed

+279
-8
lines changed

4 files changed

+279
-8
lines changed

ru/04.0.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# 4 Пользовательские формы
2+
3+
Пользовательские формы очень часто используются при разработке веб-приложений. Они дают возможность клиенту и серверу устанавливать коммуникацию друг с другом. Если вы - веб-разработчик, Вы должны быть хорошо знакомы с веб-формами; если Вы - программист на C/C++, Вы можете спросить - что такое пользовательская форма?
4+
5+
Форма - это область, которая содержит элементы формы. Пользователь может вносить информацию в элементы формы, такие как текстовые поля, выпадающие списки, радиокнопки, чекбоксы и т.д. Для определения формы используется тэг `<form>`:
6+
7+
<form>
8+
...
9+
элементы формы, через которые вносится информация
10+
...
11+
</form>
12+
13+
В Go уже есть множество удобных функций для того, чтобы взаимодействовать с пользовательскими формами. Можно легко получить данные из формы в запросе HTTP, они легко интегрируются в Ваши веб-приложения. В разделе 4.1 мы собираемся поговорить о том, как обрабатывать данные из форм в Go. Также, поскольку нельзя доверять любым данным, приходящим со стороны клиента, нужно сначала проверить их перед тем, как использовать. В разделе 4.2 мы поговорим о том, как проверить данные, пришедшие из формы.
14+
15+
Говорится, что HTTP - это протокол без сохранения состояния. Так как проверить, что данные из формы пришли от того же пользователя? И как можно быть уверенным, что одна форма может послать данные за один раз лишь единожды? Мы рассмотрим некоторые детали, касающиеся кук (куки - это информация, сохраняемая на стороне клиента и добавляемая в заголовок запроса, посылаемый на сервер) в разделах 4.3 и 4.4.
16+
17+
Еще один важный случай использования форм - загрузка файлов. В разделе 4.5 Вы научитесь, как это делать, осуществляя контроль размера файла перед тем, как его загрузить, средствами Go.
18+
19+
## Ссылки
20+
21+
- [Содержание](preface.md)
22+
- Предыдущий раздел: [Итоги главы 3](03.5.md)
23+
- Следующий раздел: [Работа с формами](04.1.md)

ru/04.1.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# 4.1 Работа с формами
2+
3+
Перед тем, как начать, давайте посмотрим на простой пример обычной пользовательской формы, сохраненной как `login.gtpl` в папке Вашего проекта:
4+
5+
<html>
6+
<head>
7+
<title></title>
8+
</head>
9+
<body>
10+
<form action="/login" method="post">
11+
Имя пользователя:<input type="text" name="username">
12+
Пароль:<input type="password" name="password">
13+
<input type="submit" value="Войти">
14+
</form>
15+
</body>
16+
</html>
17+
18+
Эта форма отправит данные по адресу `/login` на сервер. После того, как пользователь нажем кнопку "Войти", данные будут посланы на хэндлер `login`, зарегистрированный маршрутизатором сервера. Нам нужно знать, какой метод используется при этом - POST или GET?
19+
20+
Это легко узнать при помощи пакета `http`. Давайте посмотрим, как обработать данные формы со страницы входа:
21+
22+
package main
23+
24+
import (
25+
"fmt"
26+
"html/template"
27+
"log"
28+
"net/http"
29+
"strings"
30+
)
31+
32+
func sayhelloName(w http.ResponseWriter, r *http.Request) {
33+
r.ParseForm() // Анализирует переданные параметры url, затем анализирует пакет ответа для тела POST (тела запроса)
34+
// внимание: без вызова метода ParseForm последующие данные не будут получены
35+
fmt.Println(r.Form) // печатает информацию на сервере
36+
fmt.Println("Путь: ", r.URL.Path)
37+
fmt.Println("Схема: ", r.URL.Scheme)
38+
fmt.Println(r.Form["url_long"])
39+
for k, v := range r.Form {
40+
fmt.Println("Ключ: ", k)
41+
fmt.Println("Значение: ", strings.Join(v, ""))
42+
}
43+
fmt.Fprintf(w, "Привет astaxie!") // пишет данные в ответ
44+
}
45+
46+
func login(w http.ResponseWriter, r *http.Request) {
47+
fmt.Println("Метод:", r.Method) // получаем информацию о методе запроса
48+
if r.Method == "GET" {
49+
t, _ := template.ParseFiles("login.gtpl")
50+
t.Execute(w, nil)
51+
} else {
52+
//r.ParseForm()
53+
// логическая часть процесса входа
54+
fmt.Println("Пользователь:", r.Form["username"])
55+
fmt.Println("Пароль:", r.Form["password"])
56+
}
57+
}
58+
59+
func main() {
60+
http.HandleFunc("/", sayhelloName) // устанавливаем правила маршрутизатора
61+
http.HandleFunc("/login", login)
62+
err := http.ListenAndServe(":9090", nil) // устанавливаем порт для прослушивания
63+
if err != nil {
64+
log.Fatal("ListenAndServe: ", err)
65+
}
66+
}
67+
68+
69+
Здесь мы используем `r.Method` для того, чтобы получить информацию о методе запроса, и нам возвращается метод HTTP -"GET", "POST", "PUT" и т.д.
70+
71+
В функции `login` мы использовали метод `r.Method`, чтобы проверить, что это - страница входа или логика обработки информации о входе. Другими словами, мы проверяем, открыл ли пользователь страницу или уже заполнил форму и пытается войти. Сервер отображает страницу только в случае, если запрос идет посредством метода GET, и запускает логику обработки информации о входе, если запрос использует метод POST.
72+
73+
Вы должны увидеть следующий интерфейс после открытия `http://127.0.0.1:9090/login` в браузере:
74+
75+
![](images/4.1.login.png?raw=true)
76+
77+
Рисунок 4.1 Интерфейс входа пользователя
78+
79+
Сервер ничего не напечатает, пока мы не введем имя пользователя и пароль, потому что хэндлер не будет анализировать данные, пока мы не вызовем `r.ParseForm()`. Давайте добавим `r.ParseForm()` перед `fmt.Println("username:", r.Form["username"])`, скомпилируем программу и вновь протестируем её. Вы обнаружите, что в этом случае информация стала печататься на стороне сервера.
80+
81+
`r.Form` содержит все аргументы запроса, например, строку запроса в URL и данные в POST и PUT. Если в данных есть конфликты, например, параметры имеют одинаковое имя, сервер сохранит данные в срез из множества значений. Документация Go говорит о том, что Go сохраняет данные из запросов GET и POST в разных местах.
82+
83+
Попробуйте изменить значение поля 'action' в форме `http://127.0.0.1:9090/login` на `http://127.0.0.1:9090/login?username=astaxie` в файле `login.gtpl`, протестируйте работу формы, и Вы увидите, что на стороне сервера вывелся срез значений:
84+
85+
![](images/4.1.slice.png?raw=true)
86+
87+
Рисунок 4.2 Сервер печатает данные запроса
88+
89+
Тип поля `request.Form` - это `url.Value`. Данные в нем сохраняются в формате `ключ=значение`.
90+
91+
v := url.Values{}
92+
v.Set("name", "Ava")
93+
v.Add("friend", "Jess")
94+
v.Add("friend", "Sarah")
95+
v.Add("friend", "Zoe")
96+
// v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe"
97+
fmt.Println(v.Get("name"))
98+
fmt.Println(v.Get("friend"))
99+
fmt.Println(v["friend"])
100+
101+
**Совет:** Запросы могут получать доступ к данным формы с использованием метода `FormValue()`. Например, можно вместо `r.Form["username"]` использовать `r.FormValue("username")`, и Go вызовет `r.ParseForm` автоматически. Отметим, что в случае наличия нескольких аргументов с одним и тем же именем этот метод вернет первое из значений, а в случае отсутствия такого аргумента вернет пустую строку.
102+
103+
## Ссылки
104+
105+
- [Содержание](preface.md)
106+
- Предыдущий раздел: [Пользовательские формы](04.0.md)
107+
- Следующий раздел: [Проверка введенных данных](04.2.md)

ru/04.2.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# 4.2 Verification of inputs
2+
3+
One of the most important principles in web development is that you cannot trust anything from client side user forms. You have to verify all incoming data before use it. Many websites are affected by this problem, which is simple yet crucial.
4+
5+
There are two ways of verify form data that are commonly used. One is JavaScript verification in the front-end, and the other is server verification in the back-end. In this section, we are going to talk about server side verification in web development.
6+
7+
## Required fields
8+
9+
Sometimes we require that users input some fields but they don't, for example in the previous section when we required a username. You can use the `len` function to get the length of a field in order to ensure that users have entered this information.
10+
11+
if len(r.Form["username"][0])==0{
12+
// code for empty field
13+
}
14+
15+
`r.Form` treats different form element types differently when they are blank. For empty textboxes, text areas and file uploads, it returns an empty string; for radio buttons and check boxes, it doesn't even create the corresponding items. Instead, you will get errors if you try to access it. Therefore, it's safer to use `r.Form.Get()` to get field values since it will always return empty if the value does not exist. On the other hand, `r.Form.Get()` can only get one field value at a time, so you need to use `r.Form` to get the map of values.
16+
17+
## Numbers
18+
19+
Sometimes you only need numbers for the field value. For example, let's say that you require the age of a user in integer form only, i.e 50 or 10, instead of "old enough" or "young man". If we require a positive number, we can convert the value to the `int` type first, then process it.
20+
21+
getint,err:=strconv.Atoi(r.Form.Get("age"))
22+
if err!=nil{
23+
// error occurs when convert to number, it may not a number
24+
}
25+
26+
// check range of number
27+
if getint >100 {
28+
// too big
29+
}
30+
31+
Another way to do this is using regular expressions.
32+
33+
if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m {
34+
return false
35+
}
36+
37+
For high performance purposes, regular expressions are not efficient, however simple regular expressions are usually fast enough. If you are familiar with regular expressions, it's a very convenient way to verify data. Notice that Go uses [RE2](http://code.google.com/p/re2/wiki/Syntax), so all UTF-8 characters are supported.
38+
39+
## Chinese
40+
41+
Sometimes we need users to input their Chinese names and we have to verify that they all use Chinese rather than random characters. For Chinese verification, regular expressions are the only way.
42+
43+
if m, _ := regexp.MatchString("^[\\x{4e00}-\\x{9fa5}]+$", r.Form.Get("realname")); !m {
44+
return false
45+
}
46+
47+
## English letters
48+
49+
Sometimes we need users to input only English letters. For example, we require someone's English name, like astaxie instead of asta谢. We can easily use regular expressions to perform our verification.
50+
51+
if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m {
52+
return false
53+
}
54+
55+
## E-mail address
56+
57+
If you want to know whether users have entered valid E-mail addresses, you can use the following regular expression:
58+
59+
if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, r.Form.Get("email")); !m {
60+
fmt.Println("no")
61+
}else{
62+
fmt.Println("yes")
63+
}
64+
65+
## Drop down list
66+
67+
Let's say we require an item from our drop down list, but instead we get a value fabricated by hackers. How do we prevent this from happening?
68+
69+
Suppose we have the following `<select>`:
70+
71+
<select name="fruit">
72+
<option value="apple">apple</option>
73+
<option value="pear">pear</option>
74+
<option value="banana">banana</option>
75+
</select>
76+
77+
We can use the following strategy to sanitize our input:
78+
79+
slice:=[]string{"apple","pear","banana"}
80+
81+
for _, v := range slice {
82+
if v == r.Form.Get("fruit") {
83+
return true
84+
}
85+
}
86+
return false
87+
88+
All the functions I've shown above are in my open source project for operating on slices and maps: [https://github.com/astaxie/beeku](https://github.com/astaxie/beeku)
89+
90+
## Radio buttons
91+
92+
If we want to know whether the user is male or female, we may use a radio button, returning 1 for male and 2 for female. However, some little kid who just read his first book on HTTP, decides to send to you a 3. Will your program have have exception? As you can see, we need to use the same method as we did for our drop down list to make sure that only expected values are returned by our radio button.
93+
94+
<input type="radio" name="gender" value="1">Male
95+
<input type="radio" name="gender" value="2">Female
96+
97+
And we use following code to verify the input:
98+
99+
slice:=[]int{1,2}
100+
101+
for _, v := range slice {
102+
if v == r.Form.Get("gender") {
103+
return true
104+
}
105+
}
106+
return false
107+
108+
## Check boxes
109+
110+
Suppose there are some check boxes for user interests, and that you don't want extraneous values here either.
111+
112+
<input type="checkbox" name="interest" value="football">Football
113+
<input type="checkbox" name="interest" value="basketball">Basketball
114+
<input type="checkbox" name="interest" value="tennis">Tennis
115+
116+
In this case, the sanitization is a little bit different than verifying the button and check box inputs since here we get a slice from the check boxes.
117+
118+
slice:=[]string{"football","basketball","tennis"}
119+
a:=Slice_diff(r.Form["interest"],slice)
120+
if a == nil{
121+
return true
122+
}
123+
124+
return false
125+
126+
## Date and time
127+
128+
Suppose you want users to input valid dates or times. Go has the `time` package for converting year, month and day to their corresponding times. After that, it's easy to check it.
129+
130+
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
131+
fmt.Printf("Go launched at %s\n", t.Local())
132+
133+
After you have the time, you can use the `time` package for more operations, depending on your needs.
134+
135+
In this section, we've discussed some common methods for verifying form data server side. I hope that you now understand more about data verification in Go, especially how to use regular expressions to your advantage.
136+
137+
## Links
138+
139+
- [Directory](preface.md)
140+
- Previous section: [Process form inputs](04.1.md)
141+
- Next section: [Cross site scripting](04.3.md)

ru/preface.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818
- 3.2. [Build a simple web server](03.2.md)
1919
- 3.3. [How Go works with web](03.3.md)
2020
- 3.4. [Get into http package](03.4.md)
21-
- 3.5. [Summary](03.5.md)
22-
- 4.[User form](04.0.md)
23-
- 4.1. [Process form inputs](04.1.md)
24-
- 4.2. [Verification of inputs](04.2.md)
25-
- 4.3. [Cross site scripting](04.3.md)
26-
- 4.4. [Duplicate submissions](04.4.md)
27-
- 4.5. [File upload](04.5.md)
28-
- 4.6. [Summary](04.6.md)
21+
- 3.5. [Итоги раздела](03.5.md)
22+
- 4.[Пользовательские формы](04.0.md)
23+
- 4.1. [Работа с формами](04.1.md)
24+
- 4.2. [Проверка введенных данных](04.2.md)
25+
- 4.3. [Межсайтовый скриптинг](04.3.md)
26+
- 4.4. [Дублирование отправки](04.4.md)
27+
- 4.5. [Загрузка файлов](04.5.md)
28+
- 4.6. [Итоги раздела](04.6.md)
2929
- 5.[Database](05.0.md)
3030
- 5.1. [database/sql interface](05.1.md)
3131
- 5.2. [MySQL](05.2.md)

0 commit comments

Comments
 (0)