← Назад к вопросам

REST API генератор случайных чисел

1.2 Junior🔥 81 комментариев
#API и веб-протоколы#Фреймворки

Условие

Написать REST API для генерации случайных чисел с сохранением результатов.

Требования

  • Каждой генерации присваивается уникальный id
  • По id можно получить результат генерации
  • Хранение в базе данных

API методы

  • POST /api/generate - генерирует случайное число, возвращает id и число
  • GET /api/retrieve/{id} - возвращает результат генерации по id
  • GET /api/history - список всех генераций с пагинацией

Дополнительно

  • Параметры генерации: min, max (опционально)
  • Возможность генерации нескольких чисел за раз
  • Rate limiting (не более 10 запросов в минуту)

Технологии

PHP 8+, Laravel, MySQL/PostgreSQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

REST API генератор случайных чисел: Полное решение

Архитектура решения

Для реализации требуемого функционала я буду использовать чистую архитектуру с разделением на слои: контроллеры, сервисы и модели. Это обеспечит масштабируемость и тестируемость кода.

Основные компоненты:

  • Database schema для хранения результатов
  • Service layer для бизнес-логики
  • API контроллеры для обработки запросов
  • Rate limiting middleware
  • Валидация входных данных

Миграция БД

// database/migrations/2024_01_15_create_random_generations_table.php
Schema::create('random_generations', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->integer('min_value');
    $table->integer('max_value');
    $table->integer('generated_number');
    $table->integer('count')->default(1);
    $table->json('numbers')->nullable();
    $table->ipAddress('ip_address');
    $table->timestamps();
    $table->index('created_at');
});

Модель

// app/Models/RandomGeneration.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Concerns\HasUuids;

class RandomGeneration extends Model
{
    use HasUuids;
    
    protected $fillable = [
        'min_value',
        'max_value',
        'generated_number',
        'count',
        'numbers',
        'ip_address',
    ];
    
    protected $casts = [
        'numbers' => 'array',
        'generated_number' => 'integer',
        'min_value' => 'integer',
        'max_value' => 'integer',
    ];
}

Service слой

// app/Services/RandomGenerationService.php
namespace App\Services;

use App\Models\RandomGeneration;
use Illuminate\Pagination\Paginator;

class RandomGenerationService
{
    private const DEFAULT_MIN = 1;
    private const DEFAULT_MAX = 100;
    
    /**
     * Генерирует одно или несколько случайных чисел
     */
    public function generate(int $count = 1, ?int $min = null, ?int $max = null, string $ipAddress = null): RandomGeneration
    {
        $min = $min ?? self::DEFAULT_MIN;
        $max = $max ?? self::DEFAULT_MAX;
        
        $this->validateRange($min, $max);
        
        $numbers = [];
        for ($i = 0; $i < $count; $i++) {
            $numbers[] = random_int($min, $max);
        }
        
        $generation = RandomGeneration::create([
            'min_value' => $min,
            'max_value' => $max,
            'generated_number' => $numbers[0],
            'count' => $count,
            'numbers' => $count > 1 ? $numbers : null,
            'ip_address' => $ipAddress,
        ]);
        
        return $generation;
    }
    
    /**
     * Получает результат генерации по ID
     */
    public function retrieve(string $id): ?RandomGeneration
    {
        return RandomGeneration::find($id);
    }
    
    /**
     * Получает историю генераций с пагинацией
     */
    public function getHistory(int $page = 1, int $perPage = 20): Paginator
    {
        return RandomGeneration::paginate($perPage, ['*'], 'page', $page);
    }
    
    /**
     * Проверяет валидность диапазона
     */
    private function validateRange(int $min, int $max): void
    {
        if ($min > $max) {
            throw new \InvalidArgumentException('min должна быть меньше или равна max');
        }
        
        if ($min < 0 || $max > 2147483647) {
            throw new \InvalidArgumentException('Значения должны быть в пределах 0-2147483647');
        }
    }
}

API контроллер

// app/Http/Controllers/Api/RandomGenerationController.php
namespace App\Http\Controllers\Api;

use App\Services\RandomGenerationService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;

class RandomGenerationController
{
    public function __construct(private RandomGenerationService $service) {}
    
    /**
     * POST /api/generate
     */
    public function generate(Request $request): JsonResponse
    {
        try {
            $validated = $request->validate([
                'min' => 'nullable|integer|min:0',
                'max' => 'nullable|integer',
                'count' => 'nullable|integer|min:1|max:1000',
            ]);
            
            $generation = $this->service->generate(
                count: $validated['count'] ?? 1,
                min: $validated['min'] ?? null,
                max: $validated['max'] ?? null,
                ipAddress: $request->ip(),
            );
            
            return response()->json([
                'success' => true,
                'id' => $generation->id,
                'number' => $generation->generated_number,
                'numbers' => $generation->numbers,
                'min' => $generation->min_value,
                'max' => $generation->max_value,
            ], 201);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'error' => $e->getMessage(),
            ], 400);
        }
    }
    
    /**
     * GET /api/retrieve/{id}
     */
    public function retrieve(string $id): JsonResponse
    {
        $generation = $this->service->retrieve($id);
        
        if (!$generation) {
            return response()->json([
                'success' => false,
                'error' => 'Generation not found',
            ], 404);
        }
        
        return response()->json([
            'success' => true,
            'id' => $generation->id,
            'number' => $generation->generated_number,
            'numbers' => $generation->numbers,
            'min' => $generation->min_value,
            'max' => $generation->max_value,
            'created_at' => $generation->created_at,
        ]);
    }
    
    /**
     * GET /api/history
     */
    public function history(Request $request): JsonResponse
    {
        $page = $request->query('page', 1);
        $perPage = min($request->query('per_page', 20), 100);
        
        $history = $this->service->getHistory($page, $perPage);
        
        return response()->json([
            'success' => true,
            'data' => $history->items(),
            'pagination' => [
                'current_page' => $history->currentPage(),
                'per_page' => $history->perPage(),
                'total' => $history->total(),
                'last_page' => $history->lastPage(),
            ],
        ]);
    }
}

Rate Limiting Middleware

// app/Http/Middleware/RateLimitApi.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Cache\RateLimiter;

class RateLimitApi
{
    public function __construct(private RateLimiter $limiter) {}
    
    public function handle(Request $request, Closure $next)
    {
        $key = 'api_limit:' . $request->ip();
        
        if ($this->limiter->tooManyAttempts($key, 10)) {
            return response()->json([
                'success' => false,
                'error' => 'Rate limit exceeded. Max 10 requests per minute',
            ], 429);
        }
        
        $this->limiter->hit($key, 60);
        
        return $next($request)->header('X-RateLimit-Limit', 10);
    }
}

Роуты

// routes/api.php
Route::middleware('rate.limit.api')->prefix('api')->group(function () {
    Route::post('/generate', [RandomGenerationController::class, 'generate']);
    Route::get('/retrieve/{id}', [RandomGenerationController::class, 'retrieve']);
    Route::get('/history', [RandomGenerationController::class, 'history']);
});

Тестирование

// tests/Feature/RandomGenerationApiTest.php
namespace Tests\Feature;

use Tests\TestCase;

class RandomGenerationApiTest extends TestCase
{
    public function test_can_generate_random_number()
    {
        $response = $this->postJson('/api/generate');
        
        $response->assertStatus(201)
            ->assertJsonStructure(['id', 'number', 'min', 'max'])
            ->assertJsonPath('success', true);
    }
    
    public function test_can_retrieve_generated_number()
    {
        $generate = $this->postJson('/api/generate');
        $id = $generate->json('id');
        
        $retrieve = $this->getJson("/api/retrieve/{$id}");
        
        $retrieve->assertStatus(200)
            ->assertJsonPath('id', $id);
    }
    
    public function test_rate_limit_enforced()
    {
        for ($i = 0; $i < 11; $i++) {
            $response = $this->postJson('/api/generate');
            
            if ($i < 10) {
                $response->assertStatus(201);
            } else {
                $response->assertStatus(429);
            }
        }
    }
}

Заключение

Реализованное решение обеспечивает все требуемые функции с использованием best practices Laravel. Код легко тестируется, масштабируется и поддерживает rate limiting для защиты API.

REST API генератор случайных чисел | PrepBro