← Назад к вопросам
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.