Новости

Клієнт-сервер

  1. Web-сервери і HTTP (для початківців)
  2. Приклад запиту / відповіді GET
  3. запит
  4. відповідь
  5. Приклад запиту / відповіді POST
  6. запит
  7. відповідь
  8. статичні сайти
  9. динамічні сайти
  10. Анатомія динамічного запиту
  11. Виконання іншої роботи
  12. Повернення чогось Іншого, кроме HTML
  13. Веб-структури спрощують веб-програмування на стороні сервера
  14. резюме

Тепер, коли ви знаєте мета і потенційні переваги програмування на стороні сервера, ми детально розглянемо, що відбувається, коли сервер отримує «динамічний запит» від браузера. Оскільки велика частина серверного коду веб-сайту обробляє запити і відповіді аналогічним чином, це допоможе вам зрозуміти, що потрібно робити при написанні здебільшого власного коду.

Перед стартом: Базова комп'ютерна грамотність. Базове розуміння того, що таке веб-сервер. Мета: Вивчити взаємодію між клієнтом і сервером на динамічному веб-сайті та зокрема дізнатися, які дії потрібно зробити в коді серверної частини.

В обговоренні немає реального коду, оскільки ми ще не вибрали, який веб-фреймворк ми будемо використовувати для написання нашого коду! Це обговорення, проте, дуже актуально, оскільки описана поведінка має бути реалізовано вашим серверним кодом незалежно від того, яку мову програмування або веб-фреймворк ви вибираєте.

Web-сервери і HTTP (для початківців)

Веб-браузери взаємодіють з веб-серверами за допомогою протоколу передачі гіпертексту (HTTP). Коли ви натискаєте на посилання на сторінці, заповнюєте форму або запускаєте пошук, браузер відправляє на сервер HTTP-запит.

Цей запит включає:

  • Шлях, який визначає цільової сервер і ресурс (наприклад, файл, певна точка даних на сервері, що запускається сервіс, і т.д.).
  • Метод, який визначає необхідну дію (наприклад, отримати файл, зберегти або відновити деякі дані). Різні методи / команди і пов'язані з ними дії перераховані нижче:
    • GET - отримати певний ресурс (наприклад, HTML-файл, що містить інформацію про товар або список товарів).

    • POST - створити новий ресурс (наприклад, нову статтю на вікі, додати новий контакт в базу даних).
    • HEAD - отримати метадані про певний ресурсі без отримання змісту, як робить GET. Ви, наприклад, можете використовувати запит HEAD, щоб дізнатися, коли в останній раз ресурс оновлювався і тільки потім використовувати (більш «витратний») запит GET, щоб завантажити ресурс, який був змінений.
    • PUT - оновити існуючий ресурс (або створити новий, якщо такої не існує).
    • DELETE - видалити вказаний ресурс.
    • TRACE, OPTIONS, CONNECT, PATCH - ці команди використовуються для менш популярних / просунутих завдань, тому ми їх не будемо розглядати.
  • Додаткова інформація може бути закодована в запиті (наприклад, дані форми). Інформація може бути закодована як:
    • Параметри URL: GET запити зашифровують дані в URL-адресу, що відправляється на сервер, додаючи пари ім'я / значення в його кінець, наприклад, http://mysite.com? Name = Fred & age = 11. У вас завжди є знак питання (?), Що відокремлює іншу частину URL-адреси від параметрів URL-адреси, знак рівності (=), що відокремлює кожне ім'я від відповідного йому значення, і амперсанд (&), що розділяє пари. URL-параметри за своєю суттю «небезпечні», так як вони можуть бути змінені користувачами і потім відправлені заново. В результаті параметри / GET запити не використовуються для запитів, які оновлюють дані на сервері.
    • POST дані. POST запити додають нові ресурси, дані яких зашифровані в тілі запиту.
    • Кукі-файли клієнтської частини. Кукі-файли містять дані сесій про клієнта, включаючи ключові слова, які сервер може використовувати для визначення його авторизаційний статус і права доступу до ресурсів.

Веб-сервери очікують повідомлень із запитами від клієнтів, обробляють їх, коли вони приходять і відповідають веб-браузеру через повідомлення з HTTP-відповіддю. відповідь містить Код статусу HTTP-відповіді , Який показує, чи був запит успішним (наприклад, «200 OK» означає успіх, «404 Not Found» якщо ресурс не може бути знайдений, «403 Forbidden», якщо користувач не має права переглядати ресурс, і т.д.). Тіло успішної відповіді на запит GET буде містити запитуваний ресурс.

Після того, як HTML сторінка була повернута, вона обробляється браузером. Далі браузер може досліджувати посилання на інші ресурси (наприклад, HTML сторінка зазвичай використовує JavaScript і CSS файли), та надіслати окремий HTTP запит на завантаження цих файлів.

Як статичні, так і динамічні веб-сайти (обговорювані в наступних розділах) використовують такий самий протокол / шаблони зв'язку.

Приклад запиту / відповіді GET

Ви можете сформувати простий GET запит клікнувши по посилання або через пошук по сайту (наприклад, сторінка механізму пошуку). Наприклад, HTTP запит, надісланий під час виконання запиту "client server overview" на сайті MDN, буде багато в чому схожий на текст нижче (він не буде ідентичним, бо частини повідомлення залежать від Вашого браузера \ налаштувань.

Формат HTTP повідомлення визначено в веб-стандарті ( RFC7230 ) .Вам не потрібно знати цей рівень деталізації, але, по крайней мере, тепер Ви знаєте звідки це з'явилося!

запит

Кожен рядок запиту містить інформацію про запит. Перша частина називається заголовок, він містить важливу інформацію про запит, точно так само як HTML head містить важливу інформацію про HTML документі (але не вміст документа, яке розташоване в body):

GET https://developer.mozilla.org/en-US/search?q=client+server+overview&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev HTTP / 1.1 Host: developer.mozilla.org Connection: keep- alive Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla / 5.0 (Windows NT 10.0; WOW64) AppleWebKit / 537.36 (KHTML, like Gecko) Chrome / 52.0.2743.116 Safari / 537.36 Accept: text / html, application / xhtml + xml, application / xml; q = 0.9, image / webp, * / *; q = 0.8 Referer: https://developer.mozilla.org/en-US/ Accept- Encoding: gzip, deflate, sdch, br Accept-Language: en-US, en; q = 0.8, es; q = 0.6 Cookie: sessionid = 6ynxs23n521lu21b1t136rhbv7ezngie; csrftoken = zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit = False; dwf_sg_task_completion = False; _gat = 1; _ga = GA1.2.1688886003.1471911953; ffo = true

Перша і друга рядки містять велику частину інформації, про яку говорилося вище:

  • Тип запиту (GET).
  • URL цільового ресурсу (/ en-US / search).
  • Параметри URL (q = client% 2Bserver% 2Boverview & topic = apps & topic = html & topic = css & topic = js & topic = api & topic = webdev).
  • Цільовий вебсайт (developer.mozilla.org).
  • Кінець першого рядка так само містить коротку рядок, що ідентифікує версію протоколу (HTTP / 1.1).

Останній рядок містить інформацію про клієнтських куки - в даному випадку можна побачити куки, які включають id для управління сесіями (Cookie: sessionid = 6ynxs23n521lu21b1t136rhbv7ezngie; ...).

Решта рядки містять інформацію про використовуваному браузері і про його деяких підтримуваних можливостях Наприклад, тут Ви можете побачити:

  • Мій браузер (User-Agent) Mozilla Firefox (Mozilla / 5.0).
  • Він може приймати інформацію упаковану gzip (Accept-Encoding: gzip).
  • Він може приймати зазначені кодування (Accept-Charset: ISO-8859-1, UTF-8; q = 0.7, *; q = 0.7) і мов (Accept-Language: de, en; q = 0.7, en-us; q = 0.3).
  • Рядок Referer ідентифікує адресу веб сторінки, що мiстить посилання на цей ресурс (тобто джерело оригінального запиту, https://developer.mozilla.org/en-US/).

HTTP запит так само може містити body, але в даному випадку воно порожнє.

відповідь

Перша частина відповіді на запит показана нижче. Тема містить інформацію, зазначену нижче:

  • Перший рядок містить код відповіді 200 OK, який свідчить про те, що запит виконаний успішно.
  • Ми можемо бачити, що відповідь має text / html формат (Content-Type).
  • Так само ми бачимо, що відповідь використовує кодування UTF-8 (Content-Type: text / html; charset = utf-8).
  • Тема так само містить розмір відповіді (Content-Length: 41823).

В кінці повідомлення ми бачимо вміст body, що містить HTML код повертається відповіді.

HTTP / 1.1 200 OK Server: Apache X-Backend-Server: developer1.webapp.scl3.mozilla.com Vary: Accept, Cookie, Accept-Encoding Content-Type: text / html; charset = utf-8 Date: Wed, 07 Sep 2016 00:11:31 GMT Keep-Alive: timeout = 5, max = 999 Connection: Keep-Alive X-Frame-Options: DENY Allow: GET X-Cache-Info: caching Content-Length: 41823 <! DOCTYPE html> <html lang = "en-US" dir = "ltr" class = "redesign no-js" data-ffo-opensanslight = false data-ffo-opensans = false> <head prefix = "og: http://ogp.me/ns#"> <meta charset = "utf-8"> <meta http-equiv = "X-UA-Compatible" content = "IE = Edge"> <script > (function (d) {d.className = d.className.replace (/ \ bno-js /, '');}) (document.documentElement); </ script> ...

Інша частина заголовка відповіді містить інформацію про відповідь (наприклад, коли він був згенерований), сервері і про те, як він очікує, що браузер обробляє сторінку (наприклад, рядок X-Frame-Options: DENY говорить браузеру не допускати впровадження цієї сторінки, якщо вона буде впроваджена в <Iframe> на іншому сайті).

Приклад запиту / відповіді POST

HTTP POST створюється, коли ви відправляєте форму, яка містить інформацію, яка повинна бути збережена на сервері.

запит

У наведеному нижче тексті показаний HTTP-запит, зроблений, коли користувач представляє нові дані профілю на цьому сайті. Формат запиту майже такий же, як приклад запиту GET, показаний раніше, хоча перший рядок ідентифікує цей запит як POST.

POST https://developer.mozilla.org/en-US/profiles/hamishwillee/edit HTTP / 1.1 Host: developer.mozilla.org Connection: keep-alive Content-Length: 432 Pragma: no-cache Cache-Control: no -cache Origin: https://developer.mozilla.org Upgrade-Insecure-Requests: 1 User-Agent: Mozilla / 5.0 (Windows NT 10.0; WOW64) AppleWebKit / 537.36 (KHTML, like Gecko) Chrome / 52.0.2743.116 Safari / 537.36 Content-Type: application / x-www-form-urlencoded Accept: text / html, application / xhtml + xml, application / xml; q = 0.9, image / webp, * / *; q = 0.8 Referer: https: / /developer.mozilla.org/en-US/profiles/hamishwillee/edit Accept-Encoding: gzip, deflate, br Accept-Language: en-US, en; q = 0.8, es; q = 0.6 Cookie: sessionid = 6ynxs23n521lu21b1t136rhbv7ezngie; _gat = 1; csrftoken = zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit = False; dwf_sg_task_completion = False; _ga = GA1.2.1688886003.1471911953; ffo = true csrfmiddlewaretoken = zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT & user-username = hamishwillee & user-fullname = Hamish + Willee & user-title = & user-organization = & user-location = Australia & user-locale = en-US & user-timezone = Australia% 2FMelbourne & user-irc_nickname = & user-interests = & user-expertise = & user-twitter_url = & user-stackoverflow_url = & user-linkedin_url = & user-mozillians_url = & user-facebook_url =

Основна відмінність полягає в тому, що URL-адреса не має параметрів. Як ви можете бачити, інформація з форми закодована в тілі запиту (наприклад, нове повне ім'я користувача встановлюється з використанням: & user-fullname = Hamish + Willee).

відповідь

Відповідь від запиту показаний нижче. Код стану «302 Found» повідомляє браузеру, що повідомлення вдалося, і що він повинен видати другий HTTP-запит для завантаження сторінки, зазначеної в поле «Місце». В іншому випадку інформація аналогічна інформації для відповіді на запит GET.

HTTP / 1.1 302 FOUND Server: Apache X-Backend-Server: developer3.webapp.scl3.mozilla.com Vary: Cookie Vary: Accept-Encoding Content-Type: text / html; charset = utf-8 Date: Wed, 07 Sep 2016 00:38:13 GMT Location: https://developer.mozilla.org/en-US/profiles/hamishwillee Keep-Alive: timeout = 5, max = 1000 Connection: Keep-Alive X-Frame-Options: DENY X-Cache-Info: not cacheable; request was not a GET or HEAD Content-Length: 0

Note: HTTP-відповіді і запити, показані в цих прикладах, були захоплені за допомогою програми Fiddler , Але ви можете отримати аналогічну інформацію за допомогою веб-сніфферов (наприклад, http://web-sniffer.net/ ) Або за допомогою розширень браузера, таких як HttpFox. Ви можете спробувати це самі. Використовуйте будь-який з пов'язаних інструментів, а потім перейдіть по сайту та введіть інформацію профілю, щоб побачити різні запити і відповіді. У більшості сучасних браузерів також є інструменти, які відстежують мережеві запити (наприклад, інструмент Network Monitor в Firefox).

статичні сайти

Статичний сайт - це той, який повертає той же жорсткий кодований контент з сервера щоразу, коли запитується конкретний ресурс. Наприклад, якщо у вас є сторінка про продукт в /static/myproduct1.html, ця ж сторінка буде повернута кожному користувачеві. Якщо ви додасте ще один подібний продукт на свій сайт, вам потрібно буде додати ще одну сторінку (наприклад, myproduct2.html) і так далі. Це може стати дійсно неефективним - що відбувається, коли ви потрапляєте на тисячі сторінок продукту? Ви повторювали б багато коду на кожній сторінці (основний шаблон сторінки, структуру і т. Д.), І якби ви хотіли змінити що-небудь про структуру сторінки - наприклад, додати новий розділ «пов'язані продукти» - тоді ви доводиться міняти кожну сторінку окремо.

Note: Статичні сайти чудові, коли у вас невелика кількість сторінок, і ви хочете відправити один і той же контент кожному користувачеві. Однак вони можуть мати значну вартість для підтримки, оскільки кількість сторінок стає більше.

Давайте згадаємо, як це працює, знову глянувши на діаграму архітектури статичного сайту, на яку ми дивилися в останній статті.

Коли користувач хоче перейти на сторінку, браузер відправляє HTTP-запит GET із зазначенням URL-адреси його сторінки HTML. Сервер витягає запитаний документ зі своєї файлової системи і повертає відповідь HTTP, що містить документ, і код стану HTTP Response status code of "200 OK» (успіх). Сервер може повернути інший код стану, наприклад «404 Not Found», якщо його немає на сервері або «301 Moved Permanently», якщо файл існує, але був перенаправлений в інше місце.

Сервера для статичного сайту буде тільки потрібно обробляти запити GET, тому що сервер не зберігає ніяких модифікуються даних. Він також не змінює свої відповіді на основі даних HTTP-запиту (наприклад, параметрів URL-адреси або файлів cookie).

Розуміння того, як працюють статичні сайти, проте корисно при вивченні програмування на стороні сервера, оскільки динамічні сайти обробляють запити на статичні файли (CSS, JavaScript, статичні зображення і т. Д.) Точно так само.

динамічні сайти

Динамічний сайт - це той, який може генерувати і повертати контент на основі конкретного URL-адреси запиту і даних (а не завжди повертати один і той же жорсткий код для певного URL-адреси). Використовуючи приклад сайту продукту, сервер буде зберігати «дані» продукту в базі даних, а не окремі файли HTML. При отриманні запиту HTTP GET для продукту сервер визначає ідентифікатор продукту, отримує дані з бази даних і потім створює HTML-сторінку для відповіді, вставляючи дані в шаблон HTML. Це має великі переваги перед статичним сайтом:

Використання бази даних дозволяє ефективно зберігати інформацію про продукт за допомогою легко розширюється, змінюваного і доступного для пошуку способу.

Використання HTML-шаблонів дозволяє дуже легко змінити структуру HTML, тому що це потрібно робити тільки в одному місці, в одному шаблоні, а не через потенційно тисячі статичних сторінок.

Анатомія динамічного запиту

У цьому розділі представлений покроковий огляд «динамічного» циклу HTTP-запиту і відповіді, грунтуючись на тому, що ми розглянули в останній статті, з набагато більш детальною інформацією. Щоб «зберегти реальність», ми будемо використовувати контекст веб-сайту менеджера спортивної команди, де тренер може вибрати ім'я своєї команди і розмір команди в формі HTML і повернутися до пропонованого «краще складу» для своєї наступної гри.

На наведеній нижче діаграмі показані основні елементи веб-сайту «team coach», а також пронумеровані ярлики для послідовності операцій, коли тренер звертається до списку «кращих команд». Частини сайту, які роблять його динамічним, є веб-додатком (так ми будемо посилатися на серверний код, що обробляє HTTP-запити і повертають відповіді HTTP), базу даних, яка містить інформацію про гравців, командах, тренерів та їх відносини і HTML- шаблони .

Частини сайту, які роблять його динамічним, є веб-додатком (так ми будемо посилатися на серверний код, що обробляє HTTP-запити і повертають відповіді HTTP), базу даних, яка містить інформацію про гравців, командах, тренерів та їх відносини і HTML- шаблони

Після того, як тренер відправить форму з ім'ям команди і кількістю гравців, послідовність операцій:

  1. Веб-браузер створює HTTP-запит GET на сервер з використанням базового URL-адреси ресурсу (/ best) і кодування номера команди і гравця як параметрів URL (наприклад, / best? Team = my_team_name & show = 11) або як частина URL-адреси (наприклад , / best / my_team_name / 11 /). Запит GETіспользуется, тому що запит - тільки вибірка даних (не зміна даних).
  2. Веб-сервер виявляє, що запит є «динамічним» і пересилає його в веб-додаток для обробки (веб-сервер визначає, як обробляти різні URL-адреси на основі правил зіставлення шаблонів, визначених у його конфігурації).
  3. Веб-додаток визначає, що мета запиту полягає в тому, щоб отримати «кращий список команд» на основі URL (/ best /) і дізнатися ім'я команди і кількість гравців з URL-адреси. Потім веб-додаток отримує необхідну інформацію з бази даних (використовуючи додаткові «внутрішні» параметри, щоб визначити, які гравці є «кращими», і, можливо, також отримати особистість зареєстрованого тренера з файлу cookie на стороні клієнта).
  4. Веб-додаток динамічно створює HTML-сторінку, поміщаючи дані (з бази даних) в наповнювачі всередині HTML-шаблону.
  5. Веб-додаток повертає згенерований HTML в веб-браузер (через веб-сервер) разом з кодом стану HTTP 200 ( «успіх»). Якщо що-небудь перешкоджає поверненню HTML, веб-додаток поверне інший код - наприклад, «404», щоб вказати, що команда не існує.
  6. Потім веб-браузер почне обробляти повернутий HTML, відправивши окремі запити, щоб отримати будь-які інші файли CSS або JavaScript, на які він посилається (див. Крок 7).
  7. Веб-сервер завантажує статичні файли з файлової системи і повертає їх безпосередньо в браузер (знову ж, правильна обробка файлів заснована на правилах конфігурації і зіставленні шаблонів URL).

Операція по оновленню записи в базі даних буде оброблятися аналогічним чином, за винятком того, що, як і будь-яке оновлення бази даних, HTTP-запит з браузера повинен бути закодований як запит POST.

Виконання іншої роботи

Завдання веб-додатки - отримувати HTTP-запити і повертати відповіді HTTP. Хоча взаємодія з базою даних для отримання або оновлення інформації є дуже поширеними завданнями, код може робити інші речі одночасно або взагалі не взаємодіяти з базою даних.

Хорошим прикладом додаткової завдання, яку може виконувати веб-додаток, є відправка електронної пошти користувачам для підтвердження їх реєстрації на сайті. Сайт також може виконувати протоколювання або інші операції.

Повернення чогось Іншого, кроме HTML

Серверні код сайту может повертаті НЕ только HTML-фрагменти / файли у ВІДПОВІДІ. Він може динамічно створювати і повертати інші типи файлів (текст, PDF, CSV і т. Д.) Або навіть дані (JSON, XML і т. Д.).

Ідея повернути дані в веб-браузер, щоб він міг динамічно оновлювати свій власний контент ( AJAX ) Існує досить давно. Зовсім недавно «Односторінкові додатки» стали популярними, де весь сайт написаний з одним HTML-файлом, який динамічно оновлюється по мірі необхідності. Веб-сайти, створені з використанням цього стилю додатка, виштовхують безліч обчислювальних витрат з сервера на веб-браузер і можуть привести до того, що веб-сайти, поводяться більше як нативні додатки (дуже чуйні і т. Д.).

Веб-структури спрощують веб-програмування на стороні сервера

Веб-фреймворки на стороні сервера роблять код записи для обробки описаних вище операцій набагато простіше.

Однією з найбільш важливих операцій, які вони виконують, є надання простих механізмів для зіставлення URL-адрес для різних ресурсів / сторінок з конкретними функціями обробника. Це спрощує збереження окремого коду, пов'язаного з кожним типом ресурсу. Він також має переваги в плані обслуговування, оскільки ви можете змінити URL-адреса, що використовується для доставки певної функції в одному місці, без необхідності змінювати функцію обробника.

Наприклад, розглянемо наступний код Django (Python), який відображає два шаблони URL для двох функцій перегляду. Перший шаблон гарантує, що HTTP-запит з URL-адресою ресурсу / bestбудет переданий функції з ім'ям index () в модулі views. Запит, який має шаблон «/ best / junior», замість цього буде переданий функції перегляду junior ().

# File: best / urls.py # from django.conf.urls import url from. import views urlpatterns = [# example: / best / url (r '^ $', views.index), # example: / best / junior / url (r '^ junior / $', views.junior),]

Note: Перші параметри у функціях url () можуть виглядати трохи незвично (наприклад, r '^ junior / $' тому що вони використовують метод зіставлення шаблонів під назвою «регулярні вирази» (RegEx або RE). Вам не потрібно знати, як працюють регулярні вираження на цьому етапі, крім того, що вони дозволяють нам зіставляти шаблони в URL-адресу (а не жорстко закодовані значення вище) і використовувати їх в якості параметрів в наших функціях перегляду. як приклад, дійсно простий RegEx може говорити «відповідати однією заголовною букві, за кото ой йдуть від 4 до 7 малих літер ».

Веб-інфраструктура також спрощує процедуру отримання інформації з бази даних. Структура наших даних визначається в моделях, які є класами Python, які визначають поля, які повинні зберігатися в базовій базі даних. Якщо у нас є модель з ім'ям Team з полем «team_type», ми можемо використовувати простий синтаксис запиту, щоб повернути все команди, які мають певний тип.

У наведеному нижче прикладі представлений список всіх команд, у яких є точний (з урахуванням регістру) team_type «junior» - зверніть увагу на формат: ім'я поля (team_type), за яким слід подвійний знак підкреслення, а потім тип відповідності для використання (в цьому випадку точне). Існує багато інших типів збігів, і ми можемо об'єднати їх. Ми також можемо контролювати порядок і кількість повертаються результатів.

# Best / views.py from django.shortcuts import render from .models import Team def junior (request): list_teams = Team.objects.filter (team_type__exact = "junior") context = { 'list': list_teams} return render (request , 'best / index.html', context)

Після того, як функція junior () отримує список молодших команд, вона викликає функцію junior (), передаючи вихідний HttpRequest, HTML-шаблон і об'єкт «context», що визначає інформацію, яка повинна бути включена в шаблон. Функція render () - це функція зручності, яка генерує HTML з використанням контексту і HTML-шаблону і повертає його в об'єкт HttpResponse.

Очевидно, що веб-фреймворки можуть допомогти вам у вирішенні багатьох інших завдань. У наступній статті ми обговоримо набагато більше переваг і деякі популярні варіанти веб-фреймворків.

резюме

На цьому етапі ви повинні добре ознайомитися з операціями, які повинен виконувати серверний код, і знати деякі способи, за допомогою яких веб-середовище на стороні сервера може зробити це простіше.

У наступному модулі ми допоможемо вам вибрати кращу веб-платформу для вашого першого сайту.

Com?
Org/en-US/search?
Це може стати дійсно неефективним - що відбувається, коли ви потрапляєте на тисячі сторінок продукту?
Наприклад, / best?

Уважаемые партнеры, если Вас заинтересовала наша продукция, мы готовы с Вами сотрудничать. Вам необходимо заполнить эту форму и отправить нам. Наши менеджеры в оперативном режиме обработают Вашу заявку, свяжутся с Вами и ответят на все интересующее Вас вопросы.

Или позвоните нам по телефонам: (048) 823-25-64

Организация (обязательно) *

Адрес доставки

Объем

Как с вами связаться:

Имя

Телефон (обязательно) *

Мобильный телефон

Ваш E-Mail

Дополнительная информация: