Новое:Talanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

+1 870новых вакансий за сутки·91 664вакансии открыты сейчас·Новое:Talanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

Новые вакансии в TelegramРаботодателям
Talanto
Вакансии
Компании7 244
РезюмеAIПодготовкаСтатьи
BoostТарифыBoost
Войти

Не нашли подходящее?

Пусть нужная вакансия найдёт вас сама.

  1. Подборка в TelegramНовые вакансии по вашему фильтру — сразу в бота.Подключить
  2. Мэтчи по резюмеЗагрузите резюме — покажем, где вы подходите лучше всего.Загрузить
  3. Сохранить фильтрНастройте поиск один раз и возвращайтесь к новым вакансиям.Настроить
Что нового на TalantoTalanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

Для работодателей

Опубликуйте вакансию — отклики придут в ваш кабинет.

  1. Вакансиябесплатно
  2. Откликиконтакты бесплатно
  3. Наймэтапы и интервью

40 компаний публикуют вакансии напрямую · 4 401 кандидат открыт к предложениям

Разместить вакансию

Популярные направления

Все подборки
  • Python16 420
  • Облачные технологии13 174
  • QA вакансии3 434
  • Backend вакансии2 036
  • Инженер данных1 672
  • Продакт-менеджер1 168

Разработка

  • Android1 816
  • iOS1 666
  • AI вакансии937
  • Fullstack вакансии892
  • Frontend вакансии717
  • Mobile вакансии38

По стеку

  • Go8 562
  • Java6 092
  • Node.js1 889
  • Kotlin1 554
  • .NET1 545
  • Rust1 433
  • PHP641
  • C#563
  • C++498

DevOps и инфраструктура

  • Kubernetes7 303
  • DevOps вакансии5 595
  • Linux-инфраструктура5 291
  • Terraform / IaC4 014
  • CI/CD2 559
  • Мониторинг и Observability1 735
  • SRE548
  • Кибербезопасность403

Аналитика и данные

  • Data Scientist1 037
  • Бизнес-аналитик868
  • Аналитик данных830
  • Системный аналитик134

Другие направления

  • Проджект-менеджер529
  • UI/UX дизайн129
  • Ручное тестирование14

По регионам

  • Вакансии в США44 925
  • Вакансии в Европе21 347
  • Вакансии в Канаде4 453
  • Работа в России3 876

По формату

  • Удалённые вакансии74 108
  • Релокация в США44 925
  • Гибридная работа11 411
  • Офисная работа6 957

По уровню

  • Senior вакансии41 290
  • Middle вакансии20 424
  • Junior вакансии6 654

Сейчас нанимают

Все компании
  • Wise194 вакансии
  • Rakuten189 вакансий
  • ASML164 вакансии
  • Grab136 вакансий
  • Celonis45 вакансий
  • Spotify36 вакансий
  • Adyen29 вакансий
  • Infobip28 вакансий
  • Monzo19 вакансий
  • Darktrace17 вакансий

Вакансии из 344 источников: карьерные страницы компаний и джоб-борды.

КомпанииО насНовостиПубличная офертаУсловия использованияПолитика конфиденциальностиПоддержка
←К теме AxumВсе вопросы
AxumMiddleТеория

Как стандартизировать error responses и map internal errors to HTTP?

В закладки
Сначала попробуйте ответить сами30 секунд
Сравнить с ответом ↓

В Axum стандартизация ошибок — через impl IntoResponse для кастомного AppError enum с thiserror. Все хендлеры возвращают Result<impl IntoResponse, AppError>, operator ? конвертирует через From.

Определение AppError с thiserror

use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};
use serde::Serialize;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum AppError {
    #[error("resource not found: {0}")]
    NotFound(String),

    #[error("unauthorized: {0}")]
    Unauthorized(String),

    #[error("forbidden")]
    Forbidden,

    #[error("validation failed: {0}")]
    Validation(String),

    #[error("database error")]
    Database(#[from] sqlx::Error),

    #[error("external service error: {0}")]
    External(String),
}

#[derive(Serialize)]
struct ErrorBody {
    error: String,
    message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    field: Option<String>,
}

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        let (status, code, message) = match &self {
            AppError::NotFound(msg) => (
                StatusCode::NOT_FOUND,
                "NOT_FOUND",
                msg.clone(),
            ),
            AppError::Unauthorized(msg) => (
                StatusCode::UNAUTHORIZED,
                "UNAUTHORIZED",
                msg.clone(),
            ),
            AppError::Forbidden => (
                StatusCode::FORBIDDEN,
                "FORBIDDEN",
                "Access denied".to_string(),
            ),
            AppError::Validation(msg) => (
                StatusCode::UNPROCESSABLE_ENTITY,
                "VALIDATION_ERROR",
                msg.clone(),
            ),
            AppError::Database(e) => {
                tracing::error!("Database error: {:?}", e);
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "DATABASE_ERROR",
                    "A database error occurred".to_string(),
                )
            }
            AppError::External(msg) => {
                tracing::error!("External service error: {}", msg);
                (
                    StatusCode::BAD_GATEWAY,
                    "EXTERNAL_ERROR",
                    "External service unavailable".to_string(),
                )
            }
        };

        (
            status,
            Json(ErrorBody {
                error: code.to_string(),
                message,
                field: None,
            }),
        ).into_response()
    }
}

Стандартизация error responses в Axum

Axum использует трейт IntoResponse для конвертации любого типа в HTTP-ответ. Это позволяет создать единый error type с детерминированным маппингом в HTTP-статусы.

From-реализации для автоматической конвертации

// sqlx::Error уже покрыт через #[from] в derive
// Добавляем кастомные конвертации
impl From<uuid::Error> for AppError {
    fn from(e: uuid::Error) -> Self {
        AppError::Validation(format!("Invalid UUID: {}", e))
    }
}

impl From<reqwest::Error> for AppError {
    fn from(e: reqwest::Error) -> Self {
        AppError::External(e.to_string())
    }
}

Использование в хендлерах

use axum::{
    extract::{Path, State},
    response::IntoResponse,
    Json,
};

async fn get_user(
    State(state): State<AppState>,
    Path(user_id): Path<uuid::Uuid>,
) -> Result<impl IntoResponse, AppError> {
    let user = sqlx::query_as!(
        User,
        "SELECT * FROM users WHERE id = $1",
        user_id
    )
    .fetch_optional(&state.db)
    .await?  // sqlx::Error -> AppError::Database via From
    .ok_or_else(|| AppError::NotFound(format!("User {} not found", user_id)))?;

    Ok(Json(user))
}

async fn create_user(
    State(state): State<AppState>,
    Json(dto): Json<CreateUserDto>,
) -> Result<impl IntoResponse, AppError> {
    if dto.email.is_empty() {
        return Err(AppError::Validation("email cannot be empty".to_string()));
    }

    let user = sqlx::query_as!(
        User,
        "INSERT INTO users (email) VALUES ($1) RETURNING *",
        dto.email
    )
    .fetch_one(&state.db)
    .await?;

    Ok((StatusCode::CREATED, Json(user)))
}

Обработка ошибок десериализации JSON

use axum::extract::rejection::JsonRejection;

// Кастомная обработка ошибок парсинга JSON body
async fn create_user_safe(
    State(state): State<AppState>,
    payload: Result<Json<CreateUserDto>, JsonRejection>,
) -> Result<impl IntoResponse, AppError> {
    let Json(dto) = payload.map_err(|e| {
        AppError::Validation(format!("Invalid request body: {}", e))
    })?;
    // ...
    Ok(Json(serde_json::json!({"ok": true})))
}

Подводные камни

  • Если хендлер возвращает (StatusCode, Json(...)) напрямую без AppError, этот путь обходит централизованное логирование.
  • thiserror::Error derive требует, что #[from] применяется только к типам, которые реализуют std::error::Error.
  • При нескольких From<X> for AppError с одним типом X компилятор откажет — нужны newtype wrappers.
  • Тело ошибки с деталями об ошибке БД (имена таблиц, SQL) никогда не должно отправляться клиенту — логируйте server-side.
  • JsonRejection возникает до вызова хендлера, поэтому стандартный Result в сигнатуре не поможет — нужен паттерн с явным Result<Json<T>, JsonRejection>.
  • axum не имеет глобального exception handler как в Actix — каждый route должен явно возвращать совместимый тип.
  • При panics внутри хендлера ответ клиенту — connection close без тела; добавьте tower::ServiceBuilder::new().layer(tower::util::CatchPanicLayer::new()).

Частые вопросы на собеседовании

  • В каких backend-проектах Axum (Rust) является сильным выбором, а где лучше выбрать более простой или другой стек?
  • Какие production-риски есть у Axum (Rust): blocking code, connection pooling, config, auth, observability, deploy или graceful shutdown?
  • Как понять, что проблема в сервисе связана с Axum (Rust), а не с базой, сетью, инфраструктурой или бизнес-логикой?
  • Проект на Axum (Rust) вырос из MVP в большой production-сервис. Какие проблемы архитектуры и сопровождения вы ожидаете увидеть?

Типичные ошибки

  • Отвечать определением без production-сценария.
  • Не называть runtime boundary, security boundary или failure mode.
  • Игнорировать версию API, observability и тестовую проверку.

Что проверяет интервьюер

  • Объясняет механизм своими словами и без выдуманных API.
  • Называет реальные риски, диагностику и критерий корректности.
  • Связывает ответ с текущей документацией и миграционными ограничениями.

Как вы знаете этот вопрос?

Проверить себя по теме Axum →

Источники

  • https://docs.rs/axum/latest/axum/

Связанные темы

Axumaxum-productionAxum (Rust)risk-analysistechnical-depth