aleikin_artem_lab_3 is ready

This commit is contained in:
Артём Алейкин 2024-11-19 15:20:55 +04:00
parent db87c6248f
commit e3248a2c6d
27 changed files with 726 additions and 0 deletions

View File

@ -0,0 +1,30 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**

View File

@ -0,0 +1,60 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using ProjectEntityProject.Entity;
namespace ProjectEntityProject.Controllers
{
[ApiController]
[Route("projects")]
public class ProjectController : ControllerBase
{
private static readonly List<Project> Projects = new();
[HttpGet]
public ActionResult<List<Project>> GetProjects() => Ok(Projects);
[HttpGet("{id}")]
public ActionResult<Project> GetProject([FromRoute] Guid id)
{
var project = Projects.FirstOrDefault(p => p.Id == id);
return project is null ? NotFound() : Ok(project);
}
[HttpPost]
public ActionResult<Project> CreateProject([FromBody] ProjectDto projectdDto)
{
Project project = new Project
{
Name = projectdDto.Name,
Description = projectdDto.Description,
};
Projects.Add(project);
return CreatedAtAction(nameof(GetProject), new { id = project.Id }, project);
}
[HttpPut("{id}")]
public ActionResult<Project> UpdateProject(Guid id, [FromBody] ProjectDto projectDto)
{
var project = Projects.FirstOrDefault(p => p.Id == id);
if (project is null) return NotFound();
project.Name = projectDto.Name;
project.Description = projectDto.Description;
return Ok(project);
}
[HttpDelete("{id}")]
public IActionResult DeleteProject(Guid id)
{
var project = Projects.FirstOrDefault(p => p.Id == id);
if (project is null) return NotFound();
Projects.Remove(project);
var client = new HttpClient();
var response = client.DeleteAsync($"http://nginx/taskservice/tasks/by-project/{id}").Result;
return response.IsSuccessStatusCode ? Ok() : StatusCode(500);
}
}
}

View File

@ -0,0 +1,29 @@
# См. статью по ссылке https://aka.ms/customizecontainer, чтобы узнать как настроить контейнер отладки и как Visual Studio использует этот Dockerfile для создания образов для ускорения отладки.
# Этот этап используется при запуске из VS в быстром режиме (по умолчанию для конфигурации отладки)
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 5001
# Этот этап используется для сборки проекта службы
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["ProjectEntityProject/ProjectEntityProject.csproj", "ProjectEntityProject/"]
RUN dotnet restore "./ProjectEntityProject/ProjectEntityProject.csproj"
COPY . .
WORKDIR "/src/ProjectEntityProject"
RUN dotnet build "./ProjectEntityProject.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Этот этап используется для публикации проекта службы, который будет скопирован на последний этап
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./ProjectEntityProject.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Этот этап используется в рабочей среде или при запуске из VS в обычном режиме (по умолчанию, когда конфигурация отладки не используется)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ProjectEntityProject.dll"]

View File

@ -0,0 +1,10 @@
namespace ProjectEntityProject.Entity
{
public class Project
{
public Guid Id { get; init; } = Guid.NewGuid();
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,4 @@
namespace ProjectEntityProject.Entity
{
public record ProjectDto(string Name, string Description) { }
}

View File

@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(5001);
});
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
var app = builder.Build();
app.UseCors("AllowAll");
app.UseSwagger(c =>
{
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
{
swaggerDoc.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}/projectservice" } };
});
});
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>8c799772-d663-4c4a-8e6b-cce6a75ee84e</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
<Controller_SelectedScaffolderID>MvcControllerWithActionsScaffolder</Controller_SelectedScaffolderID>
<Controller_SelectedScaffolderCategoryPath>root/Common/MVC/Controller</Controller_SelectedScaffolderCategoryPath>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,6 @@
@ProjectEntityProject_HostAddress = http://localhost:5168
GET {{ProjectEntityProject_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -0,0 +1,52 @@
{
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5168"
},
"https": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7265;http://localhost:5168"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Container (Dockerfile)": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_HTTPS_PORTS": "8081",
"ASPNETCORE_HTTP_PORTS": "8080"
},
"publishAllPorts": true,
"useSSL": true
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:54970",
"sslPort": 44349
}
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35312.102
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskProject", "TaskProject\TaskProject.csproj", "{D175ACE5-8DAA-41B0-A0F1-BA27B5F0426F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectEntityProject", "ProjectEntityProject\ProjectEntityProject.csproj", "{86A5B199-BE0A-4575-A4E9-664AA5D9ADDE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D175ACE5-8DAA-41B0-A0F1-BA27B5F0426F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D175ACE5-8DAA-41B0-A0F1-BA27B5F0426F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D175ACE5-8DAA-41B0-A0F1-BA27B5F0426F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D175ACE5-8DAA-41B0-A0F1-BA27B5F0426F}.Release|Any CPU.Build.0 = Release|Any CPU
{86A5B199-BE0A-4575-A4E9-664AA5D9ADDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86A5B199-BE0A-4575-A4E9-664AA5D9ADDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86A5B199-BE0A-4575-A4E9-664AA5D9ADDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86A5B199-BE0A-4575-A4E9-664AA5D9ADDE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3B478466-7705-4F87-B871-52F7F7EF02A6}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,77 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
using TaskProject.Entity;
using static System.Runtime.InteropServices.JavaScript.JSType;
using Task = TaskProject.Entity.Task;
namespace TaskProject.Controllers
{
[ApiController]
[Route("tasks")]
public class TaskController : ControllerBase
{
private static readonly List<Task> Tasks = new();
[HttpGet]
public ActionResult<List<Task>> GetTasks() => Ok(Tasks);
[HttpGet("{id}")]
public ActionResult<Task> GetTask(Guid id)
{
var task = Tasks.FirstOrDefault(t => t.Id == id);
return task is null ? NotFound() : Ok(task);
}
[HttpPost]
public ActionResult<Task> CreateTask([FromBody] TaskDto taskDto)
{
Task task = new Task
{
Title = taskDto.Title,
Description = taskDto.Description,
ProjectId = taskDto.ProjectId,
};
var client = new HttpClient();
var response = client.GetAsync($"http://nginx/projectservice/projects/{task.ProjectId}").Result;
if (!response.IsSuccessStatusCode) return BadRequest("Project not found");
Tasks.Add(task);
return CreatedAtAction(nameof(GetTask), new { id = task.Id }, task);
}
[HttpPut("{id}")]
public ActionResult<Task> UpdateTask(Guid id, [FromBody] TaskDto taskDto)
{
var task = Tasks.FirstOrDefault(t => t.Id == id);
if (task is null) return NotFound();
var client = new HttpClient();
var response = client.GetAsync($"http://nginx/projectservice/projects/{task.ProjectId}").Result;
if (!response.IsSuccessStatusCode) return BadRequest("Project not found");
task.Title = taskDto.Title;
task.Description = taskDto.Description;
task.ProjectId = taskDto.ProjectId;
return Ok(task);
}
[HttpDelete("{id}")]
public IActionResult DeleteTask(Guid id)
{
var task = Tasks.FirstOrDefault(t => t.Id == id);
if (task is null) return NotFound();
Tasks.Remove(task);
return Ok();
}
[HttpDelete("by-project/{projectId}")]
public IActionResult DeleteTasksByProject(Guid projectId)
{
Tasks.RemoveAll(t => t.ProjectId == projectId);
return Ok();
}
}
}

View File

@ -0,0 +1,29 @@
# См. статью по ссылке https://aka.ms/customizecontainer, чтобы узнать как настроить контейнер отладки и как Visual Studio использует этот Dockerfile для создания образов для ускорения отладки.
# Этот этап используется при запуске из VS в быстром режиме (по умолчанию для конфигурации отладки)
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 5002
# Этот этап используется для сборки проекта службы
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["TaskProject/TaskProject.csproj", "TaskProject/"]
RUN dotnet restore "./TaskProject/TaskProject.csproj"
COPY . .
WORKDIR "/src/TaskProject"
RUN dotnet build "./TaskProject.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Этот этап используется для публикации проекта службы, который будет скопирован на последний этап
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./TaskProject.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Этот этап используется в рабочей среде или при запуске из VS в обычном режиме (по умолчанию, когда конфигурация отладки не используется)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "TaskProject.dll"]

View File

@ -0,0 +1,11 @@
namespace TaskProject.Entity
{
public class Task
{
public Guid Id { get; init; } = Guid.NewGuid();
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public Guid ProjectId { get; set; }
}
}

View File

@ -0,0 +1,4 @@
namespace TaskProject.Entity
{
public record TaskDto(string Title, string Description, Guid ProjectId) {}
}

View File

@ -0,0 +1,45 @@
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(5002);
});
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
app.UseCors("AllowAll");
app.UseSwagger(c =>
{
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
{
swaggerDoc.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}/taskservice" } };
});
});
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,52 @@
{
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5079"
},
"https": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7111;http://localhost:5079"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Container (Dockerfile)": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_HTTPS_PORTS": "8081",
"ASPNETCORE_HTTP_PORTS": "8080"
},
"publishAllPorts": true,
"useSSL": true
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:54012",
"sslPort": 44337
}
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>735c8756-0d0c-4166-875d-9dfbf7cf10c4</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
<Controller_SelectedScaffolderID>MvcControllerWithActionsScaffolder</Controller_SelectedScaffolderID>
<Controller_SelectedScaffolderCategoryPath>root/Common/MVC/Controller</Controller_SelectedScaffolderCategoryPath>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,6 @@
@TaskProject_HostAddress = http://localhost:5079
GET {{TaskProject_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,35 @@
services:
nginx:
image: nginx
depends_on:
- projectservice
- taskservice
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
networks:
- app-network
ports:
- 80:80
projectservice:
build:
context: .
dockerfile: ProjectEntityProject/Dockerfile
networks:
- app-network
ports:
- 5001:8080
taskservice:
build:
context: .
dockerfile: TaskProject/Dockerfile
networks:
- app-network
ports:
- 5002:8080
depends_on:
- projectservice
networks:
app-network:
driver: bridge

View File

@ -0,0 +1,18 @@
server {
listen 80;
server_name localhost;
location /projectservice/ {
proxy_pass http://projectservice:5001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /taskservice/ {
proxy_pass http://taskservice:5002/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

View File

@ -0,0 +1,99 @@
# Лабораторная работа 3 - REST API, Gateway и синхронный обмен между микросервисами
## ПИбд-42 || Алейкин Артем
### Описание
В данной лабораторной работе мы разворачиваем два микросервиса:
ProjectEntityProject - проект содержащий логику работы с сущностью 'Проект - Project'.
TaskProject - проект содержащий логику работы с сущностью 'Задача - Task'.
### Файлы-конфигурации
1. docker-compose
```
services:
nginx:
image: nginx
depends_on:
- projectservice
- taskservice
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
networks:
- app-network
ports:
- 80:80
projectservice:
build:
context: .
dockerfile: ProjectEntityProject/Dockerfile
networks:
- app-network
ports:
- 5001:8080
taskservice:
build:
context: .
dockerfile: TaskProject/Dockerfile
networks:
- app-network
ports:
- 5002:8080
depends_on:
- projectservice
networks:
app-network:
driver: bridge
```
nginx: Сервис, который работает как обратный прокси-сервер и маршрутизирует запросы к другим сервисам (projectservice и taskservice), слушая на порту 80.
Зависит от двух других сервисов: projectservice и taskservice.
Прокси-сервер использует конфигурацию, предоставленную в nginx.conf.
projectservice: Сервис, отвечающий за логику работы с проектами, доступный на порту 8080 внутри контейнера (проброшен на порт 5001).
Этот сервис будет обрабатывать запросы, связанные с проектами.
taskservice: Сервис, отвечающий за логику работы с задачами, доступный на порту 8080 внутри контейнера (проброшен на порт 5002).
Этот сервис будет обрабатывать запросы, связанные с задачами.
Зависит от projectservice, так как задачи связаны с проектами.
Сервис nginx будет проксировать запросы с путями /projectservice/ и /taskservice/ на соответствующие контейнеры с сервисами.
2. nginx.conf
```
server {
listen 80;
server_name localhost;
location /projectservice/ {
proxy_pass http://projectservice:5001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /taskservice/ {
proxy_pass http://taskservice:5002/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
Этот файл конфигурирует Nginx для маршрутизации запросов:
location /projectservice/: Все запросы, начинающиеся с /projectservice/, будут проксироваться к сервису projectservice, который работает на порту 5001. Заголовки, такие как Host, X-Real-IP и X-Forwarded-Proto, устанавливаются для правильной передачи информации о запросе.
location /taskservice/: Все запросы, начинающиеся с /taskservice/, будут проксироваться к сервису taskservice, который работает на порту 5002. Аналогично, заголовки проксируются для корректной передачи данных.
Этот конфигурационный файл гарантирует, что запросы к путям /projectservice/ и /taskservice/ будут перенаправляться на соответствующие сервисы через прокси-сервер Nginx.
### Шаги для запуска:
Переходим в корневую папку всего решения и пишем команду:
```
docker-compose up --build
```
Видео демонстрации работы: https://vk.com/video248424990_456239610?list=ln-Doqjb41FQlfo377LN0