Перейти к содержанию

Programming languages

Syntax, libraries, frameworks, algorithms, OOP, functional, asynchronous, multithreaded programming. Help for beginners, expert advice, trends and cases. Solve problems, share code.

4 Темы 4 Сообщения

  • 0 0
    0 Темы
    0 Сообщения
    Нет новых сообщений
  • 0 0
    0 Темы
    0 Сообщения
    Нет новых сообщений
  • 1 Темы
    1 Сообщения
    AladdinA
    TypeScript continues to evolve with powerful features that enhance type safety and developer experience. One such feature introduced in TypeScript 4.9 is the satisfies operator—a versatile tool that bridges the gap between type checking and type preservation. In this article, we’ll explore what makes this operator special and how it can improve your TypeScript code. What is the satisfies Operator? The satisfies operator allows you to verify that an expression matches a particular type while preserving its most specific form. It acts as a guard that ensures type compatibility without altering the inferred type of the expression. Basic Syntax {expression} satisfies Type This syntax checks that expression conforms to Type, but maintains the expression’s original type information. The Problems It Solves 1. Preserving Literal Types Before satisfies, developers often faced a dilemma between type safety and preserving literal types: // Without satisfies const colors = { red: "#FF0000", green: "#00FF00", blue: "#0000FF" } as const; // Type: { readonly red: "#FF0000", readonly green: "#00FF00", readonly blue: "#0000FF" } // With satisfies const colors = { red: "#FF0000", green: "#00FF00", blue: "#0000FF" } satisfies Record<string, string>; // Type preserved: { red: "#FF0000", green: "#00FF00", blue: "#0000FF" } 2. Validating Partial Conformance The operator excels at verifying structural compatibility while maintaining full type information: interface User { id: number; name: string; email?: string; } const user = { id: 1, name: "John", age: 30 } satisfies User; // TypeScript knows all properties: { id: number, name: string, age: number } Practical Use Cases Configuration Validation interface AppConfig { name: string; port: number; environment: "development" | "staging" | "production"; } const config = { name: "My App", port: 3000, environment: "development" // Auto-completion works! } satisfies AppConfig; Color Palettes and Themes type ColorPalette = Record<string, string>; const colors = { primary: "#3498db", secondary: "#2ecc71", accent: "#e74c3c", // Error: Type 'number' is not assignable to type 'string' // warning: 123 satisfies string } satisfies ColorPalette; Discriminated Unions type Shape = | { kind: "circle"; radius: number } | { kind: "rectangle"; width: number; height: number }; const circle = { kind: "circle", radius: 5, } satisfies Shape; // TypeScript knows this is a circle with radius property console.log(circle.radius); // OK Comparative Analysis satisfies vs Type Annotations // Type annotation const obj1: { x: number } = { x: 1, y: 2 }; // Error: Property 'y' does not exist on type '{ x: number; }' // satisfies const obj2 = { x: 1, y: 2 } satisfies { x: number }; // OK: Checks compatibility while preserving full type satisfies vs Type Assertions (as) // Type assertion (potentially unsafe) const obj1 = { x: 1, y: 2 } as { x: number }; // OK, but loses information about 'y' // satisfies const obj2 = { x: 1, y: 2 } satisfies { x: number }; // OK: Verifies compatibility while maintaining complete type information Advanced Patterns Combining with as const const routes = { home: "/", about: "/about", contact: "/contact" } as const satisfies Record<string, string>; // Type: { readonly home: "/", readonly about: "/about", readonly contact: "/contact" } Complex Structure Validation type ResponseShape = | { status: "success"; data: unknown } | { status: "error"; message: string }; const apiResponse = { status: "success", data: { id: 1, name: "John" } } satisfies ResponseShape; // TypeScript knows the exact response structure if (apiResponse.status === "success") { console.log(apiResponse.data); // OK } Limitations and Considerations Not a type annotation replacement: Only verifies compatibility without assigning types Structural compatibility focus: Works with shapes rather than nominal types No type narrowing: Preserves the original expression type exactly Conclusion The satisfies operator represents a significant step forward in TypeScript’s type system, offering a elegant solution for situations where you need to: Verify type compatibility without losing specific type information Maintain literal types while ensuring structural validity Validate partial conformance to interfaces Enhance type safety without compromising flexibility By incorporating satisfies into your TypeScript workflow, you can write more robust, maintainable code that leverages TypeScript’s powerful type system while preserving the precise type information that makes your code more expressive and reliable. As TypeScript continues to evolve, features like satisfies demonstrate the language’s commitment to providing developers with tools that combine type safety with practical flexibility—a combination that makes TypeScript increasingly valuable for projects of any scale.
  • 2 Темы
    2 Сообщения
    hannadevH
    Let’s be real: if your app doesn’t talk to a server, it’s basically a fancy notepad. Whether you’re fetching user data, submitting a form, or syncing preferences across devices—you’ll need to send HTTP requests. And in the JavaScript world, that usually means GET and POST. Good news? Making these requests is easier than ever—thanks to modern APIs like fetch() and battle-tested libraries like Axios. Even better? The same code often works seamlessly on both web browsers and mobile apps (especially if you’re using React Native or hybrid frameworks). So grab your favorite drink, and let’s break this down—no jargon overload, just clear, practical steps. 🧭 Why This Matters Before we dive into syntax, let’s zoom out: GET = “Hey server, give me some data.” (e.g., load a user profile) POST = “Hey server, here’s some data—save it!” (e.g., submit a login form) These are the bread and butter of client-server communication. And JavaScript? It’s the universal translator. Option 1: The Built-in Hero — fetch() Modern browsers (and React Native!) support the Fetch API natively. No extra libraries. No fuss. Making a GET Request fetch('https://api.example.com/users') .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); // Parse JSON response }) .then(data => { console.log('Success:', data); }) .catch(error => { console.error('Error:', error); }); Pro tip: Always check response.ok! A 404 or 500 still returns a “response”—but it’s not successful. Making a POST Request fetch('https://api.example.com/login', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: 'you@example.com', password: 'supersecret' }) }) .then(response => response.json()) .then(data => console.log('Login success:', data)) .catch(error => console.error('Login failed:', error)); Key things to note: method: 'POST' tells the server what you’re doing. headers specify the data format (usually JSON). body must be a string—so we use JSON.stringify(). Option 2: The Powerhouse — Axios If you want more features out of the box (automatic JSON parsing, request/response interceptors, better error handling), Axios is your friend. Install it: # Web (via npm) npm install axios # React Native npm install axios # (or use Expo’s built-in fetch—both work!) GET with Axios import axios from 'axios'; axios.get('https://api.example.com/users') .then(response => { console.log('Users:', response.data); }) .catch(error => { console.error('Error fetching users:', error); }); POST with Axios axios.post('https://api.example.com/login', { email: 'you@example.com', password: 'supersecret' }) .then(response => { console.log('Login response:', response.data); }) .catch(error => { console.error('Login error:', error.response?.data || error.message); }); Bonus: Axios automatically transforms JSON requests/responses—no JSON.stringify() or .json() needed! Does This Work on Mobile? Yes—with caveats. Platform fetch() Axios Web (Chrome, Firefox, etc.) Full support React Native Built-in (via npm) Capacitor / Cordova Expo ️ One gotcha: CORS doesn’t apply in mobile apps (since there’s no browser sandbox), but you still need proper backend headers for web. Security & Best Practices Never hardcode secrets (API keys, tokens) in frontend code. Use HTTPS—always. No exceptions. Handle errors gracefully—users hate silent failures. For auth, store tokens in secure storage (e.g., AsyncStorage in React Native + encryption). Consider aborting requests if the user navigates away (to prevent memory leaks): const controller = new AbortController(); fetch('/api/data', { signal: controller.signal }); // Later, if needed: controller.abort(); Async/Await: Cleaner Syntax Both fetch and Axios work beautifully with async/await—making your code read like a story: async function loginUser(email, password) { try { const response = await axios.post('/login', { email, password }); return response.data; } catch (error) { console.error('Login failed:', error.message); throw error; } } Much cleaner than .then() chains, right? 🧪 Quick Comparison: fetch vs Axios Feature fetch() Axios Built-in (needs install) Automatic JSON parsing Request timeout (needs workaround) Interceptors Browser support Modern only Broader (with polyfills) Bundle size 0 KB (native) ~5 KB Use fetch if you want minimal dependencies. Use Axios if you want developer ergonomics and advanced features. Final Thoughts Whether you’re building a sleek SaaS dashboard or a cross-platform mobile app, HTTP requests are your lifeline to the backend. And thanks to JavaScript’s ecosystem, you’ve got two rock-solid options that work almost everywhere. Start simple with fetch(). Scale up to Axios when you need more control. And always—always—handle errors like a pro. Now go make your app talk to the world.
  • 0 0
    0 Темы
    0 Сообщения
    Нет новых сообщений
  • 1 1
    1 Темы
    1 Сообщения
    AladdinA
    Введение В этом гайде вы изучите принципы работы веб-серверов и научитесь создавать собственный HTTP API сервер на Python, используя только стандартные библиотеки языка. Материал предназначен для программистов-школьников и студентов, желающих понять, как работают веб-серверы на низком уровне. Что такое HTTP сервер HTTP сервер — это программа, которая принимает запросы от клиентов через протокол HTTP и отправляет им ответы. Современный веб основан на текстовом обмене данными между клиентами и серверами. Основные компоненты HTTP сервера: Сетевой слой - принимает TCP соединения Парсер запросов - разбирает HTTP запросы Маршрутизация - определяет обработчик для запроса Генератор ответов - формирует HTTP ответы Менеджер соединений - управляет клиентскими подключениями HTTP протокол: основы HTTP (HyperText Transfer Protocol) - это протокол прикладного уровня для передачи данных между веб-сервером и клиентом. Протокол работает по принципу запрос-ответ. Структура HTTP запроса: GET /api/users HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 Accept: application/json Content-Type: application/json Content-Length: 25 {"name": "John Doe"} HTTP методы Метод Описание Безопасный Идемпотентный GET Получение данных Да Да POST Создание/отправка данных Нет Нет PUT Обновление/создание Нет Да DELETE Удаление Нет Да HEAD Получение только заголовков Да Да OPTIONS Получение разрешенных методов Да Да HTTP статус коды 2xx - Успешные операции 200 OK - Запрос успешно обработан 201 Created - Ресурс успешно создан 204 No Content - Успех без содержимого 4xx - Ошибки клиента 400 Bad Request - Неверный формат запроса 401 Unauthorized - Требуется аутентификация 404 Not Found - Ресурс не найден 5xx - Ошибки сервера 500 Internal Server Error - Внутренняя ошибка сервера 502 Bad Gateway - Ошибка шлюза 503 Service Unavailable - Сервис недоступен Socket Programming в Python Socket (сокет) — это конечная точка двустороннего канала связи между процессами в сети. Python предоставляет мощный модуль socket для работы с сетевыми соединениями. Основные методы работы с сокетами: import socket # Создание TCP сокета server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Настройка переиспользования адреса server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Привязка к адресу и порту server_socket.bind(('127.0.0.1', 8080)) # Начало прослушивания (до 5 соединений в очереди) server_socket.listen(5) # Принятие клиентского соединения client_socket, address = server_socket.accept() # Получение данных data = client_socket.recv(4096) # Отправка данных client_socket.send(response.encode('utf-8')) # Закрытие соединения client_socket.close() Базовый HTTP сервер Вот полный код простого HTTP сервера, работающего без внешних зависимостей: import socket import threading from datetime import datetime class SimpleHTTPServer: def __init__(self, host='127.0.0.1', port=8080): self.host = host self.port = port self.socket = None self.running = False def start(self): """Запуск HTTP сервера""" self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: self.socket.bind((self.host, self.port)) self.socket.listen(5) self.running = True print(f"HTTP сервер запущен на http://{self.host}:{self.port}") while self.running: try: client_socket, address = self.socket.accept() # Создаем отдельный поток для каждого клиента client_thread = threading.Thread( target=self.handle_client, args=(client_socket, address) ) client_thread.daemon = True client_thread.start() except socket.error: break except Exception as e: print(f"Ошибка сервера: {e}") finally: self.stop() def handle_client(self, client_socket, address): """Обработка запроса клиента""" try: request_data = client_socket.recv(4096).decode('utf-8') if request_data: http_request = self.parse_request(request_data) response = self.generate_response(http_request) client_socket.send(response.encode('utf-8')) except Exception as e: print(f"Ошибка обработки клиента {address}: {e}") finally: client_socket.close() def parse_request(self, request_data): """Парсинг HTTP запроса""" lines = request_data.split('\r\n') # Парсим стартовую строку: "GET /path HTTP/1.1" request_line = lines[^0].split() method = request_line[^0] if len(request_line) > 0 else 'GET' path = request_line[^1] if len(request_line) > 1 else '/' version = request_line[^2] if len(request_line) > 2 else 'HTTP/1.1' # Парсим заголовки headers = {} for line in lines[1:]: if line == '': # Пустая строка означает конец заголовков break if ':' in line: key, value = line.split(':', 1) headers[key.strip()] = value.strip() return { 'method': method, 'path': path, 'version': version, 'headers': headers } def generate_response(self, request): """Генерация HTTP ответа""" method = request['method'] path = request['path'] # Простая маршрутизация if path == '/': status_code = 200 body = self.get_home_page() content_type = 'text/html' elif path == '/api/time': status_code = 200 body = f'{{"current_time": "{datetime.now().isoformat()}"}}' content_type = 'application/json' else: status_code = 404 body = '<h1>404 - Страница не найдена</h1>' content_type = 'text/html' # Формируем HTTP ответ response_headers = [ f'HTTP/1.1 {status_code} {"OK" if status_code == 200 else "Not Found"}', f'Content-Type: {content_type}; charset=utf-8', f'Content-Length: {len(body.encode("utf-8"))}', 'Connection: close', f'Date: {datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT")}', '', # Пустая строка разделяет заголовки и тело body ] return '\r\n'.join(response_headers) def get_home_page(self): """Генерация главной страницы""" return '''<!DOCTYPE html> <html> <head> <title>Simple HTTP Server</title> <meta charset="utf-8"> </head> <body> <h1>Простой HTTP сервер на Python</h1> <p>Сервер работает без фреймворков!</p> <ul> <li><a href="/">/</a> - Главная страница</li> <li><a href="/api/time">/api/time</a> - Текущее время</li> </ul> </body> </html>''' def stop(self): """Остановка сервера""" self.running = False if self.socket: self.socket.close() # Запуск сервера if __name__ == '__main__': server = SimpleHTTPServer('127.0.0.1', 8080) try: server.start() except KeyboardInterrupt: print("\nСервер остановлен") server.stop() Многопоточность и производительность Без многопоточности сервер может обрабатывать только один запрос за раз, что создает блокировки. Python предоставляет несколько способов обработки множественных соединений: Threading vs Multiprocessing Подход Преимущества Недостатки Threading Общая память, быстрое переключение GIL ограничивает параллелизм Multiprocessing Истинный параллелизм Больше памяти, сложнее обмен данными AsyncIO Эффективность I/O операций Сложность программирования Пул потоков для оптимизации: from concurrent.futures import ThreadPoolExecutor class ThreadedServer(SimpleHTTPServer): def __init__(self, host='127.0.0.1', port=8080, max_workers=50): super().__init__(host, port) self.executor = ThreadPoolExecutor(max_workers=max_workers) def start(self): """Запуск с пулом потоков""" self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: self.socket.bind((self.host, self.port)) self.socket.listen(5) self.running = True while self.running: try: client_socket, address = self.socket.accept() # Используем пул потоков self.executor.submit( self.handle_client, client_socket, address ) except socket.error: break finally: self.executor.shutdown() Управление памятью в Python Python автоматически управляет памятью через reference counting и garbage collection. Однако в веб-серверах важно оптимизировать использование памяти. Python Memory Management in Web Servers Основные техники оптимизации памяти: Используйте генераторы вместо списков для больших данных Ограничивайте размеры буферов и кэшей Применяйте weak references для автоматической очистки Мониторьте память и принудительно вызывайте сборщик мусора import gc import weakref from collections import deque class MemoryOptimizedServer: def __init__(self): # Ограничиваем историю запросов self.request_history = deque(maxlen=1000) # Weak references для соединений self.active_connections = weakref.WeakSet() # Настройка сборщика мусора gc.set_threshold(700, 10, 10) def process_large_data(self, data): """Используем генераторы для экономии памяти""" for chunk in self.data_chunks(data, 1024): yield self.process_chunk(chunk) def data_chunks(self, data, chunk_size): """Генератор для обработки данных по частям""" for i in range(0, len(data), chunk_size): yield data[i:i + chunk_size] def monitor_memory(self): """Мониторинг использования памяти""" import psutil process = psutil.Process() memory_percent = process.memory_percent() # Принудительная очистка при превышении лимита if memory_percent > 80: gc.collect() return memory_percent Продвинутый парсинг HTTP запросов Для надежной работы сервера необходим качественный парсинг HTTP запросов: import re import json from urllib.parse import parse_qs, urlparse class HTTPRequestParser: def __init__(self): self.request_line_pattern = re.compile( r'^([A-Z]+)\s+([^\s]+)\s+HTTP/([0-9]\.[0-9])$' ) def parse(self, request_data): """Полный парсинг HTTP запроса""" if isinstance(request_data, bytes): request_data = request_data.decode('utf-8') # Разделяем заголовки и тело if '\r\n\r\n' in request_data: headers_part, body = request_data.split('\r\n\r\n', 1) else: headers_part = request_data body = '' lines = headers_part.split('\r\n') # Парсим компоненты method, path, version = self.parse_request_line(lines[^0]) url_parts = self.parse_url(path) headers = self.parse_headers(lines[1:]) parsed_body = self.parse_body(body, headers) return { 'method': method, 'path': url_parts['path'], 'query_params': url_parts['query'], 'version': version, 'headers': headers, 'body': parsed_body } def parse_body(self, body, headers): """Парсинг тела запроса по Content-Type""" if not body: return None content_type = headers.get('content-type', '').lower() if 'application/json' in content_type: try: return json.loads(body) except json.JSONDecodeError: return body elif 'application/x-www-form-urlencoded' in content_type: return parse_qs(body) else: return body Маршрутизация запросов Система маршрутизации определяет, какой код должен обработать конкретный запрос: import re from functools import wraps class Router: def __init__(self): self.routes = [] def add_route(self, method, pattern, handler): """Добавление маршрута""" compiled_pattern = re.compile(pattern) self.routes.append({ 'method': method.upper(), 'pattern': compiled_pattern, 'handler': handler }) def get(self, pattern): """Декоратор для GET запросов""" def decorator(func): self.add_route('GET', pattern, func) return func return decorator def post(self, pattern): """Декоратор для POST запросов""" def decorator(func): self.add_route('POST', pattern, func) return func return decorator def route(self, request): """Поиск подходящего маршрута""" method = request['method'] path = request['path'] for route in self.routes: if route['method'] == method: match = route['pattern'].match(path) if match: request['params'] = match.groups() return route['handler'](request) return self.not_found_response() # Использование роутера router = Router() @router.get(r'^/$') def home_page(request): return { 'status': 200, 'headers': {'Content-Type': 'text/html'}, 'body': '<h1>Главная страница</h1>' } @router.get(r'^/api/users/(\d+)$') def get_user(request): user_id = request['params'][^0] return { 'status': 200, 'headers': {'Content-Type': 'application/json'}, 'body': f'{{"user_id": {user_id}}}' } Обработка ошибок и отладка Качественная обработка ошибок критична для стабильности сервера: import logging from datetime import datetime # Настройка логирования logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class LoggingServer(SimpleHTTPServer): def handle_client(self, client_socket, address): """Обработка с логированием""" start_time = datetime.now() try: request_data = client_socket.recv(4096).decode('utf-8') if request_data: request_line = request_data.split('\r\n')[^0] logger.info(f"Запрос от {address}: {request_line}") http_request = self.parse_request(request_data) response = self.generate_response(http_request) client_socket.send(response.encode('utf-8')) duration = datetime.now() - start_time logger.info(f"Обработано за {duration.total_seconds():.3f}с") except Exception as e: logger.error(f"Ошибка обработки {address}: {e}") error_response = self.generate_error_response(500, str(e)) try: client_socket.send(error_response.encode('utf-8')) except: pass finally: client_socket.close() Тестирование сервера Для проверки работоспособности сервера создадим простой тестовый клиент: import socket import time def test_server(host='127.0.0.1', port=8080): """Тестирование HTTP сервера""" test_requests = [ "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n", "GET /api/time HTTP/1.1\r\nHost: localhost\r\n\r\n", "GET /nonexistent HTTP/1.1\r\nHost: localhost\r\n\r\n" ] for i, request in enumerate(test_requests): print(f"\n--- Тест {i+1} ---") try: client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect((host, port)) client_socket.send(request.encode('utf-8')) response = client_socket.recv(4096).decode('utf-8') status_line = response.split('\r\n')[^0] print(f"Статус: {status_line}") client_socket.close() except Exception as e: print(f"Ошибка теста: {e}") if __name__ == '__main__': test_server() Оптимизация производительности Кэширование ответов import time from functools import lru_cache class CachingServer: def __init__(self): self.response_cache = {} self.cache_ttl = 60 # секунд def get_cached_response(self, cache_key): """Получение ответа из кэша""" if cache_key in self.response_cache: response, timestamp = self.response_cache[cache_key] if time.time() - timestamp < self.cache_ttl: return response return None @lru_cache(maxsize=128) def generate_static_response(self, path): """Кэшированная генерация статических ответов""" if path == '/': return self.get_home_page() return None Сжатие ответов import gzip def compress_response(body, encoding='gzip'): """Сжатие тела ответа""" if isinstance(body, str): body = body.encode('utf-8') if encoding == 'gzip' and len(body) > 1024: return gzip.compress(body) return body Безопасность Базовые принципы безопасности для веб-серверов: import time import hashlib class SecureServer: def __init__(self): self.rate_limits = {} # IP -> timestamps self.blocked_ips = set() def check_rate_limit(self, client_ip, max_requests=100, window=3600): """Проверка ограничений скорости запросов""" now = time.time() if client_ip in self.rate_limits: self.rate_limits[client_ip] = [ ts for ts in self.rate_limits[client_ip] if now - ts < window ] else: self.rate_limits[client_ip] = [] if len(self.rate_limits[client_ip]) >= max_requests: self.blocked_ips.add(client_ip) return False self.rate_limits[client_ip].append(now) return True def validate_request(self, request): """Валидация запроса""" path = request['path'] # Проверка на Path Traversal if '..' in path or '~' in path: return False, "Path traversal detected" # Проверка размера content_length = int(request['headers'].get('content-length', 0)) if content_length > 10 * 1024 * 1024: # 10MB return False, "Request too large" return True, "OK" Заключение Ограничения самодельных серверов: Производительность уступает оптимизированным фреймворкам Сложность обеспечения полной совместимости с HTTP стандартами Требуется больше времени на разработку и отладку Для продакшена рекомендуется использовать: FastAPI - современный, быстрый фреймворк Flask - простой и гибкий aiohttp - асинхронный веб-сервер Помните: понимание низкоуровневых принципов работы веб-серверов поможет вам стать лучшим разработчиком и эффективнее использовать готовые фреймворки!
  • 0 0
    0 Темы
    0 Сообщения
    Нет новых сообщений
  • 0 0
    0 Темы
    0 Сообщения
    Нет новых сообщений
  • 0 0
    0 Темы
    0 Сообщения
    Нет новых сообщений
  • 0 0
    0 Темы
    0 Сообщения
    Нет новых сообщений