Задать вопрос
  • Какой курс по asp.net core вы можете посоветовать?

    SunnyPh
    @SunnyPh
    Интересуют только бесплатные курсы, или платные тоже? Один курс по всему ASP.NET, или что то конкретное типа Razor Pages, MVC, Blazor?
  • Как понять, что пользователь SignalR закрыл браузер (или перешел на другую страницу)?

    SunnyPh
    @SunnyPh
    Gedonist, Ну ок, видел реализацию, где используется идентификация пользователей с помощью ASP.NET Identity, далее, ведется список соединений открытых для каждого авторизовавшегося пользователя, так что проблема синхронизации различных девайсов и/или браузеров или девайсов, там решается на уровне идентификации пользователей в каждом из устройств, ну а группы используются, по прямому назначению, что бы управлять коммуникациями внутри групп. Вот в этом плейлисте, есть кое что на эту тему...
  • Как понять, что пользователь SignalR закрыл браузер (или перешел на другую страницу)?

    SunnyPh
    @SunnyPh
    Gedonist, Посмотрел, мне сложно разобраться в чем сейчас проблема.
    Увидел только, что вы пытаетесь реализовать самостоятельно, управление подключениями с помощью групп. Но в SignalR, уже есть готовый функционал для управления группами через вызов await Groups.AddToGroupAsync ( "идентификатор соединения", "название группы") или await Groups.RemoveFromGroupAsync ("идентификатор соединения", "название группы"), посмотрите примеры как это можно будет использовать для создания групп или комнат для общения в вашем проекте, может быть это поможет.
  • Как понять, что пользователь SignalR закрыл браузер (или перешел на другую страницу)?

    SunnyPh
    @SunnyPh
    Если у вас правильно формируется список коннектов в статическом списке _messagesHubUsers в классе Hub на сервере, то далее,
    после удаления элемента в этом статическом списке, нужно как то обратиться к логике вашего класса "UsersListChanged", который вероятно находится у вас на сервере, раз ваше приложение это Blazor-Server.
    И хотелось бы посмотреть его код и проверить что происходит обращение именно к нему, и что там происходит далее...
    А пока что вы просто пытаетесь сделать обратный вызов скрипта на JS - UsersListChanged, на какой то странице у всех активных клиентов.
  • Как универсально сформировать Response?

    SunnyPh
    @SunnyPh
    Можете попробовать самостоятельно создать стандартный ответ из API.
    Например:
    public class APIResponse
        {
            public APIResponse()
            {
                ErrorMessages = new List<string>();
            }
            public HttpStatusCode StatusCode { get; set; }
            public bool IsSuccess { get; set; } = true;
            public List<string> ErrorMessages { get; set; }
            public object Result { get; set; }
        }


    И такой класс с универсальным ответом из API, будет содержать статус код, признак того обработан ли успешно запрос,
    список строк с ошибками и собственно сам результат ответа, как тип Object, где может быть любой тип C#, в том числе массив. Но как по мне это должен быть в любом случае DTO, один или множество, все зависит от запроса.

    Далее, как пример, запрос HttpGet, для получения данных, о конкретном экземпляре , мог бы выглядеть примерно так, с использованием стандартного ответа:

    public async Task<ActionResult<APIResponse>> GetProductById(int id)
            {
                try
                {
                    if (id == 0)
                    {
                        _response.StatusCode = HttpStatusCode.BadRequest;    
                        return BadRequest(_response);
                    }
                   var product = await _productRepository.GetById(id);
                    if (product == null)
                    {
                        _response.StatusCode = HttpStatusCode.NotFound;
                        return NotFound(_response);
                    }
                    _response.Result = product;
                    _response.StatusCode = HttpStatusCode.OK;
                    return Ok(_response);
                }
                catch (Exception ex)
                {
                    _response.IsSuccess = false;
                    _response.ErrorMessages
                         = new List<string>() { ex.ToString() };
                }
                return _response;
            }


    Где _response в данном контексте, это APIResponse.

    Пример реализации такого подхода для WEB API в репозитории.
  • Как отображать в HTML разметки ответы gRPC стрима в asp net core?

    SunnyPh
    @SunnyPh
    SignalR можно использовать для такого класса задач, на бекенде сделать Hub SignalR, на фронте использовать скрипт на JS для работы с серверным Hub, а также кастомный скрипт, который будет изменять текущие значения на странице в HTML, без активных действий пользователя.
  • Как свести ответы сервисов к одной сущности asp net core web api?

    SunnyPh
    @SunnyPh
    Можете попробовать самостоятельно создать стандартный ответ из API.
    Например:
    public class APIResponse
        {
            public APIResponse()
            {
                ErrorMessages = new List<string>();
            }
            public HttpStatusCode StatusCode { get; set; }
            public bool IsSuccess { get; set; } = true;
            public List<string> ErrorMessages { get; set; }
            public object Result { get; set; }
        }


    И такой объект, будет содержать статус код, признак того обработан ли успешно запрос,
    список возможных ошибок и собственно сам результат ответа.

    Далее, ваш метод мог бы выглядеть так, с использованием этого стандартного ответа:

    public async Task<ActionResult<APIResponse>> GetProductById(int id)
            {
                try
                {
                    if (id == 0)
                    {
                        _response.StatusCode = HttpStatusCode.BadRequest;
                        return BadRequest(_response);
                    }
                   var product = await _productRepository.GetById(id);
                    if (product == null)
                    {
                        _response.StatusCode = HttpStatusCode.NotFound;
                        return NotFound(_response);
                    }
                    _response.Result = product;
                    _response.StatusCode = HttpStatusCode.OK;
                    return Ok(_response);
                }
                catch (Exception ex)
                {
                    _response.IsSuccess = false;
                    _response.ErrorMessages
                         = new List<string>() { ex.ToString() };
                }
                return _response;
            }


    Пример реализации такого подхода для WEB API в репозитории.
  • Как лучше сделать быстрое уведомление менеджера о поступившем заказе в ИМ?

    SunnyPh
    @SunnyPh
    Если нужно получать уведомления о новых заказах немедленно в реальном времени, в открытом окне заказов, то это можно сделать если коммуникация между сервером и страницей заказов происходит через веб-сокеты, а не по HTTP запросам. Если, например, бэкенд вашего интернет магазина сделан на .NET, то можно использовать SignalR для этого.
  • Смысл в JWT, если любой его может распарсить?

    SunnyPh
    @SunnyPh
    JWT токен генерируется для каждого успешно авторизованного пользователя, с помощью секретного ключа, так что если пользователь попытается получить данные с ресурса к которому у него уже закончился доступ, меняя просто дату, то такой токен не будет валидными и доступ к ресурсу для такого запроса будет запрещен. На примере .NET Identity в этом видео есть короткие объяснения как происходит генерация JWT токена. Это для C# и ASP.NET, но для других языков и фреймворков, все работает также.
  • Где можно посмотреть реализацию готовых и профессиональных WebAPI в связке с клиентом?

    SunnyPh
    @SunnyPh
    Изучал тему WEB API в .NET 7, используя вот этот репозиторий.
    Здесь предствлен пример как самого WEB API, с реализацией CRUD на базе SQL базы данных, так и клиент ASP.NET MVC, также рассмотрена защита доступа к ресурсам как с помощью встроенной .NET Identity, так и независимого Duende Identity Server.
  • Почему не проходит Authorize?

    SunnyPh
    @SunnyPh
    Посмотри вот этот плейлист. Здесь про авторизацию для приложений на ASP.NET MVC + Web API, версия .NET 7.

    Вот работающий код, для генерации токена, после успешной авторизации в API:
    public async Task<LoginResponseDTO> Login(LoginRequestDTO loginRequestDTO)
            {
                var user = _db.LocalUsers.FirstOrDefault(u => u.UserName.ToLower() == loginRequestDTO.UserName.ToLower()
                && u.Password == loginRequestDTO.Password);
    
                if (user == null)
                {
                    return null;
                }
    
                //if user was found generate JWT Token
    
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(secretKey);
    
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(new Claim[]
                    {
                        new Claim(ClaimTypes.Name, user.Id.ToString()),
                        new Claim(ClaimTypes.Role, user.Role)
                    }),
                    Expires = DateTime.UtcNow.AddDays(7),
                    SigningCredentials = new(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                };
    
                var token = tokenHandler.CreateToken(tokenDescriptor);
                LoginResponseDTO loginResponseDTO = new LoginResponseDTO()
                {
                    Token = tokenHandler.WriteToken(token),
                    User = user
                };
                return loginResponseDTO;
            }


    Вот код контроллера идентификации на уровне WEB приложения ASP.NET MVC, с сохранением токена в текущей сессии.
    public class AuthController : Controller
        {
            private readonly IAuthService _authService;
            public AuthController(IAuthService authService)
            {
                _authService = authService;
            }
    
            [HttpGet]
            public IActionResult Login()
            {
                LoginRequestDTO obj = new();
                return View(obj);
            }
    
            [HttpPost]
            [ValidateAntiForgeryToken]
            public async Task<IActionResult> Login(LoginRequestDTO obj)
            {
                APIResponse response = await _authService.LoginAsync<APIResponse>(obj);
                if (response != null && response.IsSuccess)
                { 
                    LoginResponseDTO model = JsonConvert.DeserializeObject<LoginResponseDTO>(Convert.ToString(response.Result));
    
                    var handler = new JwtSecurityTokenHandler();
                    var jwt = handler.ReadJwtToken(model.Token);
    
                    var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
                    identity.AddClaim(new Claim(ClaimTypes.Name, jwt.Claims.FirstOrDefault(u => u.Type == "name").Value));
                    identity.AddClaim(new Claim(ClaimTypes.Role, jwt.Claims.FirstOrDefault(u=>u.Type=="role").Value));
                    var principal = new ClaimsPrincipal(identity);
                    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
    
    
                    HttpContext.Session.SetString(SD.SessionToken, model.Token);
                    return RedirectToAction("Index","Home");
                }
                else
                {
                    ModelState.AddModelError("CustomError", response.ErrorMessages.FirstOrDefault());
                    return View(obj);
                }
            }
    
            [HttpGet]
            public IActionResult Register()
            {
                return View();
            }
    
    
            [HttpPost]
            [ValidateAntiForgeryToken]
            public async Task<IActionResult> Register(RegisterationRequestDTO obj)
            {
                APIResponse result =  await _authService.RegisterAsync<APIResponse>(obj);
                if (result != null && result.IsSuccess)
                {
                    return RedirectToAction("Login");
                }
                return View();
            }
    
    
            public async Task<IActionResult> Logout()
            {
                await HttpContext.SignOutAsync();
                HttpContext.Session.SetString(SD.SessionToken, "");
                return RedirectToAction("Index","Home");
            }
    
            public IActionResult AccessDenied()
            {
                return View();
            }
        }
  • Какую LMS выбрать Фрилансеру и Руководителю для обучения сотрудников в 2023?

    SunnyPh
    @SunnyPh
    Мне тоже интересен этот вопрос, вот думаю самому написать LMS для себя. Бекенд точно на WEB API на .NET, а фронт еще не решил - React или может быть новомодный Blazor. Где то видел реализацию LMS с полным кодом React + Neыt.js, слышал что сейчас это модно, но у меня не хватает знаний для этого стека.
  • Как добавить сервисы и использовать их в контроллерах?

    SunnyPh
    @SunnyPh
    pshevnin, вам уже ответили кратко но по сути, но если хотите разобраться получше, как добавить доступ к сервису Аутентификации, а также добавить его как компонент в конвейер для обработки запросов в WEB API, можете посмотреть вот в этом плейлисте
  • Как добавить сервисы и использовать их в контроллерах?

    SunnyPh
    @SunnyPh
    В принципе, вы уже разобрались в чем различия версий .NET, но вот в этом видео, все подробно разжевано об этом.
  • Почему в запросе нет тела?

    SunnyPh
    @SunnyPh
    OwDafuq, Извините, тогда я не понял вопроса. И если у вас в проекте с Blazor-Server, нет конвейера с Middlewares, тогда не знаю в чем проблема.
  • Почему в запросе нет тела?

    SunnyPh
    @SunnyPh
    Покажите код из проекта на Blazor Server, там где вы формируете запросы к API, для получения или передачи данных из него. Вы пишите что у вас все Ок? с Blazor WASM, но при этом показываете код для двух проектов, с сервисом доступа к API в Middleware. Однако где формируются такие запросы не совсем понятно. В Blazor, для передачи данных также используются объекты-модели, которые нужно будет задавать в блоке @code{...} компонента. Затем нужно будет настроить получение данных, для экземпляра такого объекта из API. Я бы, сначала создал класс Репозиторий для этого, в котором настроен доступ к API, а потом уже внутри проекта Blazor-Server запрашивал такие данные из API. Вот в этом видео, показан пример такой настройки, только вместо обращения к API, используется обращение к классу репозитория, внутри проекта на Blazor-Server.
  • Как вывести заработок с продаж в Microsoft Partner Center (Microsoft Store)?

    SunnyPh
    @SunnyPh
    Можно получить на карту иностранного банка, а далее через крипту перевести в РФ. Я так делал, только наоборот, то есть у меня есть карта иностранного банка на жену иностранку, а также есть именной, не обезличенный счет в официальном крипто обменнике, с банковской лицензией в стране, в Юго-Востончой Азии (где я сейчас и проживаю по ВНЖ). Так вот ситуация было обратная, в том смысле что мой бизнес партнер имел крипту, но сам находился в Евросоюзе, и что бы уменьшить налоги с продажи крипты, он переводил крипту мне, а я ему переводил банковским переводом на его европейский банк в евро, якобы за выполненные работы, то есть как его доход от бизнеса.....Как то так....Иначе вариков нет...
  • Blazor Server, как получить тело запроса?

    SunnyPh
    @SunnyPh
    OwDafuq, Не знаю, поможет вам это или нет, но содержимое передаваемое через Hub, можно посмотреть в браузере, в NetWork, в закладке с вебсокетами WS, где можно увидет название вашего Hub, и сообщение если оно передается в json а не в messagepack 64eda39b13a12776998668.jpeg