Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/5: Рейтинг темы: голосов - 5, средняя оценка - 5.00
 Аватар для Worldmaster
323 / 190 / 45
Регистрация: 25.08.2011
Сообщений: 1,263

WebApplication обработка CORS

06.12.2023, 09:46. Показов 1055. Ответов 16
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте.

В продолжение темы вебсервера.

Оформил приложение. Включил обработку CORS запросов и целый день все работало стабильно. Перезапускал сервер, отлаживал код.

Сегодня с утра решил продолжить и гугель опять сыпет ошибку CORS и блокирует пакеты.
Как так то? Что я и где забыл??


C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
 
builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy =>
                      {
                          policy.WithOrigins("https://remotehost.ru")
                          .AllowAnyHeader()
                          .AllowAnyMethod()
                          .SetIsOriginAllowed((host) => true);
                      });
});
 
var app = builder.Build();
 
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
 
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();

И еще хотел уточнить по поводу логов.
Вроде также везде включил но никаких ошибок ни в консоль ни в студию не сыпется. И как проверить срабатывает ли вообще обработка CORS?

C#
1
2
3
4
5
6
7
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
 
builder.Logging.AddFilter("Microsoft", LogLevel.Trace); // Категория Microsoft
builder.Logging.AddFilter("System", LogLevel.Trace); // Категория System
builder.Logging.AddFilter("WebApplication1", LogLevel.Trace); // Замените YourAppNamespace на пространство имен вашего приложения
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
06.12.2023, 09:46
Ответы с готовыми решениями:

Как переделать WebApplication в WebSite
Как переделать WebApplication в WebSite подскажите пожалуйста Добавлено через 2 часа 43 минуты using System; using...

Hibernate WebApplication
Создал проект WebApplication в NetBeans. Создал вручную hibernate.cfg.xml и классы сущностей. Есть пустая база данных SQL. Как смаппить мои...

Заливание WebApplication на хостинг
Всем привет. Хотел попрактиковаться в сайтокрафте. Зарегался на http://somee.com/ оформил у них бесплатный хостинг. А вот тут...

16
1339 / 919 / 264
Регистрация: 08.08.2014
Сообщений: 2,763
06.12.2023, 10:24
Если запрос от Гугла, то он должен быть в списке 'WithOrigins'. Причём, по полному совпадению. У меня в одном из проектов так настроено (более ничего и нигде, только этот код). Секция 'EnabledCorsOrigins' конфига содержит коллекцию всех нужных адресов:
C#
1
2
3
4
5
        app.UseCors(builder =>
        {
            var origins = configuration.GetSection("EnabledCorsOrigins").Get<string[]>();
            builder.WithOrigins(origins).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
        });
Добавлено через 2 минуты
При неудаче проверки CORS да, в логах появляется тематическое сообщение об этом с указанием точного адреса, который проверку не прошёл.
Code
1
2
CORS policy execution failed. 
Request origin https://<....> does not have permission to access the resource.
Но там не только это, там ещё возможна ситуация, когда сервер CORS корректно отработал, но не вернул заголовок 'Access-Control-Allow-Origin' (вот тот код выше его как-то сам возвращает), тогда именно на стороне браузера ответ не получится прочитать (с соответствующим уведомлением в консоли браузера).

Добавлено через 1 минуту
Насчёт логов и консоли - они совсем пустые или туда всё же выводится информация о запуске хоста с номером порта и прочим? Оно просто должно из коробки работать при создании WebAPI-проекта из дефолтного (погодного) шаблона, вообще без какого-либо дополнительного конфигурирования.

Для теста можно попробовать запросить логгер и явным образом туда что-нибудь записать.

Добавлено через 18 минут
В общем, даже такой минимальный сервер с пустым конфигом, выдаёт в консоль (и в отладке, и релизе) как внутренние сообщения самого фреймворка, так и пользовательские. Надо смотреть, что у вас в конфигах и настройке самого 'app':
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
using Microsoft.AspNetCore.Mvc;
 
var builder = WebApplication.CreateBuilder(args);
 
var app = builder.Build();
 
app.MapGet("/", ([FromServices] ILogger<Program> logger) =>
{
    logger.LogInformation("=> root Ok done");
    return "Ok.";
});
 
app.Run();
0
 Аватар для Worldmaster
323 / 190 / 45
Регистрация: 25.08.2011
Сообщений: 1,263
06.12.2023, 10:44  [ТС]
Цитата Сообщение от kotelok Посмотреть сообщение
При неудаче проверки CORS да, в логах появляется тематическое сообщение об этом с указанием точного адреса, который проверку не прошёл.
Вообще нету ничего.


Цитата Сообщение от kotelok Посмотреть сообщение
(с соответствующим уведомлением в консоли браузера).
Вот что пишет в консоли браузера.
Access to fetch at 'https://xxxx:4001/Home' from origin 'https://yyyyy' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Ваше предположение верно.
Цитата Сообщение от kotelok Посмотреть сообщение
не вернул заголовок 'Access-Control-Allow-Origin'

Цитата Сообщение от kotelok Посмотреть сообщение
туда всё же выводится информация о запуске хоста с номером порта и прочим?

Стандартная инфа то конечно выводится. Если я в процессе работы пишу в _logger что нибудь то оно конечно выводиться.
Я имею ввиду про обработку CORS.


В вашем коде объект configuration откуда взялася??
И что в секции "EnabledCorsOrigins"??
0
1339 / 919 / 264
Регистрация: 08.08.2014
Сообщений: 2,763
06.12.2023, 11:35
Цитата Сообщение от Worldmaster Посмотреть сообщение
В вашем коде объект configuration откуда взялася??
Это дефолтный конфиг приложения, который по умолчанию загружается из 'appsettings.<env>.json' и доступен через свойство 'builder.Configuration' (или через DI). В данном случае это не принципиально, просто мне удобнее параметры CORS через конфиг задавать, чтобы в случае чего их можно было на лету подкорректировать и чтобы в разных контурах можно было разные указать.

Цитата Сообщение от Worldmaster Посмотреть сообщение
И что в секции "EnabledCorsOrigins"
Просто массив строк:
Code
1
"EnabledCorsOrigins": [ "https://some.domain.ru", "https://www.domain.ru", "https://domain.ru" ]
0
 Аватар для Worldmaster
323 / 190 / 45
Регистрация: 25.08.2011
Сообщений: 1,263
06.12.2023, 11:46  [ТС]
kotelok, неа .. не работает.

А может ли как то влиять на это микротик?? У него настроена трансляция с 4001 порта на мой комп на 7001 порт.

Но все равно не понятно почему в каких то случаях нормально проходит а когда то нет.
0
1339 / 919 / 264
Регистрация: 08.08.2014
Сообщений: 2,763
06.12.2023, 11:59
Цитата Сообщение от Worldmaster Посмотреть сообщение
А может ли как то влиять на это микротик?? У него настроена трансляция с 4001 порта на мой комп на 7001 порт
Не должно. У меня сервисы хостятся во внутренней сети и слушают запросы на 'http://<IP>:<port>/', но фактическое обращение к ним идёт по 'https://api.<domain>.ru' (на nginx, который потом уже перенаправляет на конкретный сервис). А в случае простой прокидки порта, там вообще всё прозрачно должно быть.

Насколько я понял, браузер сам добавляет HTTP-заголовок 'Origin', куда вписывает полный адрес. И далее ожидает, что сервер и сам этот запрос не отвергнет, и нужные заголовки в ответ пришлёт (и если не пришлёт, то браузер ответ в JS не отдаёт).

P.S.: полагаю, тут нужен кто-то более опытный, кто хорошо все нюансы CORS знает.
0
 Аватар для Worldmaster
323 / 190 / 45
Регистрация: 25.08.2011
Сообщений: 1,263
06.12.2023, 12:34  [ТС]
Цитата Сообщение от kotelok Посмотреть сообщение
браузер сам добавляет HTTP-заголовок 'Origin', куда вписывает полный адрес.
А как то просмотреть то можно?? В консоли как то не полностью отображается. Или сам CORS отладить через POSTMAN например??

Там есть OPTION но он выдает ошибку 405 при запросе.
0
1339 / 919 / 264
Регистрация: 08.08.2014
Сообщений: 2,763
06.12.2023, 14:15
Я через консоль Хрома отлаживаю:
JavaScript
1
fetch('https://<some>.<domain>.ru/api/users').then(res => res.json()).then(console.log);
Браузер отправляет с ориждином того сайта, который во вкладке с консолью загружен. Ну и на сервере в логах появляется:
CORS policy execution failed.
Request origin https://stackoverflow.com does not have permission to access the resource.
А браузер в консоли сообщает:
making-http-requests-using-chrome-developer-tools:1 Access to fetch at 'https://<some>.<domain>.ru/api/users' from origin 'https://stackoverflow.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Добавлено через 5 минут
Через Postman можно отправить запрос и явно указать HTTP-заголовок 'Origin'. Сервер в этом случае в логах тоже отметит, что 'CORS failed', но сам Постман ответ при этом корректно отобразит, т.к. он эти политики безопасности не проверяет. Ну т.е. это не закроет сценарий, когда сервер-ОК, а браузер не доволен.

Добавлено через 3 минуты
Хотя, наверное, можно и вручную в Postman посмотреть, есть ли в ответе заголовок 'Access-Control-Allow-Origin' с исходным значением 'Origin'. Но по мне так, через браузер надёжнее.

Добавлено через 1 час 23 минуты
В общем, вот такой минимальный пример позволяет запрашивать ресурс из браузера с любого 'Origin':
C#
1
2
3
4
5
6
7
8
9
10
var app = WebApplication.CreateBuilder(args).Build();
 
app.MapGet("/", (HttpContext ctx) =>
{
    ctx.Response.Headers.Add("Access-Control-Allow-Origin", "*");
 
    return "{ \"result\": 42 }";
});
 
app.Run();
Можно оформить в виде мидлвара. Другой вопрос, что это не особо безопасно. И, вероятно, это не единственный механизм, который может применяться браузерами. Так что всё же более корректно будет реализовывать именно через настройку CORS штатными средствами, где могут быть учтены разные возможные сценарии.
0
 Аватар для Worldmaster
323 / 190 / 45
Регистрация: 25.08.2011
Сообщений: 1,263
07.12.2023, 08:25  [ТС]
Цитата Сообщение от kotelok Посмотреть сообщение
Ну и на сервере в логах появляется:
Вообще в логах сервера ничего.
Но браузер в своей консоли сообщил о блокировке.


Цитата Сообщение от kotelok Посмотреть сообщение
пример позволяет запрашивать ресурс из браузера с любого 'Origin':
Добавил этот код. Добавил бряк внутри. Но код не выполняется вообще.


Цитата Сообщение от kotelok Посмотреть сообщение
Так что всё же более корректно будет реализовывать именно через настройку CORS штатными средствами, где могут быть учтены разные возможные сценарии.

Ну ни в какую не хочет работать. Причем я не пойму почему оно в какой то момент нормально начинает работать.

Вот такие заголовки ответов показывается в браузере:

Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Content-Length:10200
Content-Type:text/html; charset=utf-8
Expires:Mon, 04 Dec 1999 21:29:02 GMT
Pragma:no-cache

0
1339 / 919 / 264
Регистрация: 08.08.2014
Сообщений: 2,763
07.12.2023, 08:47
Цитата Сообщение от Worldmaster Посмотреть сообщение
Добавил этот код. Добавил бряк внутри. Но код не выполняется вообще.
Это на конкретный путь пример при реализации через minimal-API.

Чтобы оно вообще для всех запросов отрабатывало, попробуйте для теста добавить мидлвар куда-нибудь в начала пипелайна (т.е. до контроллеров):
C#
1
2
3
4
5
6
    app.Use(async (context, next) =>
    {
        context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
        //context.Response.Headers.Add("Access-Control-Allow-Origin", context.Request.Headers["Origin"]);
        await next(context);
    });
И для чистоты эксперимента закомментируйте все настройки CORS штатными средствами.
0
 Аватар для Worldmaster
323 / 190 / 45
Регистрация: 25.08.2011
Сообщений: 1,263
07.12.2023, 09:17  [ТС]
Цитата Сообщение от kotelok Посмотреть сообщение
попробуйте для теста добавить мидлвар куда-нибудь в начала пипелайна
Эта штука вроде бы отрабатывает.

Сделал вот так:

C#
1
2
3
4
5
6
7
8
9
app.Use(async (context, next) =>
{
 
    // Добавляем заголовок к каждому ответу
    context.Response.Headers.Add("Access-Control-Allow-Origin", "https://www.cyberforum.ru");
 
    // Продолжаем обработку запроса
    await next();
});

Иду на постман и запрашиваю OPTION и вижу такой результат.



то есть нормально вроде как отрабатывает.

Далее в консоли пишу запрос fetch и на тебе ошибку ((

No 'Access-Control-Allow-Origin' header is present on the requested resource.

Че он врет то. Вроде бы сервер то нормально отработал или нет.
0
1339 / 919 / 264
Регистрация: 08.08.2014
Сообщений: 2,763
07.12.2023, 09:24
Цитата Сообщение от Worldmaster Посмотреть сообщение
Че он врет то. Вроде бы сервер то нормально отработал или нет.
Добавьте логирование заголовка 'Origin', который присылает браузер. Ответный заголовок 'Access-Control-Allow-Origin' должен с ним совпадать:
C#
1
Debug.WriteLine(context.Request.Headers["Origin"]);
Может, у вас консоль браузера не оттуда открылась или ещё что.
Ну и попробуйте всё же именно через "*".
И именно GET-запрос на тот ресурс, что реально есть в API и реально отдаёт данные.
0
 Аватар для Worldmaster
323 / 190 / 45
Регистрация: 25.08.2011
Сообщений: 1,263
07.12.2023, 09:43  [ТС]
Цитата Сообщение от kotelok Посмотреть сообщение
Добавьте логирование заголовка 'Origin', который присылает браузер.

Вот так сделал:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
app.Use(async (context, next) =>
{
    Debug.WriteLine(context.Request.Headers["Origin"]);  <- это почему то не выводиться нигде. хотя постман показывает что хидеры вышли. Консоль не пишет ничего.
     
    // Добавляем заголовок к каждому ответу
    //context.Response.Headers.Add("Access-Control-Allow-Origin", "https://www.cyberforum.ru");
    //context.Response.Headers.Add("Allow", "POST, GET");
    context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
    context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "*" });
    context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET, POST, PUT, DELETE, OPTIONS" });
    context.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
    context.Response.StatusCode = 200;
 
    // Продолжаем обработку запроса
    await next(); 
});

Вот контроллер:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[ApiController]
[Route("[controller]")] 
public class HomeController : ControllerBase
{
    private readonly ILogger<HomeController> _logger;
 
    public HomeController(ILogger<HomeController> logger)
    {
        _logger = logger;
        _logger.Log( LogLevel.Information, "New request ...");
    }
 
    [HttpGet]
    public async Task<IActionResult> GetResponse(UserInfo requestData)
    {
        bool Exist = false;
        await Task.Factory.StartNew(() => Thread.Sleep(1));
        return Ok(new { Message = "GET response" }); 
 
    }
 
 
    [HttpPost]       
    public async Task<IActionResult> PostResponse(UserInfo requestData)
    {
        bool Exist = false;
        _logger.Log(LogLevel.Information, "New UserInfo request ...");
        await Task.Factory.StartNew(() => Thread.Sleep(1));
        return Ok(new { Message = "POST response" });
    }
}

Вот команды апи. Они точно отрабатывают и возвращают ответ.



А как в app.Use добавить логгер??
0
1339 / 919 / 264
Регистрация: 08.08.2014
Сообщений: 2,763
07.12.2023, 10:00
Цитата Сообщение от Worldmaster Посмотреть сообщение
это почему то не выводиться нигде
Это выводится в консоль VS, если проект запущен в debug-сборке.

Цитата Сообщение от Worldmaster Посмотреть сообщение
А как в app.Use добавить логгер??
В таком синтаксисе не знаю. Но можно оформить в виде полноценного класса/метода и запросить логер через DI (там просто минимальный класс и один метод):
https://learn.microsoft.com/en... etcore-8.0
0
 Аватар для Worldmaster
323 / 190 / 45
Регистрация: 25.08.2011
Сообщений: 1,263
07.12.2023, 10:04  [ТС]
Блин... методом GET заработало. fetch с форума возвращает результат.
Но если запрос делать постом с данными то пишет что такая ошибка:

Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Теперь то что ему надо?
0
1339 / 919 / 264
Регистрация: 08.08.2014
Сообщений: 2,763
07.12.2023, 10:13
Цитата Сообщение от Worldmaster Посмотреть сообщение
preflight
Это связано с тем самым OPTIONS-запросом, который браузер сам отправляет перед POST-запросом и на который тоже надо как-то правильно ответить:
https://developer.mozilla.org/... ht_request
0
 Аватар для Worldmaster
323 / 190 / 45
Регистрация: 25.08.2011
Сообщений: 1,263
07.12.2023, 10:33  [ТС]
Цитата Сообщение от kotelok Посмотреть сообщение
Это связано с тем самым OPTIONS-запросом,
Так я же отвечаю правильно. И статус 200 поставил и остальное.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.Use(async (context, next) =>
{ 
    context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
    context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "*" });
    context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET", "POST", "PUT", "DELETE", "OPTIONS" });
    context.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
 
    context.Response.Headers.Add("Connection", new[] { "keep-alive" });
    context.Response.Headers.Add("Access-Control-Max-Age", new[] { "86400" });
    context.Response.StatusCode = 200;
 
    // Продолжаем обработку запроса
    await next();
});
Логи добавил. Спасибо. Полезная штука.

Добавлено через 7 минут
Я разобрался.
Короче сервер на запрос OPTION отправляет все правильно, однако он отправляет ответ 202. Типа нет данных.
Реализовал в контроллере метод для этого запроса и сразу все заработало.

C#
1
2
3
4
5
   [HttpOptions]
   public async Task<IActionResult> GetOptions()
   {
       return Ok(new { Message = "GET response" });
   }
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
07.12.2023, 10:33
Помогаю со студенческими работами здесь

Разворачивание WebApplication на IIS
Доброе время суток, Господа! Можете скинуть толковую инструкцию по которой я смогу развернуть WebApplication на IIS. Заранее...

Выбрать начальную страницу в WebApplication
С недавних пор перешел на intelijIdea. При запуске web приложения запускается определенный сервлет, а я хочу чтоб показывалась моя...

WebApplication. Не подключается к базе данных MySQL
Здравствуйте, Пишу webApplication в netbeans, server tomcat. Пытаюсь подключится к базе данных через сервлет: ...

Как вывести обычный MessageBox в WebApplication в C#?
Товарищи как вывести обычный MessageBox в WebApplication в C#? Заранее благодарю!

Не выводит список моей коллекции через поиск WebApplication Net.Core (404)
Проблема на самой начальной стадии. Коллекция есть, интерфейсы есть, контроллер прописан, билдер сервисов билдит, вроде всё на месте, но...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
17
Ответ Создать тему
Новые блоги и статьи
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка. Рецензия / Мнение/ Перевод Сайт называется reddit: The Thinkpad X220 Tablet is the best budget school laptop period. Это. . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru