Что такое код asp logextfileprotocolversion

Содержание

ASP.NET Core — Написание ясного кода в ASP.NET Core с использованием встраивания зависимостей

Эта статья основана на ASP.NET Core 1.0 предварительной версии RC1. Некоторая информация может быть изменена при выпуске версии RC2.

Продукты и технологии:

ASP.NET Core 1.0

В статье рассматриваются:

  • примеры жесткого связывания;
  • написание «честных» классов;
  • встраивание зависимостей в ASP.NET Core;
  • код с возможностью модульного тестирования;
  • тестирование обязанностей контроллера.

ASP.NET Core 1.0 является полностью переписанной ASP.NET, и одной из основных целей в этой новой инфраструктуре является более модульная архитектура. То есть приложения должны иметь возможность использовать только те части инфраструктуры, которые им нужны, причем инфраструктура предоставляет зависимости по мере того, как они запрашиваются. Более того, разработчики, создающие приложения на основе ASP.NET Core, должны иметь возможность использовать ту же функциональность, чтобы поддерживать свои приложения свободно связанными и модульными. С появлением ASP.NET MVC группа ASP.NET значительно улучшила поддержку со стороны инфраструктуры в написании свободно связанного кода, но все равно было очень легко попасть в ловушку жесткого связывания, особенно в классах контроллеров.

Жесткое связывание

Жесткое связывание (tight coupling) хорошо подходит для демонстрационного программного обеспечения. Если вы посмотрите на типичное приложение-пример, показывающее, как создавать сайты на основе ASP.NET MVC (версий 3–5), то скорее всего найдете код, подобный следующему (взят из класса DinnersController приложения-примера NerdDinner, использующего MVC 4):

Глядя на код, чтобы оценить степень его связывания, помните фразу «new является связующим».

Этот тип кода очень сложен в модульном тестировании, потому что NerdDinnerContext создается в процессе конструирования класса и требует подключения к базе данных. Неудивительно, что такие демонстрационные приложения нечасто включают какие-либо модульные тесты. Однако ваше приложение может выиграть от нескольких модульных тестов, даже если вы не занимаетесь разработкой на основе тестов, так что было бы лучше писать код, который можно было бы протестировать. Более того, этот код нарушает принцип Don’t Repeat Yourself (DRY) (принцип «не повторяйся»), поскольку каждый класс контроллера, так или иначе обращающийся к данным, содержит один и тот же код для создания EF-контекста базы данных (Entity Framework). Это делает внесение будущих изменений более дорогостоящим и подверженным ошибкам, особенно по мере развития приложения в течение длительного времени.

Глядя на код, чтобы оценить степень его связывания, помните фразу «new является связующим». То есть везде, где экземпляр класса создается с помощью ключевого слова new, ваша реализация связывается конкретно с этим кодом реализации. Dependency Inversion Principle (принцип инверсии зависимостей) (bit.ly/DI-Principle) утверждает: «Абстракции не должны зависеть от деталей — детали должны зависеть от абстракций». В этом примере детали того, как контроллер извлекает данные для передачи представлению, зависят от деталей того, как эти данные извлекаются, а именно от EF.

Вдобавок к ключевому слову new «статическое сцепление» является еще одним источником жесткого связывания, которое затрудняет тестирование и сопровождение приложений. В предыдущем примере имеется зависимость от системных часов компьютера в виде вызова DateTime.Now. Это связывание усложнило бы создание набора тестовых Dinners для использования в некоторых модульных тестах, так как их свойства EventDate пришлось бы устанавливать относительно текущему показанию часов. Это связывание можно было бы удалить из данного метода несколькими способами, самый простой из которых — переложить заботу об этом на какую-то новую абстракцию, возвращающую Dinners, чтобы это больше не было частью метода. В качестве альтернативы я мог бы сделать значение параметром, чтобы метод возвращал все Dinners после предоставленного параметра DateTime вместо использования только DateTime.Now. Наконец, можно было бы создать абстракцию для текущего времени и ссылаться на текущее время через эту абстракцию. Это может оказаться хорошим подходом, если приложение часто ссылается на DateTime.Now. (Кроме того, заметьте, что, поскольку эти обеды [dinners] предположительно происходят в разных часовых поясах, тип DateTimeOffset может быть более эффективным выбором в реальном приложении.)

Будьте честны

Другая проблема с сопровождением кода вроде этого — он нечестен со взаимодействующими с ним объектами. Вы должны избегать написания классов, экземпляры которых можно создавать в недопустимых состояниях, так как это является частым источником ошибок. Таким образом, все, что необходимо вашему классу для выполнения своих задач, должно предоставляться через его конструктор. Как утверждает Explicit Dependencies Principle (принцип явных зависимостей) (bit.ly/ED-Principle), «методы и классы должны явным образом требовать любые взаимодействующие объекты (collaborating objects), нужные им для корректной работы». Класс DinnersController имеет лишь конструктор по умолчанию, а это подразумевает, что ему не надо взаимодействовать с какими-либо объектами для корректной работы. Но что будет, если вы подвергнете его тесту? Что сделает этот код, если вы запустите его из нового консольного приложения, которое ссылается на MVC-проект?

Первое, что не удастся в этом случае, — попытка создать экземпляр EF-контекста. Код сгенерирует исключение InvalidOperationException: «No connection string named ‘NerdDinnerContext’ could be found in the application config file.» (в конфигурационном файле приложения не найдена строка подключения с именем ‘NerdDinnerContext’). Меня обманули! Для работы этому классу нужно больше, чем заявляет его конструктор! Если классу необходим какой-то способ доступа к наборам экземпляров Dinner, он должен запрашивать их через свой конструктор (или как параметры в своих методах).

Встраивание зависимостей

Встраивание зависимостей (dependency injection, DI) относится к передаче зависимостей класса или метода в виде параметров вместо «зашивания» в код этих связей через new или статические вызовы. Это набирающий популярность метод в .NET-разработке из-за обеспечения им разъединения приложений, в которых он применяется. В ранних версиях ASP.NET преимущества DI не использовались, и, хотя в ASP.NET MVC и Web API наблюдается прогресс в отношении его поддержки, ни одна из инфраструктур до сих пор не предоставляет полной поддержки, в том числе контейнер для управления зависимостями и жизненными циклами их объектов. В ASP.NET Core 1.0 DI не просто полностью поддерживается — оно повсеместно применяется в самом продукте.

В ASP.NET Core 1.0 DI не просто полностью поддерживается — оно повсеместно применяется в самом продукте.

ASP.NET Core не только поддерживает DI, но и включает DI-контейнер, также называемый контейнером Inversion of Control (IoC) или контейнером сервисов. Каждое приложение ASP.NET Core конфигурирует свои зависимости, используя этот контейнер, в методе ConfigureServices класса Startup. Данный контейнер обеспечивает необходимую базовую поддержку, но может быть заменен пользовательской реализацией, если в этом есть потребность. Более того, EF Core также имеет встроенную поддержку DI, поэтому ее конфигурирование в приложении ASP.NET Core сводится к простому вызову метода расширения. Для этой статьи я создал ответвление NerdDinner с именем GeekDinner. EF Core конфигурируется, как показано ниже:

После этого довольно легко запросить через DI экземпляр GeekDinnerDbContext от класса контроллера вроде DinnersController:

Заметьте, что нет ни одного экземпляра ключевого слова new; все зависимости, нужные контроллеру, передаются через его контроллер, и заботится об этом за меня DI-контейнер ASP.NET. В процессе написания приложения мне незачем беспокоиться об инфраструктуре, участвующей в разрешении зависимостей, запрашиваемых моими классами через свои конструкторы. Конечно, при желании можно изменить это поведение, даже заменить контейнер по умолчанию другой реализацией. Поскольку теперь мой класс контроллера следует принципу явных зависимостей, я знаю, что для его работы нужно предоставить экземпляр GeekDinnerDbContext. Выполнив небольшую подготовку для DbContext, я могу создать экземпляр контроллера сам по себе, как демонстрирует это консольное приложение:

При конструировании DbContext в EF Core требуется несколько больше работы, чем в EF6, где просто принималась строка подключения. Дело в том, что, как и ASP.NET Core, EF Core спроектирован в расчете на большую модульность. Обычно вам не понадобится иметь дело напрямую с DbContextOptionsBuilder, так как он используется «за кулисами», когда вы конфигурируете EF через методы расширения вроде AddEntityFramework и AddSqlServer.

А можно ли это протестировать?

Тестирование приложения вручную является важным — вам нужна возможность запустить его и убедиться, что оно действительно запускается и дает ожидаемый вывод. Но поступать так всякий раз, когда вносится какое-то изменение, — пустая трата времени. Одно из значимых преимуществ свободно связанных приложений в том, что они, как правило, лучше поддаются модульному тестированию, чем жестко связанные. Еще важнее, что ASP.NET Core и EF Core гораздо проще в тестировании, чем их предшественники. Для начала я напишу простой тест, который напрямую передает в контроллер какой-то DbContext, который был сконфигурирован на использование хранилища в памяти. Я настрою GeekDinnerDbContext, используя параметр DbContextOptions, который предоставляется этим контекстом через конструктор:

Сконфигурировав это в тестовом классе, легко написать тест, показывающий, что Model в ViewResult возвращает корректные данные:

Конечно, здесь пока что мало логики для тестирования, поэтому данный тест ничего особенного и не проверяет. Критики возразили бы, что этот тест малозначим, и я согласился бы с ними. Однако это отправная точка для будущего теста, когда появится больше логики, что и будет сделано в самом ближайшем будущем. Но сначала, хоть EF Core и поддерживает модульное тестирование в памяти, я все же позабочусь о предотвращении прямого связывания с EF в своем контроллере. Нет никаких причин для связывания обязанностей UI с обязанностями инфраструктуры доступа к данным — по сути, это нарушило бы другой принцип, Separation of Concerns (принцип разделения обязанностей).

Избегайте зависимости от того, что не используется

Принцип отделения интерфейса (Interface Segregation Principle) (bit.ly/LS-Principle) утверждает, что классы должны зависеть только от той функциональности, которую они действительно используют. В случае нового DinnersController с поддержкой DI тот по-прежнему зависит от всего DbContext. Вместо склеивания реализации контроллера с EF можно было бы задействовать некую абстракцию, которая предоставляла бы необходимую функциональность.

Что на самом деле нужно этому методу действия (action method) для должного функционирования? Определенно не весь DbContext. Ему даже не требуется доступ к полному свойству Dinners контекста. Ему достаточно возможности отображать экземпляры Dinner соответствующей страницы. Простейшая .NET-абстракция, представляющая это, — IEnumerable . Поэтому я определю интерфейс, который просто возвращает IEnumerable , и это удовлетворит (большую часть) требований метода Index:

Я называю это репозитарием, поскольку он следует такому шаблону: он абстрагирует доступ к данным интерфейсом, подобным набору. Если по какой-то причине вам не нравится шаблон репозитария или имя, вы можете назвать его IGetDinners, IDinnerService или как угодно иначе (мой рецензент предложил ICanHasDinner). Независимо от того, как вы назовете этот тип, он будет служить той же цели.

Одно из значимых преимуществ свободно связанных приложений в том, что они, как правило, лучше поддаются модульному тестированию, чем жестко связанные.

Покончив с этим, теперь подстроим DinnersController для приема IDinnerRepository в качестве параметра конструктора вместо GeekDinnerDbContext и вызова метода List вместо прямого обращения к Dinners DbSet:

К этому моменту можно скомпилировать и запустить ваше веб-приложение, но вы столкнетесь с исключением, если перейдете к /Dinners: InvalidOperationException («Unable to resolve service for type ‘GeekDinner.Core.Interfaces.IdinnerRepository’ while attempting to activate GeekDinner.Controllers.DinnersController.») («Не удалось разрешить сервис для типа ‘GeekDinner.Core.Interfaces.IdinnerRepository’ при попытке активировать GeekDinner.Controllers.DinnersController.»). Я пока что не реализовал интерфейс, и, как только это будет сделано, мне также понадобится сконфигурировать свою реализацию для использования, когда DI будет выполнять запросы к IDinnerRepository. Реализация этого интерфейса тривиальна:

Заметьте, что прямое связывание реализации репозитария с EF совершенно нормально. Если мне понадобится заменить EF, я просто создам новую реализацию интерфейса. Этот класс реализации является частью инфраструктуры моего приложения, которая представляет собой одно из мест в приложении, где мои классы зависят от конкретных реализаций.

Чтобы сконфигурировать ASP.NET Core на встраивание правильной реализации, когда классы запрашивают IDinnerRepository, нужно добавить следующую строку кода в конец ранее показанного метода ConfigureServices:

Это выражение инструктирует DI-контейнер ASP.NET Core использовать экземпляр DinnerRepository всякий раз, когда требуется разрешить тип, зависимый от экземпляра IDinnerRepository. Scoped означает, что для каждого веб-запроса, обрабатываемого ASP.NET, будет использоваться один экземпляр. Также можно добавлять сервисы, указывая жизненные циклы Transient или Singleton. В данном случае подходит Scoped, поскольку мой DinnerRepository зависит от DbContext, который тоже использует жизненный цикл Scoped. Вот краткое описание доступных сроков жизни объектов.

  • Transient Используется новый экземпляр типа всякий раз, когда запрашивается этот тип.
  • Scoped При первом запросе в рамках данного HTTP-запроса создается новый экземпляр типа, а затем он повторно используется для всех последующих типов, разрешаемых при этом HTTP-запросе.
  • Singleton Единственный экземпляр типа создается один раз и используется всеми последующими запросами для этого типа.

Встроенный контейнер поддерживает несколько способов конструирования типов, которые он будет предоставлять. Самый типичный случай — простая передача типа контейнеру, после чего тот попытается создать экземпляр этого типа, предоставляя ему любые зависимости, которые тип запрашивает по мере выполнения. Вы также можете указать лямбда-выражение для конструирования типа или — в случае жизненного цикла Singleton — передать полностью сконструированный экземпляр в ConfigureServices при его регистрации.

После подключения DI приложение выполняется, как и раньше. Теперь, как показано на рис. 1, я могу тестировать его с применением новой абстракции, используя имитацию или заглушку реализации интерфейса IDinnerRepository вместо того, чтобы напрямую полагаться на EF в коде теста.

Рис. 1. Тестирование DinnersController, используя имитирующий объект

Этот тест работает независимо от того, откуда берется список экземпляров Dinner. Вы могли бы переписать код для доступа к данным, чтобы использовать другую базу данных, Azure Table Storage или XML-файлы, а контроллер все равно работал бы точно так же. Конечно, в данном случае он не делает ничего особенного, поэтому вам, возможно, интересно…

А как насчет реальной логики?

До сих пор я не реализовал никакой реальной бизнес-логики — у меня были лишь простые методы, возвращающие несложные наборы данных. Истинная ценность тестирования проявляется только при наличии логики и особых случаев, в которых вы должны убедиться, что приложение будет вести себя ожидаемым образом. Чтобы продемонстрировать это, я добавлю некоторые требования к своему сайту GeekDinner. Сайт будет предоставлять API, который позволит кому угодно отвечать на приглашение на обед. Однако для числа приглашаемых будет дополнительно задан максимум, и количество ответов на приглашение (RSVP) не должно превышать этот максимум. Пользователи, запрашивающие RSVP, когда максимум уже достигнут, должны добавляться в список ожидания. Наконец, желающие пообедать могут указывать крайний срок относительно их начального времени, по истечении которого они не станут принимать приглашения.

Я мог бы закодировать всю эту логику в метод действия (action), но считаю, что тогда на один метод было бы возложено слишком много обязанностей, особенно на UI-метод, который должен концентрироваться на задачах, связанных с UI, а не на бизнес-логике. Контроллер должен проверять, что ему передаются допустимые входные аргументы, и гарантировать возврат ответов, подходящих для клиента. Решения, выходящие за рамки этих обязанностей, и особенно бизнес-логика не относятся к компетенции контроллеров.

Лучшее место для размещения бизнес-логики — модель предметной области приложения, которая не должна зависеть от обязанностей инфраструктуры (вроде работы с базами данных или UI). Класс Dinner подходит для управления RSVP, описанными в требованиях, поскольку он будет хранить максимальное количество участников мероприятия и знать, сколько RSVP было выдано на данный момент. Однако часть логики также зависит от того, когда появляется RSVP (до или после крайнего срока), поэтому методу понадобится доступ к текущему времени.

Я мог бы просто использовать DateTime.Now, но это затруднило бы тестирование моей логики и связало бы модель предметной области с системными часами. Другой вариант — задействовать абстракцию IDateTime и встроить ее в сущность Dinner. Однако, как показывает мой опыт, лучше всего сохранять сущности вроде Dinner свободными от зависимостей, особенно если вы планируете применять какое-то O/RM-средство наподобие EF для извлечения этих сущностей с уровня хранения. В связи с этим я не хочу заполнять сущности зависимостями, да и EF определенно не смогла бы иметь с этим дело без дополнительного кода с моей стороны. Распространенный подход — изъятие логики из сущности Dinner и перенос ее в какой-либо сервис (вроде DinnerService или RsvpService), в который можно легко встраивать зависимости. Однако это обычно приводит к антишаблону анемичной модели предметной области (anemic domain model antipattern) (bit.ly/anemic-model), в которой сущности имеют очень мало логики (или вообще не имеют ее) и являются просто контейнерами состояния. Нет, в данном случае решение прямолинейное: метод может просто принимать текущее время как параметр и позволять вызывающему коду передавать его.

При таком подходе логика добавления RSVP проста (рис. 2). Для этого метода есть ряд тестов, которые демонстрируют, что он ведет себя, как ожидалось; тесты доступны в проекте-примере, сопутствующем этой статье.

Рис. 2. Бизнес-логика в модели предметной области

Переместив эту логику в модель предметной области, я добился того, что API-метод моего контроллера останется компактным и сконцентрированным на собственных обязанностях. В итоге можно легко протестировать, что контроллер делает то, что ему положено, поскольку через этот метод проходит сравнительно немного путей выполнения.

Обязанности контроллера

Часть обязанностей контроллера — проверка ModelState и обеспечение его допустимости. Для ясности я делаю это в методе действия, но в более крупном приложении я исключил бы этот повторяющийся код в каждом методе действия, используя Action Filter:

Предполагая, что ModelState допустим, метод действия должен извлечь соответствующий экземпляр Dinner по идентификатору, переданному в запросе. Если метод не может найти экземпляр Dinner с совпадающим идентификатором, он должен вернуть результат Not Found:

По завершении этих проверок метод действия может делегировать бизнес-операцию, представленную запросом, модели предметной области, вызвав метод AddRsvp класса Dinner, который вы видели ранее, и сохранив обновленное состояние модели предметной области (в данном случае экземпляр dinner и его набор RSVP); после этого он возвращает ответ OK:

Вспомните: я решил, что у класса Dinner не должно быть зависимости от системных часов, и предпочел передавать текущее время в его метод. В контроллере я передаю _systemClock.Now как параметр currentDateTime. Это локальное поле, заполняемое через DI, что избавляет и контроллер от жесткого связывания с системными часами. Использовать DI в контроллере вполне разумно в противоположность сущности предметной области, так как контроллеры всегда создаются контейнерами сервисов ASP.NET; это подключает любые зависимости, объявленные контроллером в его конструкторе. Поле _systemClock имеет тип IDateTime, что определяется и реализуется всего несколькими строками кода:

Конечно, мне также нужно сконфигурировать контейнер ASP.NET так, чтобы он использовал MachineClockDateTime всякий раз, когда классу требуется экземпляр IDateTime. Это делается в ConfigureServices класса Startup, и, хотя подойдет любой жизненный цикл объекта, в данном случае я предпочел использовать Singleton, так как один экземпляр MachineClockDateTime будет обслуживать все приложение:

Включив эту простую абстракцию, я могу тестировать поведение контроллера в зависимости от того, передан ли крайний срок для RSVP, и гарантировать возврат корректного результата. Поскольку у меня уже есть тесты для метода Dinner.AddRsvp, проверяющие корректность его поведения, мне не понадобится очень много тестов для проверки того же поведения через контроллер, чтобы быть уверенным, что при совместной работе контроллер и модель предметной области функционируют правильно.

Следующие шаги

Скачайте сопутствующий этой статье проект-пример и посмотрите модульные тесты для Dinner и DinnersController. Помните, что свободно связанный код, как правило, гораздо проще в модульном тестировании, чем жестко связанный код, напичканный ключевыми словами new или вызовами статических методов, зависящих от обязанностей инфраструктуры. «New является связующим», так что ключевое слово new следует использовать в приложении осознанно, а не по воле случая. Узнать больше о ASP.NET Core и ее поддержке встраивания зависимостей можно на docs.asp.net.

Стив Смит (Steve Smith) — независимый тренер, преподаватель и консультант, а также обладатель звания ASP.NET MVP. Написал десятки статей для официальной документации ASP.NET Core (docs.asp.net) и работает с группами, осваивающими эту технологию. С ним можно связаться через сайт ardalis.com, также следите за его заметками в Twitter (@ardalis).

Выражаю благодарность за рецензирование статьи эксперту Microsoft Дугу Бантингу (Doug Bunting).

Илон Маск рекомендует:  Kylix обзор kylix (delphi для linux)

Что такое Web API?

Web API – новая исполяющая среда веб-приложения, построенная на уроках и паттернах, одобренных в ASP.NET MVC. Используя простую парадигму контроллеров, Web API позволяет разработчику создавать простые Web API веб-службы с небольшим по объему кодом и конфигурацией.

Вы можете задать очень разумный вопрос: почему нам нужен новый фреймворк веб-служб? Не входит ли уже в стек разработки компании Microsoft популярная и широко совместимая технология Simple Object Access Protocol (SOAP) (простой протокол доступа к объектам)? И не существовали ли ASMX веб-службы с тех самых пор, как был выпущен ASP.NET? И не поддерживает ли уже Windows Communication Foundation (WCF) самую гибкую и расширяемую архитектуру веб-служб? Веб-службы являются повсеместными, и разработчики понимают их. Почему Web API?

Почему Web API?

Для того чтобы ответить на этот вопрос, вам необходимо рассмотреть ряд представлений, связанных с установленной проблемой, и фреймворки, которые существуют для обращения к этой проблеме. Если любое из этих доверительных утверждений отзывается в вас, то продолжайте читать данную главу. Если вы обнаружите, что вы не разделяете эти утверждения, то вашим нуждам вполне будут удовлетворять существующие фреймворки веб-служб.

  • Я верю в то, что существует лучший способ создания веб-служб.
  • Я верю, что веб-службы могут быть простыми и что WCF слишком сложен.
  • Я верю, что в будущем мне нужно будет поддерживать больше HTTP клиентов.
  • Я верю, что основных веб-технологий таких, как GET , POST , PUT и DELETE , достаточно.

Если вы все еще читаете, то мы продолжим обзором того, чем Web API отличается от других фреймворков. Затем мы расширим приложение «Guestbook» таким образом, чтобы поддерживались HTTP веб-службы для существующих экранных функций, с целью продемонстрировать вам, как просто использовать Web API.

Чем Web API отличается от WCF?

ASMX веб-службы на протяжении многих лет поддерживали SOAP веб-службы поверх HTTP, но они не без труда поддерживали более простые веб-службы, которым не нужно было наличие способности взаимодействовать и, таким образом, для них не нужен был SOAP. WCF занял место ASMX как самый последний и лучший способ создания веб-служб на стеке .NET. WCF службы для конечных точек HTTP похожи на следующий код.

Листинг 24-1: Для WCF служб требуется интерфейс, класс и множество атрибутов

Строка 2: Интерфейс определяет службу

Строка 4: Атрибуты определяют операции

Строка 11: Отдельный класс реализует логику службы

В рамках WCF каждая веб-служба определяется с помощью интерфейса, который определяет соглашение. Каждый метод, отмеченный OperationContract , определяет операцию в SOAP конверте WCF службы. Наконец, класс, который реализует интерфейс службы, реализует код и логику.

Запуская эту службу в Visual Studio, вы можете использовать тестовый клиент WCF для того, чтобы увидеть запрос и отклик операции GetData , как это продемонстрировано на рисунке 24-1.

Рисунок 24-1: Тестовый клиент WCF может помочь вам протестировать SOAP веб-службу с помощью WCF.

В рамках отрасли многие разработчики прилагают усилия для упрощения WCF HTTP веб-служб. Многие говорят о RESTful-стиле (Representational State Transfer – репрезентативная передача состояния), который был введен для того, чтобы обозначать использование простейших HTTP веб-служб без всяких украшательств.

ASP.NET Web API использует понятие обычного MVC контроллера и базируется на нем для того, чтобы создать для разработчика простое и продуктивное событие. Web API оставляет SOAP в истории как средство, которое используют приложения для взаимодействия. На сегодняшний момент, из-за повсеместного использования HTTP, большинство рабочих сред и систем программирования поддерживают основные принципы HTTP веб-коммуникации. В связи с тем, что вопрос совместимости решается другими способами, SOAP может быть отодвинут в сторону возрастающими технологиями наследования, а разработчики могут быстро создавать простые HTTP веб-службы (web APIs) с помощью ASP.NET Web API фреймворка.

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

Листинг 24-2: Web API обладает очень простым стилем программирования с ApiController

Строка 4: Базовый класс разрешает основную функциональность

Строка 7: Простые методы определяют операции

Первое отличие, которое вы должны были заметить, – это то, что количество строк кода в данном примере меньше, поскольку отсутствует необходимость в конкретном классе, а также в интерфейсе. Простое наследование от ApiController разрешает функциональность, необходимую для объявления метода в виде операции.

Возврат значения в рамках Web API схож с использованием WCF, но результат совершенно другой. Вы можете увидеть результат, запуская проект в Visual Studio и тестируя его с помощью веб-браузера. Помните, что одним из основополагающих убеждений, касающихся Web API, является тот факт, что веб-службы могут быть простыми. Перейдите с помощью Internet Explorer по адресу http://localhost:/api/values/43 , содержащий средства разработки (нажмите F12 ). На рисунке 24-2 продемонстрировано, что получится в результате.

Рисунок 24-2: Используются HTTP заголовки вместо SOAP конверта.

Вместо того чтобы возвращать SOAP XML , как это делается в WCF, используется более простой формат, JavaScript Object Notation (JSON). Этот формат силен в передаче единичных значений, а также структур сложных объектов. Поскольку язык JavaScript понимает этот формат, jQuery может принимать этот тип данных для использования в AJAX вызовах.

Теперь, когда вы увидели отличие WCF от Web API, давайте начнем добавлять некоторую интересную функциональность поверх приложения «Guestbook» из главы 2.

Логгирование

Ведение лога и ILogger

ASP.NET Core имеет встроенную поддержку логгирования, что позволяет применять логгирование с минимальными вкраплениями кода в функционал приложения.

Для логгирования данных нам необходим объект ILogger . По умолчанию среда ASP NET Core через механизм внедрения зависимостей уже предоставляет нам такой объект. Например, возьмем стандартный проект по типу Empty и добавим механизм логгирования. Для этого перейдем к классу Startup и изменим его метод Configure() :

Средой выполнения в метод Configure передается объект ILogger, который представляет логгер. А метод логгера logger.LogInformation передает на консоль некоторую информацию. По умолчанию информация логгируется на консоль, поэтому для тестирования логгера нам надо запустить приложение как консольное:

При обращении к приложению с помощью следующего запроса http://localhost:xxxxx/index на консоль будет выведена информация, переданная логгером:

Важно, что, если мы используем класс Startup для логгирования, то получить объект логгера мы можем только в методе Configure() , но никак не в методе ConfigureServices() или конструкторе класса Startup, поскольку инфраструктура логгирования зависит от конфигурации и контейнера DI, который окончательно устанавливаются лишь после завершения работы метода ConfigureServices.

Категория логгера

В примере выше в метод передается не просто объект ILogger, а объект ILogger LogLevel . Всего мы можем использовать следующие значения:

Trace : используется для вывода наиболее детализированных сообщений. Подобные сообщения могут нести важную информацию о приложении и его строении, поэтому данный уровень лучше использовать при разработке, но никак не при публикации

Debug : для вывода информации, которая может быть полезной в процессе разработки и отладки приложения

Information : уровень сообщений, позволяющий просто отследить поток выполнения приложения

Warning : используется для вывода сообщений о неожиданных событиях, например, ошибках, которые не влияют не останавливают выполнение приложения, но в то же время должны быть иследованы

Error : информация об ошибках, вследствие которых приложение должно быть остановлено

Critical : уровень критических ошибок, которые могут быть связаны с какими-то ситуациями извне — ошибками операционной системы, потерей данных в бд, переполнение памяти диска и т.д.

None : вывод информации в лог не применяется

Для вывода соответствующего уровня информации у объекта ILogger определены соответствующие методы расширения:

Так, в примере выше для вывода информации на консоль использовался метод LogInformation() .

Вывод сообщений уровня Trace по умолчанию отключен.

Каждый такой метод имеет несколько перегрузок, которые могут принимать ряд различных параметров:

string data : строковое сообщение для лога

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

string format : строковое сообщения для лога, которое моет содержать параметры

object[] args : набор параметров для строкового сообщения

Exception error : логгируемый объект исключения

Также для логгирования определен общий метод Log() , который позволяет определить уровень логгера через один из параметров:

При стандартном логгировании на консоль для каждого уровня/метода определен своя метка и цветовой маркер, которые позволяют сразу выделить сообщение соответствующего уровня. Например, при запуске следующего кода:

Логгирование в ASP.NET Core

Поскольку логгирование является неотъемлемой частью любого приложения, в ASP.NET Core появилась унифицированная инфраструктура для логгирования.

Единый интерфейс для записи в лог позволяет приложениям работать с логом в едином стиле, независимо от того, какой провайдер используется.

Настройка инфраструктуры логгирования

Прежде чем начать писать что-то в логи, нужно настроить провайдеров логов. Для этого в метод Configure() класса Startup инжектируется сервис ILoggerFactory . Используя этот сервис можно настроить провайдеров:

В данном примере добавялется провайдер, который будет писать логи в консоль отладчика. Чтобы добавить этого провайдера, нужно установить пакет Microsoft.Extensions.Logging.Debug . Впрочем, он уже итак установлен в проекте, который создается в базовом шаблоне проекта ASP.NET MVC.

Аналогичным образом добавляются и другие провайдеры. Microsoft предоставляет такие провайдеры как Microsoft.Extensions.Logging.Console , Microsoft.Extensions.Logging.Debug , Microsoft.Extensions.Logging.TraceSource , Microsoft.Extensions.Logging.AzureAppServices , Microsoft.Extensions.Logging.EventSource , Microsoft.Extensions.Logging.EventLog . Как нетрудно догадаться из названия, каждый из них имеет собственный источник для хранения логов.

Можно использовать несколько провайдеров одновременно:

Запись в лог

Для того, чтобы начать писать что-то в лог, следует инжектировать интерфейс ILogger в нужный компонент приложения. Причем в качестве generic-параметра мы можем указать категорию логгера. Например, получим доступ к логгеру в компоненте HomeController :

После этого в рамках компонента можно пользоваться этим логгером и добавлять записи в лог. Для записи используются методы, соответствующие уровню важности сообщения:

Попробуем записать что-нибудь в лог:

Теперь при срабатывании этого действия в консоли отладчика обязательно появится запись:

WebApplication8.Controllers.HomeController:Error: Some error occured

Если при настройке провайдеров мы настроили также вывод в консоль ( AddConsole() ), то эта же запись появится и в консоли (если мы запускаем приложение через Kestrel и консоль нам видна).

Записывать в лог можно не только обычные строки, но и структурированные данные. Для этого существуют специальные перегрузки методов LogXXX() . Запишем, например, в лог данные с идентификатором объекта:

В логе появится строка следующего содержания:

WebApplication8.Controllers.HomeController:Error: Unable to delete object with 25 key

Если провайдер поддерживает структурированные данные, то ID будет доступно как отдельное поле. Более того, поле может быть не только простым типом, но и составным объектом. Но это — отдельная тема и здесь касаться этого не будем.

Scope

Иногда бывает полезно объединить набор сообщений лога в группы. Это можно сделать используя метод BeginScope() , реализующий IDisposable . Поэтому можно использовать конструкцию using для группировки сообщений в логе:

Не все провайдеры поддерживают такую группировку. Однако, те, кто поддерживают будут иметь пометку Updating object для каждой записи внутри scope.

Например, группировку поддерживает Microsoft.Extensions.Logging.Console . Для её использования это нужно указать явно при настройке провайдера:

В таком случае для каждого сообщения внутри scope будет добавлена строка Updating object :

Фильтрация логов

В логи обычно записывается большой объем отладочной информации. Например, инфраструктура ASP.NET записывает туда информацию о каждом обработанном HTTP-запросе. Это может быть полезно во время отладки, но когда приложение работает на боевом сервере, эта информация не нужна. Более того, в большом потоке отладочной информации можно не увидеть действительно ценного — сообщения об ошибках или каких-либо нестандартных ситуациях. Поэтому для разных окружений настройки логгирования можно настроить по-разному.

Все записи в логах разделяются по степени важности. Например, методы LogDebug() и LogInformation() записывают событие с какими-то отладочными данными. Их смысл в том, чтобы дать представление о процессах, которые происходят в приложении, но не более. С другой стороны LogWarning() , говорит о том, что в приложении произошло что-то, на что было бы неплохо обратить внимание. Наконец, LogError() или LogCritical() записывает информацию об ошибках — т.е. приложение повело себя не так, как планировалось, и на такие записи обязательно нужно обращать внимание.

В ASP.NET Core можно задать минимальный уровень для фильтрации логов. Например, при отладке можно разрешить показывать все сообщения, включая Trace , Debug и Information . При этом на боевом сервере эти сообщения можно зафильтровать и отображать только ошибки.

Это поведение задается при помощи специального параметра:

В этом случае всё, что менее критично, чем Error или Warning будет игнорироваться для этих провайдеров.

Но не все категории записей в логе одинаково полезны. Например, в каких-то ситуациях нам могут быть неинтересны ошибки, которые пишутся в лог инфраструктурой ASP.NET, даже если для них задан уровень Error , но при этом очень важны записи, сделанные нашими собственными сервисами, причем для менее приоритетных уровней.

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

Вот как например можно настроить провайдер для записи в консоль:

Другой способ — использовать конфигурационный файл для настройки уровней логгирования:

Конфигурационный файл в этом случае может выглядеть так:

Ещё один удобный способ настроить фильтры — это использовать пакет Microsoft.Extensions.Logging.Filter . После установки этого пакета для ILoggerFactory , появится метод-расширение WithFilter() , позволяющий настроить фильтры:

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

Создание ASP-компонентов в Delphi 6

Что такое ASP

SP (Active Server Pages) — технология, которая позволяет создавать Web-страницы, содержащие код, выполняемый Web-сервером, и динамически формировать Web-страницы. Вместе с Microsoft Internet Information Services поставляется несколько COM-объектов, выполняющих серверный код. Современная версия этой технологии позволяет использовать в ASP-страницах произвольные серверные компоненты. Клиентское приложение, использующее ASP-объекты, представляет собой HTML-документ (куда могут также входить клиентский и серверный коды на скриптовых языках), который можно прочесть практически с помощью любого Web-браузера. Обычно эти HTML-документы размещаются на каком-либо Web-сервере (как правило, это Microsoft Internet Information Server версии 3.0 и выше). Web-сервер, получив требование о предоставлении документа, считывает его из локального хранилища и передает клиенту, при этом часть информации вносится в документ Web-сервером динамически; сам же Web-сервер в этом случае может обращаться к ASP-объектам (входящим в комплект поставки Internet Information Server или созданным сторонними разработчиками). Как правило, Web-документы, содержащие обращения к ASP-объектам, имеют расширение *.asp. Примеры подобных документов можно найти в каталогах, создаваемых при установке Internet Information Server.

Ранее (см. Компьютер Пресс, №2’2001, c. 169) мы рассказывали о создании простейших ASP-приложений, в том числе для работы с базами данных. Настоящую публикацию следует рассматривать как продолжение той статьи.

Объекты, используемые в ASP

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

Объект Request

Объект Request используется для доступа к данным, которые формируются клиентом при обращении к ASP-серверу. Доступ к объекту Request получают с помощью интерфейса IRequest, которому присущи следующие основные свойства и методы:

  • свойство ClientSertificate — содержит значения всех полей клиентного сертификата, которые пересылаются в http-сообщении;
  • свойство Cookies — содержит значения всех заголовков Cookie в HTTP-запросе. Cookie — небольшие текстовые файлы, которые запоминаются на компьютере конечного пользователя и передаются на сервер в каждом запросе. С их помощью можно создать «сессию» для клиента: когда клиент обращается первоначально к одной, затем к другой странице и т.д. (при этом клиент не имеет права перейти к следующей странице, не обратившись к предыдущей);
  • свойство Form — если клиент осуществляет обращение к ASP-серверу с помощью формы, то можно получить значения заполненных полей в форме при обращении к свойству Form с добавлением имени поля;
  • свойство ServerVariables — содержат большинство переменных, необходимых для формирования HTTP-заголовков;
  • свойство TotalBytes — суммарное число байт, которые передаются клиентом на сервер. Реальное число байт может быть меньше, так как протокол HTTP не позволяет пересылать пакеты, размер которых превышает 8 Kбайт;
  • свойство QueryString — содержит имена и значения полей запроса при использовании метода GET;
  • свойство Body — содержит имена и значения полей запроса при использовании метода POST;
  • метод BinaryRead — используется для получения содержимого всего запроса, посылаемого клиентом, если размер запроса превышает 8 Kбайт. Вызов этого метода позволяет получить следующие 8 Kбайт данных HTTP-запроса. Вызов следует повторять до тех пор, пока не будет прочитан весь запрос; при этом размер последнего пакета может быть менее 8 Kбайт. Результаты вызова метода запоминаются в двоичном массиве. Следует обратить внимание на то, что после вызова этого метода нельзя обращаться к свойству Form.

Объект Response

В этом классе формируется отклик ASP-сервера, который передается клиенту. Доступ к этому объекту осуществляется через интерфейс IResponse. Основные свойства и методы данного интерфейса таковы:

  • свойство Cookies — позволяет создать коллекцию параметров вместе с их значениями. К этой коллекции также добавляется время, в течение которого данную коллекцию следует сохранять в клиентском приложении. При успешном получении отклика клиент запоминает эти параметры на диске в виде файла. При следующем обращении к серверу эти данные будут переданы в запросе и их можно будет получить при использовании свойства Request.Cookie. Анализ этих данных позволяет определить, обращался ли клиент к серверу ранее и если обращался, то с какими запросами. Cookie используются для создания «сессии» с клиентом в CGI-приложениях, поскольку IP-адреса клиента могут изменяться Proxy-серверами во время сессии. В ASP-приложениях возможно также создание сессии для клиента (это мы обсудим ниже);
  • свойство Buffer — если значение этого свойства равно True, то осуществляется буферизация отклика. При этом значении параметра отклик клиенту не отправляется до тех пор, пока не будет вызван метод Request.End (этот метод автоматически вызывается после обработки всего ASP-документа, то есть когда сформированы отклики от всех ASP-серверов) или Flush;
  • свойство CacheControl — определяет, может ли клиент кэшировать отклик;
  • свойство Charset — определяет имя шрифта для содержимого отклика ContentType=”text/html”;
  • свойство ContentType — указывает содержимое отклика. По умолчанию это свойство имеет значение text/html. Это значение необходимо изменять, например, при передаче картинок (image/jpeg или image/gif и т.д);
  • свойство Expires — при значении CacheControl=True определяет, сколько времени хранится отклик на клиентском месте;
  • свойство ExpiresAbsolute — то же самое, что Expires, только определяет абсолютную дату и время хранения отклика;
  • свойство IsClientConnected — указывает, был ли клиент отсоединен от сервера;
  • свойство Status — нормальное значение статуса «200 OK» означает успешную генерацию отклика. При невозможности создать отклик следует изменять значение статуса. Так, «401 Unauthorized» означает, что пользователю запрещено обращаться к данной странице по соображениям безопасности, например в случае неправильно введенного пароля;
  • метод AddHeader — добавляет заголовок в отклик. Заголовки добавляются в виде =” ”;
  • метод AppendToLog — добавляет строку в Log-файл Web-сервера. Эта строка не передается клиенту;
  • метод BinaryWrite — формирует двоичный отклик. Чаще всего используется для передачи клиенту картинок;
  • метод Clear — полностью очищает отклик. После этого его требуется формировать заново с самого начала;
  • метод End — прекращает обработку ASP-документа и немедленно возвращает результат клиенту. Вызывается автоматически после завершения обработки ASP-документа, но может быть вызван явно из кода ASP-сервера. После его вызова вызовы методов Write или BinaryWrite запрещены;
  • метод Flush — отправляет текущее содержимое буфера клиенту. Формирование отклика может продолжаться дальше;
  • метод Write — используется для записи текстовой информации в отклик;
  • метод Redirect — позволяет переадресовать запрос на другой URL. При этом можно поменять параметры запроса в объекте Request.

Объект Server

Этот объект позволяет обращаться к Internet Information Services и экспонирует ряд его методов и свойств:

  • свойство ScriptTimeout — время в минутах, в течение которого сессия существует без генерации нового запроса или без вызова команды Refresh со стороны клиента;
  • метод CreateObject — вызов этого метода используется для запуска COM-сервера, который будет принимать скрипты с ASP-документа и генерировать отклик, который вставляется вместо скрипта;
  • метод Execute — выполняет скрипт в указанном ASP-файле;
  • метод GetLastError — возвращает объект ASPError, в котором можно получить полную информацию о последней ошибке;
  • метод HTMLEncode — заменяет зарезервированные символы в HTML-документе подходящим набором символов, которые интерпретируются браузером и позволяют клиенту увидеть на экране зарезервированные символы. Например, дизайнер Web-сайта хочет, чтобы клиент увидел на экране следующее сочетание символов:
    . Если эту последовательность вставить в HTML-документ, то она будет интерпретирована как разрыв строки. Для этой последовательности символов метод HTMLEncode вернет: %3CBR%3E. При помещении этой последовательности символов в HTML-документ клиент увидит на экране
    ;
  • метод MapPath — конвертирует путь, выраженный в терминах виртуальных каталогов (абсолютный или относительный), в путь к физическому каталогу на данном компьютере;
  • метод Transfer — пересылает весь текущий отклик к другому ASP-серверу для продолжения формирования отклика;
  • метод URLEncode — перекодирует URL, включая специальные символы, в строку.
Илон Маск рекомендует:  Тег tt

Объект Session

Этот объект создается во время первого обращения клиента к ASP-серверу. Замечательная особенность этого объекта — его способность сохраняется в течение некоторого времени после генерации отклика клиенту. Если в течение этого времени клиент вновь обратится к серверу, то сможет работать внутри ранее созданной сессии. Если внутри этого объекта объявить переменные, то в них можно запоминать состояние клиента. Этот факт значительно облегчает проведение сессий с клиентом по сравнению с CGI-приложениями, где постоянно приходится анализировать Cookie. Основные свойства и методы объекта Session:

  • свойство StaticObjects — коллекция всех объектов, добавленных к сессии при помощи тэга . Можно прочитать или изменить все значения свойств этих объектов. Вызов метода Remove удаляет данный объект. Вызов метода RemoveAll удаляет все объекты;
  • свойство Contents — коллекция всех динамически создаваемых объектов. Эти объекты можно создавать во время выполнения и использовать их при последующих обращениях клиентов к Web-серверу;
  • свойство CodePage — указывает страницу кодировки символов, которая может изменяться в зависимости от локальных настроек;
  • свойство LCID — языковой идентификатор, используемый для интерпретации текстовых строк;
  • свойство SessionID — идентификатор сессии для данного клиента. Генерируется автоматически и является постоянным для всех запросов данного клиента;
  • свойство Timeout — время в минутах, в течение которого существует сессия без генерации нового запроса или без вызова команды Refresh со стороны клиента;
  • метод Abandon — разрушает сессию и возвращает все ресурсы;
  • событие Session_OnStart — генерируется в момент создания сессии. Оно генерируется после события Application_OnStart, но до начала выполнения кода. Все переменные в сессии уже являются доступными, и к ним можно обращаться. Обработчик этого события должен быть написан на языке VBScript или JavaScript;
  • событие Session_OnEnd — наступает перед разрушением сессии по истечении времени TimeOut или после вызова метода Abandon. В обработчике событий можно работать с объектами Application, Server и Session, но не с Request и Response. Обработчик этого события должен быть написан на языке VBScript или JavaScript.

Для сохранения состояния клиента (это главная особенность) используется следующий синтаксис:

Рассмотрим в качестве примера Интернет-магазин. Предположим, пользователь на первой Web-странице выбирает наименование товара (например, Intel Processor), на второй странице — количество этого товара (например, 5), на третьей странице отображается цена, после чего ожидается подтверждение заказа. Ясно, что для генерации третьей страницы необходимо знать значения параметров с первых двух страниц. Поэтому после возвращения отклика с первой страницы используется команда:

Session.Value[‘GoodName’]:=’ Intel Processor ‘;

Имя выбранного товара можно получить после анализа свойств объекта Request. После второй страницы используем команду:

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

В объекте Session можно также запоминать динамически создаваемые объекты (об этом будет рассказано ниже).

Объект Application

Этот объект используется для хранения и обработки данных, которые являются общими для всех сессий. При помощи объекта Application можно передавать данные от одного клиента к другому. Основные свойства и методы объекта Application:

  • свойство Contents – содержит список всех объектов, которые были добавлены при выполнении скриптов из ASP-документов. Объект Contents имеет два метода: Remove удаляет данный объект, а RemoveAll удаляет все объекты. Сюда же можно помещать ссылки на динамически создаваемые объекты при выполнении кода ASP-сервера;
  • свойство StaticObjects — список всех объектов, которые были добавлены при помощи тэга ;
  • метод Lock. Клиенты работают в многопоточном режиме, а переменные в объекте Application являются общими для всех клиентов. Поэтому если надо обратиться к свойствам объекта Application или к методам, которые работают с переменными данного объекта, то обязательно требуется вызов метода Lock. Это дает возможность безопасно работать с переменными Application, в то время как остальные клиенты будут ожидать окончания работы с этим объектом. После чтения или записи свойства (либо окончания работы метода) требуется вызов метода Unlock, и только после этого переменные (методы) будут доступны для других клиентов;
  • метод Unlock — делает доступными переменные или методы объекта Application другим клиентам;
  • событие Appliation_OnStart — наступает в момент первого старта приложения и продолжается до создания сессии. Из обработчика этого события можно обращаться только к объектам Server и Application. Обработчик этого события должен быть написан на языке VBScript или JavaScript;
  • событие Application_OnEnd — вызывается в момент завершения ASP-приложения, после разрушения всех сессий. Доступные объекты и языки для описания обработчика те же, что и у обработчика Application_OnStart.

Использование объекта Session для хранения состояния

втоматическая обработка полей Cookie в ASP-технологии позволяет определить, обращался ли ранее клиент к данному Web-серверу. При первом обращении клиента для него создается объект Session. При последующих обращениях этого же клиента для него назначается ранее созданный объект Session. Объект Session разрушается либо при отсутствии в течение интервала времени Session.TimeOut новых запросов от клиентов, либо при вызове метода Session.Abandon из кода. В объекте Session можно хранить переменные и объекты, которые динамически создаются или изменяются. Эти изменения видны при каждом последующем обращении клиента. Таким образом, объект Session может хранить состояние клиента. Данная возможность, реализованная на уровне технологии, обеспечивает ASP-приложениям существенные преимущества по сравнению с CGI- и ISAPI-приложениями, где для хранения состояния необходимо писать трудоемкий код для работы с Cookie.

Для тестирования прежде всего требуется настроить Microsoft Internet Information Services (IIS). Для этого создадим виртуальную директорию IIS, которая обязательно должна иметь разрешение как на чтение (из нее будут читаться данные), так и на выполнениe скриптов (из нее будет загружен *.asp-документ). Директория должна быть доступна с помощью протокола HTTP. Поэтому в первую очередь нужно обратиться к Web-сервису Internet Information Server, просмотреть список доступных директорий и при необходимости создать новые с соответствующими правами доступа.

В нашем примере на компьютере, который имеет IP-адрес 192.168.0.2, была создана виртуальная директория /Test, соответствующая физическому адресу на компьютере C:\ASPTest. В директорию, имеющую права доступа Read и Script, был скопирован файл Test.asp. Сам файл ASP01.dll (COM-сервер) можно поместить в произвольную директорию. Главное — чтобы он был зарегистрирован в системном реестре. Для этого после выполнения команды Save As в Delphi 6 выберем команду меню Run/Register ActiveX server и выполним ее.

Продолжим работу с сессией. Воспользуемся проектом, создание которого было описано ранее (см. КомпьютерПресс, № 2’2001, c. 196). Исходный код проекта можно найти на нашем CD-ROM. Сначала разберемся, каким образом можно сохранять и изменять переменные. Для этого в секции private класса TTest модуля ASP01_U1 обьявим метод:

В каждом методе модуля ASP01_U1 вызовем метод IncrementCallCount с параметром Name= . Например, в ранее объявленном методе GetPicture (см. КомпьютерПресс, № 2’2001, с.196) обратимся к IncrementCallCount следующим образом:

Далее добавим в библиотеку типов новый метод GetCallCount, как было описано выше. Реализуем этот метод следующим образом:

И наконец, создадим ASP-документ с вызовом метода GetCallCount:

Сохраним этот документ в файле Test3.asp в директории C:\ASPTest (напомним, что она зарегистрирована в Internet Information Server и имеет разрешения на чтение и выполнение кода). Далее, обращаясь к ранее созданным ASP-документам, будем обращаться к объявленным ранее методам ASP-сервера в произвольной последовательности и произвольное число раз, пока, наконец, не дойдем до страницы Test3.asp. В результате получим примерно следующий отклик, какой представлен на рис. 2.

Напротив названия каждого метода стоит число обращений к нему клиента в текущей сессии. Если вызвать команду Refresh в браузере, то можно заметить, что число напротив надписи GetCallCount увеличится на 1.

Данные в коллекции Session.Value хранятся в виде вариантных переменных; иными словами, они могут принадлежать любому типу, конвертируемому в OleVariant: integer, single, double, WideString и т.д. Можно хранить и массивы — для этого вызывается функция VarArrayCreate. Доступ к переменным осуществляется по имени. Коллекция Session.Value возвращает пустое значение (null), если ранее переменная с данным именем не была добавлена к коллекции. Добавление новой переменной осуществляется в случае присвоения значения элемента коллекции с уникальным именем.

Однако ссылки на экземпляры классов (объекты) в этой коллекции хранить нельзя. Точнее говоря, хранить их можно: например привести указатель к типу integer и сохранить в коллекции Session.Value. Далее можно получать доступ к объекту приведением типа и таким образом при каждом следующем обращении клиента получать ссылку на объект и работать с ним. Казалось бы, все хорошо, однако при разрушении сессии по истечении TimeOut деструкторы объектов вызваны не будут. Это означает, что при работе такого сервера постепенно будут исчерпываться доступные системные ресурсы и в конце концов Web-сервер просто «рухнет». Если бы существовала возможность создать обработчик события Session_OnEnd в коде Delphi, то деструкторы можно было бы вызвать явно. Однако этот обработчик может быть создан только в ASP-документе на языках VBScript или JavaScript.

Выход из создавшейся ситуации заключается в использовании коллекции Session.Contents, которая предназначена для хранения объектов. В принципе, коллекция Session.StaticObjects также используется для хранения объектов, но ее нельзя модифицировать во время выполнения приложения. Идея хранения объектов в коллекции Session.Contents заключается в следующем: объекты, которые необходимо хранить, поддерживают интерфейс IUnknown (или его потомок). Именно ссылка на этот интерфейс и помещается в коллекцию Session.Contents. При закрытии сессии для всех интерфейсов, хранящихся в этой коллекции, вызывается метод Release. Соответственно из этого обработчика событий можно вызвать деструктор класса при равенстве нулю числа ссылок на интерфейс.

Для проверки вышесказанного попробуем решить следующую задачу: мы хотим помещать содержимое одной записи в базе данных на отдельную Web-страницу, давая пользователю возможность передвигаться по записям вперед и назад. В этом случае требуется запоминать положение указателя текущей записи в наборе данных, что будет выполняться в компоненте Session. Наиболее простой способ запомнить положение указателя текущей записи — не закрывать набор данных после формирования отклика пользователю.

Первоначально создадим ASP-документ, содержащий форму с двумя кнопками: Back и Next. В данном документе вызовем новый метод ASP-сервера, который назовем, например, TestSession. В этом методе будем анализировать запрос и смещать текущую запись вперед или назад. Сохраним документ в файле Test4.asp. Окончательный вид документа следующий:

Далее создадим новый модуль данных и поместим на него компоненты TADOConnection и TADOTable. В ADOConnection1 сошлемся на базу данных Northwind. В компоненте ADOTable1 сошлемся на ADOConnection1, свойству TableName присвоим значение Alphabetical list of products, а его свойство Active установим равным True. Это означает, что немедленно после отработки конструктора будет установлена связь с сервером баз данных. Объявим новый интерфейс ISessionObject — потомок IUnknown. Полезно к этому интерфейсу добавить метод, который будет возвращать указатель на модуль данных. Несмотря на то что использовать указатели в интерфейсах запрещено вследствие необходимости осуществлять маршалинг для разных модулей, в нашем случае это оправданно, поскольку данный метод будет использоваться только внутри модуля ASP01. Все внешние приложения (в том числе Internet Information Services) будут работать с этим интерфейсом как с Iunknown, не «зная» о существовании метода, возвращающего указатель.

Окончательный код выглядит следующим образом:

Данный код прост и не требует комментариев. Ключевой метод — ObjRelease — уменьшает число ссылок и при их равенстве нулю вызывает деструктор класса. Некоторые изменения потребуются и в классе TTest модуля ASP01_U1. Прежде всего необходимо вызвать конструктор класса TDataModule2. Наиболее подходящее место для вызова конструктора — событие Session_OnStart — недоступно из кода Delphi. Метод AfterConstruction также подходит мало — при обращении к нему не определены ссылки на объекты Session, Application, Server. Необходимо, чтобы эти объекты были инициализированы из интерфейса IScriptingContext — именно это и делает метод OnStartPage. Поэтому будем вызывать конструктор из метода TTest.OnStartPage. При этом следует учитывать, что конструктор мог быть вызван раньше — во время предыдущего запроса клиента. Следовательно, необходима предварительная проверка существования экземпляра класса TTest. Окончательно код будет выглядеть следующим образом:

Первоначально в переменную V пытаемся копировать переменную с именем DataModule, которая хранится в объекте Session. Если это не удается (что происходит, когда пользователь обращается к ASP-серверу в первый раз), то создаем экземпляр класса TDataModule2, получаем от него ссылку на интерфейс ISessionObject и запоминаем ее в объекте Session под именем DataModule. Теперь поведение экземпляра класса TDataModule2 будет корректным: он будет существовать, пока существует объект Session для данного клиента, и разрушаться с окончанием сессии. Чтобы убедиться в этом, достаточно переписать деструктор TDataModule2 и поместить туда оператор Beep (визуальные элементы управления в ASP-приложениях показывать запрещено). Если после этого вновь обратиться к ASP01 и затем выгрузить ASP-сервер, как было описано выше, то можно услышать звук динамика.

Осталось реализовать метод TestSession в классе TTest — он формирует отклик для клиента. В этом методе необходимо получить доступ к хранящемуся в объекте Session экземпляру класса TDataModule2, проанализировать запрос и при нажатии кнопки Next переместить курсор на следующую запись, а при нажатии кнопки Prior — на предыдущую. Окончательный код выглядит следующим образом:

В нем используется определенный ранее метод ISessionObject.getDataModule для получения ссылки на экземпляр класса TDataModule2. Скомпилировав этот проект и обратившись к созданной ранее странице Test4.asp, можно получить отклик, который представлен на рис. 3.

При нажатии клавиши Next получаем страницу со следующей записью; Prior — с предыдущей.

В заключение следует отметить, что объект Application также имеет аналогичные свойства для хранения переменных и объектов. Эти свойства имеют те же самые названия, что и в объекте Session. Существует два отличия использования аналогичных свойств объекта Application:

Разработка ASP.NET 5 веб-приложений с Visual Studio Code

Введение

10 лет назад трудно было вообразить, что разработка ASP.NET веб-приложений вне интегрированной среды разработки Visual Studio .NET буде возможна. Но в прошлом году произошли изменения. В апреле 2014 года на конференции разработчиков (Build) Microsoft анонсировал запуск нового легкого кросс-платформенного кодового редактора для разработки современных веб-приложений под именем Visual Studio Code.

Visual Studio Code

Visual Studio Code свободна для скачивания с официального сайта. Работаете ли Вы на Linux, Mac или Windows – не имеет значения. Вы можете скачать и запустить VS код на своей платформе.

Установка Visual Studio Code довольно проста, но если Вы застрянете, то всегда можете просмотреть документацию по установке.

Visual Studio Code является просто редактором кода на файловой основе и не имеет всех преимуществ полной интегрированной среды разработки Visual Studio .NET. Он легче по дизайну. Тем не менее, у редактора есть множество особенностей, которые поддерживают такие технологии, как IntelliSense для дополнения кода, Peek Definition для быстрого взгляда на функциональный код без навигации, реорганизацию кода и прочие. Visual Studio Code также поддерживает множество языков, например CoffeeScript, F#, Go, Jade, Java, Handlebars, Powershell и Python, для примера. Вы можете проверить языковую поддержку здесь.

Также Visual Studio Code способен поддерживать такие среды выполнения, как ASP.NET 5 и Node.JS. Если Вы их используете для веб-разработки с Microsoft Stack, можете быть уверенны, что ASP.NET 5 (новая версия ASP.NET) сейчас поддерживает кросс-платформенную разработку. Это значит, что можно разрабатывать ASP.NET-приложение в среде Linux, Mac или Windows так же, как и запускать его в любой из них. И Вам даже не нужно иметь интегрированную среду разработки Visual Studio .NET, чтобы сделать это.

Visual Studio Code – это все, что вам нужно, чтобы начать работать с ASP.NET 5, и это здорово!

Установка ASP.NET 5 & DNX (среды выполнения .NET):

ASP . NET 5 был построен с нуля, чтобы убедиться, что он придерживается современной парадигмы веб-приложений, и что приложения, разработанные с его помощью являются «облачными». Ключевыми аспектами ASP . Net 5 явля ются гибкость и модульность – он предлагает минимальные накладные расходы и позволяет нам выбирать только то, что мы хотим в рамках нашего веб-приложения.

DNX расшифровывается как Dot Net eXecution Environment.

Что такое Yeoman?

Если Вы работали в интегрированной среде разработки Visual Studio .NET, Вам будет интересно: «Есть ли здесь File > New > ASP.NET шаблон проекта?» Visual Studio Code является редактором кода на файловой базе, так что Вы можете просто открыть файл и начать редактирование. Кроме того, нужны поддерживающие средства, чтобы работать с исполняемым шаблоном ASP.NET.

Yeoman является популярным консольным инструментом для автоматического построения структуры проекта, а также обеспечивает базовым ASP.NET шаблоном для старта. Yeoman может быть установлен с помощью NPM, но для начала надо установить Node . JS .

Если у Вас нет Node в системе, можете установить его. Кроме Yeoman , Вам также нужны другие поддерживающие средства, такие как генератор ASP . NET , исполнитель задач Grunt и Bower . Вы можете выполнить это за одну команду. В командной строке набрать следующую команду и нажать enter:

npm install –g yo grunt-cli generator-aspnet bower

Теперь Вы можете строить веб-приложения.

Создание веб-приложения

Разберем пошагово, как построить структуру проекта нового ASP.NET 5 веб-приложения.

1. Откройте командную строку и перейдите в папку, где Вы хотите создать свое новое веб-приложение.

2. Введите в командную строку следующую команду:

3. Yeoman отобразит варианты приложений для генератора aspnet. Возможные варианты:

  • консольное приложение
  • веб-приложение
  • основное веб-приложение (без членов/аутентификации)
  • веб-приложение API
  • Nancy ASP.NET приложение
  • библиотека классов
  • тестовый проект Unit

Выберите сейчас основное приложение. Используйте клавиши со стрелками для выбора опции и нажмите enter.

4. Дальше нам нужно назвать веб-приложение. Используем HelloWorld как имя нашего образца ASP . NET 5 веб-приложения. Введите имя и нажмите enter. Yeoman построит структуру проекта.

5. Каталог, в котором будет создано наше веб-приложение будет иметь то же имя, что мы дали только что Yeoman . В данном случае — “ HelloWolrd ”.

6. Через командную строку откройте Visual Studio Code

7. Visual Studio Code запустит проект HelloWorld. Файлы в проекте будут отображаться в окне Проводника.

8. В редакторе Visual Studio Code выберите View > Command Palette option
и в командной палитре введите следующую команду:

dnx: dnu restore — (HelloWorld)

Выше написанная команда restore устанавливает нужные NuGet пакеты, необходимые для запуска веб-приложения. Она запустит командную строку, куда будут загружаться все пакеты. После выполнения будет получено сообщение, что загрузка завершена.

Запуск веб-приложения

Теперь, когда мы успешно создали веб-приложение, пришло время запустить его и посмотреть на результат.

1. В Visual Studio Code откройте Command Palette, выбрав View > Command Palette. Введите следующую команду для запуска приложения:

dnx: kestrel -(HelloWorld,Microsoft.AspNet.Hosting—server Kestrel–config hosting.ini

Примечание: Когда Вы начинаете набирать команду, командная палитра подскажет Вам полную команду в списке. Вы можете выбрать команду из списка и команда будет выполнена.

2. Откройте браузер и перейдите по ссылке http://localhost5000

Мы только что создали ASP.NET веб-приложение вне интегрированной среды разработки Visual Studio. Фактически, в настоящее время ASP.NET больше не только в Windows. Мы переходим на кросс-платформу – как с точки зрения разработки, так и размещения.

Интеграция Telerik UI для набора ASP.NET MVC

Teleric предлагает пользовательский интерфейс, известный как UI для ASP.NET MVC. Он произошел от Kendo UI и предусматривает HTML-помощников, которых называют “Kendo UI wrappers.” Они упрощают работу с элементами управления Kendo UI и ускорят вашу разработку.

Представим пошагово добавление пользовательского интерфейса для ASP.NET MVC в наш проект:

1. Откройте файл project.json и в узле (“dependencies”) добавьте Kendo (в настоящее время доступна бинарная версия Kendo Mvc – 2015.2.805).

2. Дальше откройте Startup.cs и найдите метод “ConfigureServices”. Добавьте следующий фрагмент в метод.

//Register UI for ASP.NET MVC Helpers

3. Затем откройте

/Views/_ViewImports.cshtml и импортируйте пространство имен Kendo.Mvc.UI .

@using Kendo . Mvc . UI

4. Скопируйте Kendo UI ресурс с клиентской стороны. Для этого Вам нужно установить пакет Kendo UI Professional (Commercial Package). Его можно установить через Bower с помощью следующей команды:

bower install https://bower.telerik.com/bower-kendo-ui.git

Пакет Kendo UI Professional Bower размещается в частном git -хранилище и требует активировать аккаунт Telerik. Во время установки Вам предложат ввести пароль несколько раз.

Bower установит пакет Kendo UI Professional как “ kendo — ui ” в папку wwwroot / lib .

5. Дальше нам необходимо зарегистрировать скрипты Kendo UI и стили в

link rel =»stylesheet» href =»

link rel =»stylesheet» href =»

link rel =»stylesheet» href =»

6. Теперь давайте используем виджет Kendo UI в одном из видов. Мы будем использовать виджет Kendo UI DatePicker. Откройте

/Views/Home.Index.cshtml и добавьте следующий фрагмент:

@RenderSection(«scripts», required: false)

7. Запустите веб-приложение через dnx: kestrel команду, что мы использовали ранее. Результат представлен ниже.

ASP.Net. Лекция 5. AutoPostBack Привязка к данным. Коллекции. Проверка правильности вводимых данных (исходники)

Свойство AutoPostBack

Программирование в ASP.NET ориентировано на события. События на странице, например, нажатие на кнопку, обрабатываются на сервере. Изменения в тексте поля редактирования, выбора опции в списке, нажатие на флажок или переключатель не вызывают немедленной отправки на сервер. Этого можно добиться, если установить свойство AutoPostBack для этих элементов.

Илон Маск рекомендует:  Шаблон сайта роза HTML, CSS, 1 страница

Если AutoPostBack установлен для элемента управления TextBox, то для него будет вызываться событие TextChanged, как только поле потеряет фокус или будет нажата клавиша Enter. Чтобы это свойство работало, браузер должен поддерживать ECMAScript(стандарт JavaScript, принятый Европейской ассоциацией производителей компьютеров).

Источником данных для элементов управления могут служить таблицы данных. Давайте разберем пример, входящий в состав Visual Studio — CarSelectorSample. Дело происходит в электронном магазине автомобилей. Имеются разные марки машин, причем для каждой марки имеются несколько моделей. При выборе марки машины в первом списке во второй список автоматически грузятся соответствующие модели.

Brand Buick Chevrolet Pontiac Toyota Mileage Features
Power seat
Buick Century Impala Grand Am Avalon 0-10000 Leather seat
Chevrolet LeSabre Malibu Grand Prix Camry 10000-20000 Sun roof
Pontiac Park Avenue Metro Montana Camry Solara 20000-30000 CD player
Toyota Regal Prizm Sunfire Celica 30000 and more ABS

Все данные, используемые на этой странице, собраны в таблицу. Для хранения такой таблицы есть класс DataTable. Таблица состоит из столбцов — DataColumn и строк DataRow. Класс DataView позволяет создавать различные представления данных таблицы. Первый столбец служит источником данных списка марок. В зависимости от выбранной модели в список моделей загружается одна из 2-5 колонок.

Вначале создается таблица

Здесь вызывается один из конструкторов DataColumn. Первый аргумент — название колонки, второй — тип.

Создается новая строка таблицы. Ячейка таблицы задается с помощью индекса строки.

И строка добавляется в таблицу.

У выпадающего списка марок установлено свойство AutoPostBack. Это значит, что страница автоматически подается на сервер, когда в этом списка меняется выбранный элемент.

В обработчике выбора нового элемента вначале выясняется, какой элемент выбран.

В операторе switch происходит переключение второго списка на один из столбцов таблицы заданием свойств DataTextField и DataValueField, где DataTextField — текст, отображаемый в списке, а DataValueField — выбранное значение. В данном случае, как часто бывает, они одинаковы.

Привязка к данным

Некоторые элементы управления: списки, таблицы и другие — имеют свойство DataSource, которое отвечает за привязку к данным. Тип этого свойства — object, то есть может быть любого типа, но этот тип должен реализовывать интерфейс IEnumerable. Часто значениями этого свойства назначают коллекции. В таком случае нет нужды добавлять значения вручную. Свойство DataSource может быть привязано к коллекциям, поддерживающим интерфейсы IEnumerable, ICollection или IListSource. Источником данных также могут быть XML-файлы, базы данных. Вызовом метода DataBind данные привязываются к элементу управления. Метод Page.DataBind вызывает привязку данных у всех элементов на странице.

Приведенный ниже выпадающий список помогает выбрать континент для путешествия. Источник данных — динамический массив ArrayList . Используйте его, если в программе происходит много вставок и удалений.

Можно использовать в качестве источника данных хеш-таблицы (Hashtable). Хеш-таблицы — это структуры данных, которые были придуманы давно(см. 3 том «Искусства программирования» Д.Кнута), но программисты долгое время были вынуждены реализовывать их вручную. В языке php обычный массив и есть хэш-таблица. В библиотеке STL для языка С++ тоже есть тип map, в котором данные хранятся таким способом. Хеш-таблицы позволяют очень быстро найти значение по ключу. Индекс в коллекции вычисляется как простая хэш-функция ключа. В C# ключи используются как индексаторы. Используйте Hashtable , если в программе часто осуществляется поиск. Вставка и удаление происходят в нем медленно. Ключи могут быть произвольного типа. В классе Object определен виртуальный метод GetHashCode, он и используется в Hashtable.

Здесь ключом хэш-таблицы является дата. Convert.ToDateTime конвертирует строку в тип даты. VisibleDate гарантирует, что на календаре будет июнь 2006 года. Если значений по ключу в таблице нет, то индексатор просто возвращает null. Значения можно вводить в любом порядке.

Хотелось бы добавить в страницу новую возможность введения новых записей. Можно ввести новые элементы управления — строку ввода и кнопку для подачи данных. При обработке нажатия на кнопку добавим в хэш-таблицу новое значение.

Эта страница не работает. Дело в том, что страница загружается заново, когда меняется дата. Хэш-таблица создается заново, и введенные в нее значения теряются. Как же решить эту проблему? Сделаем хэш-таблицу статической переменной.

Классы проверки данных (Валидаторы)

Данные из форм обычно записываются в базы данных, и тип информации в них должен соответствовать типу и длине данных в полях таблиц баз данных. Кроме того, иногда нужно вводить взаимосвязанные данные, например пароль во время регистрации нужно вводить 2 раза, и он в обоих полях должен совпадать. Некоторые хакеры пытаются вводить в формы программные коды, чтобы взломать вашу систему. Бдительность и еще раз бдительность, как говорил товарищ. не помню кто. А если пользователь ввел неверные данные случайно, сервер выдаст непонятное сообщение об ошибке, и ценный клиент уйдет с нашего сайта на другой.

Прежде чем работать с данными, нужно убедиться, что:

  • В определенное поле введена информация;
  • Текст в поле адрес имеет форму электронного адреса(с @ и с точкой);
  • Дата рождения разумна, например, пользователь не сообщил о себе, что ему 300 лет или 1 годик.
  • Пароль достаточно сложен и не совпадает с логином.

Проверка может происходить и на стороне клиента, и на сервере. При валидации на стороне клиента в страницу встраивается код на Javascript. Если данные в форме не проходят проверку, страница просто не будет отправлена на сервер. Таким образом избежим лишнего трафика и не будем загружать сервер. С другой стороны, валидации на стороне сервера более надежна. Javascript-код хакеры могут легко посмотреть и отправить неправильные данные, которые пройдут эту проверку. Наконец, Javascript можно просто выключить в настройках браузера. При валидации на стороне сервера данные проверяются программой на полноценном языке. Ее код пользователю неизвестен. В результате проверки генерируется новая страница с сообщениями об ошибках. Самая разумная стратегия — применять комбинацию этих методов. Предварительная проверка у клиента защитит от опечаток, а серьезная проверка на сервере — от злонамеренного взлома.

Существует целый ряд серверных элементов управления, которые не занимаются выводом информации, а проверяют данные, введенные пользователем. ASP.NET 2.0 сам определяет тип браузера и генерирует наиболее подходящий для данного случая код, который. Если браузер поддерживает Javascript-код, который он может послать, то валидация или ее часть происходит на стороне клиента. Если браузер не поддерживает Javascript, то вся валидация происходит на сервере.

Получить доступ к валидаторам просто — раскройте в Toolbox вкладку Validation.

Класы валидаторов образуют иерархию, во главе которой стоит абстрактный класс BaseValidator.

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

Общие свойства валидаторов
Display Предоставлять ли место статически или динамически
EnableClientScript Разрешать ли генерировать клиентский код
ErrorMessage Текст сообщения об ошибке
IsValid Прошел ли валидацию связанный с валидатором элемент

Инициация проверки данных

Проверка всегда инициируется каким-либо событием. Обычно это щелчок на кнопках Button, ImageButton, LinkButton, в которые по умолчанию свойство CausesValidation установлено в true. Можно убрать это свойство для некоторых кнопок, которым оно не нужно, например, кнопки Cancel.

В примере с автосалоном на странице имеются несколько валидаторов.

Класс RequiredFieldValidator проверяет, было ли изменено значение в связанном с ним элементе управления. Если, как в данном случае, это выпадающий список, первоначально выбрано пустое значение, но требуется, чтобы пользователь выбрал конкретную марку. Если не выбрал, но нажал кнопку submit, валидация проваливается и выводится текст, заданный в ErrorMessage или в Text. Валидаторы отображают текст указанный в свойстве «Text», всегда, когда оно не пусто, а текст установленный в свойстве «ErrorMessage», тогда, когда свойство «Text» равно «». Первоначальное значение задается свойством InitialValue. Если это свойство не задано, то проверка проводится на отсутствие значения (например, пустой Textbox).

Для проверки корректности ввода электронного адреса используется класс RegularExpressionValidator.

ValidationExpression — регулярное выражение, на соответствие которому проходит проверку значение текстового поля. в Visual Studio 2005 предоставляет несколько готовых шаблонов регулярных выражений, которые можно выбрать в окне свойств — телефонных номеров разных стран, адресов, и, самое полезное, шаблоны электронной почты и адреса в интернете.

С одним элементом управления может быть связано несколько валидаторов. Например, электронный адрес проверяется и на соответствие шаблону, и на обязательное заполнение.

Свойство Page.IsValid позволяет определить, прошла ли вся страница валидацию. Для браузеров, которые поддерживают DHTML, проверка происходит на стороне клиента. Для этого автоматически генерируется JavaScript-код. Таким образом экономятся ресурсы сервера и трафик, которые бы пришлось потратить на передачу неправильных данных.

Валидаторы сравнения

CompareValidator сравнивает значение со значением в другом элементе управления или с константой. Также можно проверить, можно ли конвертировать значение в связанном с ним элементе управления в какой-либо тип.

Свойство Operator позволяет установить операцию, посредством которой происходит сравнение: Equal, NotEqual, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual. Значение DataTypeCheck означает проверку на соответствие типу.

Свойство Type может принимать значения String, integer, Date, Double и Currency.

Свойство ControlToCompare задает элемент управления, с которым происходит сравнение. ValueToCompare задает значение. Из этих двух свойств установленным может быть только одно.

RangeValidator проверяет соответствие введенного значения диапазону, заданному своствами MinValue и MaxValue.

ValidationSummary

Класс ValidationSummary позволяет вывести итоговую информацию по всем валидаторам на странице. Она может быть выведена в различной форме:

  • BulletList — список со значками
  • List — обычный список
  • SingleParagraph — простой параграф

Информацию можно выводить на странице, а можно в информационном окне, если ShowMessage поставить в True. Для всех валидаторов выводится свойство Error Message, а не текст. Text выводится в самом валидаторе.

Вернемся к странице Registration.aspx. Добавим в него еще одно поле для ввода пароля.

Свойство CausesValidation работает, когда элемент управления теряет фокус. В таком случае связанный с ним валидатор показывает значение своего свойства Text.

Нам нужно, чтобы имя было обязательно введено, а пароль совпадал в обоих текстовых полях.

Перетащите RequiredFieldValidator и бросьте его на форму. ControlToValidate установите в txtName. Второй валидатор — RequiredFieldValidator для пароля. Третий — CompareValidator, который сравнивает значение паролей.

А также один ValidationSummary:

Поставим кнопку, при нажатии на которую будет происходить проверка.

Обработчик нажатия на кнопку подтверждает ввод, если валидация прошла успешно:

Вся проверка этой страницы происходит у клиента. Можете даже остановить сервер, чтобы в этом убедиться. Только когда все данные введены правильно, происходит отправка формы на сервер.

CustomValidator

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

Напишем пользователький валидатор, который будет проверять пароль на длину — не меньше 5 символов.

Проверка происходит на стороне клиента функцией на JavaScript validatePassword. Для этого надо в свойстве ClientValidationFunction записать имя функции. В сгенерированном коде к кнопке привязан такой код:

Известно, что если функция, связанная с onclick, вернет логическое значение false, форма не будет отправлена на сервер.

Произведем такую же валидацию на сервере. Чтобы запустить проверку на сервере, используется свойство OnServerValidate. Функция, которая указана в ней, входит в класс страницы и должна быть написана на C#:

ServerValidate вызывается после события Page_Load, поэтому нельзя, как в лекции 3, выводить результаты в Page_Load.

Можно также одновременно проводить и клиентскую, и серверную валидацию:

Чтобы отключить возможность генерации клиентского кода для всех валидаторов, можно написать:

Для отображения сообщения об ошибке можно использовать звуки и картинки. Для этого в свойство ErrorMessage нужно записать не текст, а соответствующих тег HTML, например:

Группы валидации.

Иногда бывает нужно иметь на странице несколько кнопок, и при нажатии на каждую вводится информация из логически взаимосвязанных групп элементов управления. Поэтому должны проверяться значения только из этой группы. У всех валидаторов и элементов управление, через которые возможен ввод информации, есть свойство ValidationGroup. Функцию Page.Validate() тоже можно использовать с таким параметром. Если происходит нажатие на кнопку с установленным ValidationGroup, запускается проверка тех валидаторов, у которых это свойство такое же.

Заключение

Назначение форм в том, чтобы получать данные от пользователей, но данные не имеют смысла, если они введены неправильно. Элементы-валидаторы позволяют автоматизировать рутинные действия по проверке данных и гарантировать ввод данных, соответствующих нашим требованиям.

Что такое код asp logextfileprotocolversion

Ситуация следующая: в инфраструктуре используются терминальные серверы (Windows server 2008) с посредником сеанс служб терминалов (Windows server 2008 R2) и перемещаемые терминальные профили пользователей. При входе в систему пользователей, которые пользуются приложением MS project professional, им выдается сообщение о том, что перемещаемый профиль был загружен неправильно. В логах при этом отображается следующие 2 ошибки в порядке очередности:

1) Имя журнала: Application
Подача: Microsoft-Windows-User Profiles General
Дата: 26.05.2010 16:57:11
Код события: 1509
Категория задачи:Отсутствует
Уровень: Предупреждение
Ключевые слова:Классический
Пользователь: domain\username
Компьютер: computername.domain.ru
Описание:
Не удалось скопировать файл C:\Users\username\AppData\Roaming\Microsoft\MS Project\Cache\
<7748ebe7-f64b-4c22-a9db-276b92a4a26d>.domain Project Server.username\название проекта[1].mpc

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

2) Имя журнала: Application
Подача: Microsoft-Windows-User Profiles Service
Дата: 26.05.2010 16:57:13
Код события: 1540
Категория задачи:Отсутствует
Уровень: Предупреждение
Ключевые слова:Классический
Пользователь: domain\username
Компьютер: computername.domain.ru
Описание:
Перемещаемый профиль неправильно синхронизирован с сервером.
Вместо него Windows загрузит локальный профиль, который был сохранен ранее.
См. предшествующие события для получения более подробных сведений.

Самое интересное то, что у пользователей, которые не пользуются MS Project profissional таких ошибок не возникает!

И еще: проверял длину этих файлов — все они около 220 символов, включая пробелы. т.е. никак не превышают 259 разрешенных.

Помогите советом, как избавится от этого! спасибо!

В ASP.NET, что называется кодом ASP?

Подробнее на мой вопрос:

HTML и JavaScript называются «клиентским кодом».

С# и VB в файлах, находящихся за кодом, называются «серверным кодом».

Итак, что такое код inline-asp и ‘runat = server’, который называется?

Лучший термин, который я могу придумать, — это «код веб-форм».

Чтобы быть явным, Microsoft называет их встроенными блоками кода.

Они представляют собой кодовые блоки, встроенные в жизненный цикл страницы, вызываемые во время фазы Render.

Разделы ASP-страницы, начинающиеся с и заканчивающиеся на %> , являются фрагменты кода и

Части, начинающиеся с , являются директивами. Блоки рендеринга кода, начинающиеся с , являются просто короткой рукой для вызова writer.Write() в методе Page.Render() .

В разделе ASP на сайте MSDN они называются «script командами, » серверных команд script « и » первичные команды script.

Ниже я включил выдержки из сайта MSDN и ссылку ссылки.

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

Команды, заключенные разделителями, называются основными script командами, которые обрабатываются с использованием основного языка сценариев. Любая команда, используемая в разделителях script, должна быть действительной для основного языка сценариев. По умолчанию основным языком сценариев является VBScript, но вы также можете установить другой язык по умолчанию.

Логгирование в ASP.NET Core

Поскольку логгирование является неотъемлемой частью любого приложения, в ASP.NET Core появилась унифицированная инфраструктура для логгирования.

Единый интерфейс для записи в лог позволяет приложениям работать с логом в едином стиле, независимо от того, какой провайдер используется.

Настройка инфраструктуры логгирования

Прежде чем начать писать что-то в логи, нужно настроить провайдеров логов. Для этого в метод Configure() класса Startup инжектируется сервис ILoggerFactory . Используя этот сервис можно настроить провайдеров:

В данном примере добавялется провайдер, который будет писать логи в консоль отладчика. Чтобы добавить этого провайдера, нужно установить пакет Microsoft.Extensions.Logging.Debug . Впрочем, он уже итак установлен в проекте, который создается в базовом шаблоне проекта ASP.NET MVC.

Аналогичным образом добавляются и другие провайдеры. Microsoft предоставляет такие провайдеры как Microsoft.Extensions.Logging.Console , Microsoft.Extensions.Logging.Debug , Microsoft.Extensions.Logging.TraceSource , Microsoft.Extensions.Logging.AzureAppServices , Microsoft.Extensions.Logging.EventSource , Microsoft.Extensions.Logging.EventLog . Как нетрудно догадаться из названия, каждый из них имеет собственный источник для хранения логов.

Можно использовать несколько провайдеров одновременно:

Запись в лог

Для того, чтобы начать писать что-то в лог, следует инжектировать интерфейс ILogger в нужный компонент приложения. Причем в качестве generic-параметра мы можем указать категорию логгера. Например, получим доступ к логгеру в компоненте HomeController :

После этого в рамках компонента можно пользоваться этим логгером и добавлять записи в лог. Для записи используются методы, соответствующие уровню важности сообщения:

Попробуем записать что-нибудь в лог:

Теперь при срабатывании этого действия в консоли отладчика обязательно появится запись:

WebApplication8.Controllers.HomeController:Error: Some error occured

Если при настройке провайдеров мы настроили также вывод в консоль ( AddConsole() ), то эта же запись появится и в консоли (если мы запускаем приложение через Kestrel и консоль нам видна).

Записывать в лог можно не только обычные строки, но и структурированные данные. Для этого существуют специальные перегрузки методов LogXXX() . Запишем, например, в лог данные с идентификатором объекта:

В логе появится строка следующего содержания:

WebApplication8.Controllers.HomeController:Error: Unable to delete object with 25 key

Если провайдер поддерживает структурированные данные, то ID будет доступно как отдельное поле. Более того, поле может быть не только простым типом, но и составным объектом. Но это — отдельная тема и здесь касаться этого не будем.

Scope

Иногда бывает полезно объединить набор сообщений лога в группы. Это можно сделать используя метод BeginScope() , реализующий IDisposable . Поэтому можно использовать конструкцию using для группировки сообщений в логе:

Не все провайдеры поддерживают такую группировку. Однако, те, кто поддерживают будут иметь пометку Updating object для каждой записи внутри scope.

Например, группировку поддерживает Microsoft.Extensions.Logging.Console . Для её использования это нужно указать явно при настройке провайдера:

В таком случае для каждого сообщения внутри scope будет добавлена строка Updating object :

Фильтрация логов

В логи обычно записывается большой объем отладочной информации. Например, инфраструктура ASP.NET записывает туда информацию о каждом обработанном HTTP-запросе. Это может быть полезно во время отладки, но когда приложение работает на боевом сервере, эта информация не нужна. Более того, в большом потоке отладочной информации можно не увидеть действительно ценного — сообщения об ошибках или каких-либо нестандартных ситуациях. Поэтому для разных окружений настройки логгирования можно настроить по-разному.

Все записи в логах разделяются по степени важности. Например, методы LogDebug() и LogInformation() записывают событие с какими-то отладочными данными. Их смысл в том, чтобы дать представление о процессах, которые происходят в приложении, но не более. С другой стороны LogWarning() , говорит о том, что в приложении произошло что-то, на что было бы неплохо обратить внимание. Наконец, LogError() или LogCritical() записывает информацию об ошибках — т.е. приложение повело себя не так, как планировалось, и на такие записи обязательно нужно обращать внимание.

В ASP.NET Core можно задать минимальный уровень для фильтрации логов. Например, при отладке можно разрешить показывать все сообщения, включая Trace , Debug и Information . При этом на боевом сервере эти сообщения можно зафильтровать и отображать только ошибки.

Это поведение задается при помощи специального параметра:

В этом случае всё, что менее критично, чем Error или Warning будет игнорироваться для этих провайдеров.

Но не все категории записей в логе одинаково полезны. Например, в каких-то ситуациях нам могут быть неинтересны ошибки, которые пишутся в лог инфраструктурой ASP.NET, даже если для них задан уровень Error , но при этом очень важны записи, сделанные нашими собственными сервисами, причем для менее приоритетных уровней.

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

Вот как например можно настроить провайдер для записи в консоль:

Другой способ — использовать конфигурационный файл для настройки уровней логгирования:

Конфигурационный файл в этом случае может выглядеть так:

Ещё один удобный способ настроить фильтры — это использовать пакет Microsoft.Extensions.Logging.Filter . После установки этого пакета для ILoggerFactory , появится метод-расширение WithFilter() , позволяющий настроить фильтры:

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

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL