Сохранение состояния вошедшего в систему пользователя в ASP .Net Core Web API

У меня есть ASP .Net Core 1.1 Web Api, который обслуживает запросы от внешнего веб-приложения и телефонного приложения. Приложение предназначено для агентов по недвижимости для проведения проверок недвижимости. Таким образом, в системе есть объекты, такие как инспекции, свойства, пользователи, агентства и т. д. Пользователи (т. е. агенты по недвижимости) и свойства принадлежат агентствам. Таким образом, агентство недвижимости может иметь одного или нескольких пользователей и одно или несколько свойств.

Например, пользователь может захотеть просмотреть список всех свойств, проверенных кем-либо из его агентства недвижимости. В этом случае он щелкает где-нибудь в веб-приложении/телефонном приложении, и приложение отправляет HTTP-запрос GET в API для получения списка свойств. Веб-API при получении этого запроса сначала проверяет, какой пользователь это запрашивает, а затем проверяет, к какому агентству принадлежит этот пользователь, а затем возвращает все свойства, которые принадлежат этому агентству. Итак... это [упрощенные] модели, которые у меня есть:

[Table("agency")]
public class Agency
{

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Column("agency_id")]
    public int? Id { get; set; }

    [Column("name")]
    public string Name { get; set; }
}

[Table("user")]
public class User
{

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Column("user_id")]
    public int? Id { get; set; }

    [Column("username")]
    public string Username { get; set; }

    [Column("agency_id")]
    public int AgencyId { get; set; }

    [ForeignKey("AgencyId")]
    public Agency Agency { get; set; }
}

[Table("property")]
public class Property
{

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Column("property_id")]
    public int? Id { get; set; }

    [Column("address")]
    public string Address { get; set; }

    [Column("agency_id")]
    public int AgencyId { get; set; }

    [ForeignKey("AgencyId")]
    public Agency Agency { get; set; }
}

Действие контроллера в веб-API, которое обрабатывает вышеупомянутый запрос, выглядит следующим образом:

[Authorize]
[HttpGet]
public async Task<IActionResult> GetProperties()
{
    // Get Auth0 identifier for the user performing the request
    string nameIdentifier = User.Claims.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
    // Retrieve this user from the database
    User user = await _context.User.SingleOrDefaultAsync(u => u.Username == nameIdentifier);
    // Return a list of properties that belong to the user's agency
    return Ok(_context.Properties
        .Where(p.AgencyId == user.AgencyId));
}

Теперь все запросы GET к API фильтруются следующим образом: API возвращает записи, относящиеся к агентству вошедшего в систему пользователя. Независимо от того, запрашивает ли пользователь список проверок, арендодателей или арендаторов, API всегда возвращает только записи, принадлежащие агентству пользователя.

Теперь я знаю, что ASP .Net Core не имеет состояния, поэтому я не могу сохранить объект пользователя на сервере, когда пользователь входит в систему, и иметь его в памяти, готовым для вызова в каждом действии контроллера. Но я чувствую, что этот круговой обход базы данных КАЖДЫЙ раз, когда API получает любой запрос GET, чтобы узнать, к какому агентству принадлежит этот пользователь, является расточительным. Есть лучший способ это сделать? Я довольно новичок в ASP и веб-разработке в целом. Буду очень признателен за любые идеи/подсказки/указатели.


person Fabricio Rodriguez    schedule 06.07.2017    source источник


Ответы (1)


Теперь я знаю, что ASP .Net Core не имеет состояния, поэтому я не могу сохранить объект пользователя на сервере, когда пользователь входит в систему, и иметь его в памяти, готовым для вызова в каждом действии контроллера. Но я чувствую, что этот круговой обход базы данных КАЖДЫЙ раз, когда API получает любой запрос GET, чтобы узнать, к какому агентству принадлежит этот пользователь, является расточительным.


Welcome to the web, it is a stateless environment. If you need the user record to handle each http request you basically have a few choices:
  1. Получить его из базы данных по любому запросу
  2. Поместите нужную часть записи в сессию
  3. Поместите нужную часть записи в файл cookie

У каждого подхода есть плюсы и минусы.

Вариант 1 предоставит вам самую свежую информацию из записи пользователя, но за счет дополнительного обращения к базе данных в оба конца для любого запроса. И если у вас есть несколько разных записей, которые вам нужны для обработки запроса, вы можете получить несколько обращений к базе данных для каждого запроса в зависимости от того, как вы работаете над архитектурой,

Вариант 2 может устранить этот круговорот базы данных для любого запроса, если ваше приложение Asp.Net Core настроено на использование поставщика сеансов в памяти. Но недостатком является то, что если веб-приложение переработано (что довольно часто, если работает за IIS), сеанс будет очищен. Если у вас есть сеанс, настроенный на использование поставщика базы данных, такого как SqlServer, тогда вы все равно будете совершать круговой обход базы данных для каждого запроса, поскольку он считывается в сеансе из базы данных (но это будет только один туда и обратно вместо, возможно, нескольких, в зависимости от того, что у вас есть помещается в сеанс и сколько циклов обхода базы данных потребовалось для компиляции информации.) Недостатком размещения информации в сеансе с поддержкой базы данных является то, что он заставляет базу данных обращаться к базе данных для каждого запроса, даже если запрос не нуждается в какой-либо информации, которая помещается в сессию. И если вы не будете осторожны, размер данных, хранящихся в сеансе (и запрашиваемых из базы данных при каждом запросе), довольно быстро становится большим. Иногда может быть слишком просто просто бросить его в сеанс, что в конечном итоге может привести к проблемам с производительностью.

Вариант 3 помещает необходимые данные в файл cookie, который будет перемещаться вместе с каждым запросом в заголовке http-запроса. Недостатком этого является то, что он делает каждый запрос файла немного больше (часто даже запросы изображения), и вы, вероятно, захотите ограничить файл cookie только запросами https, чтобы информация не передавалась в открытом виде через Интернет. Этот подход обычно используется только для очень небольших объемов данных.

Это резюме никоим образом не является исчерпывающим, существуют дополнительные способы отслеживания состояния (параметры запроса и т. д.) и дополнительные соображения безопасности для любого выбранного метода. Главный вывод заключается в том, что да, Интернет — это среда без сохранения состояния, но все же есть способы отслеживать состояние, однако все они имеют свои плюсы и минусы, и ни один из способов не всегда является лучшим. Большинство крупных веб-приложений используют вариации всех доступных подходов для различных аспектов своего веб-приложения.

Подробнее о сеансе Asp.NET Core и состоянии приложения можно прочитать здесь: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state

person RonC    schedule 07.07.2017