Разработка веб-сайтов, впрочем как и любых других софтверных строений, всегда стояла на распутьи трех дорог: скорость разработки, качество продукта и производительность веб-сайта. И именно на последнее и пожаловался недавно наш заказчик...
Что-то случилось...
... понял я, когда в час ночи зазвонил мой телефон. Сквозь сон я выслушал все новогодние "пожелания" заказчика и краткое описание проблемы - "Страница медленно работает". Затем пока нашел штаны, пока нашел где у меня ноги, пока оделся и, спустя часы, я оказался на работе. Нагрузочное (или даже стресc-тестирование) показало, что страница действительно забирает 90% процессорного времени при больших объемах данных на ней. После небольшого разбирательства выяснилось, что показания %Time in GC счетчика производительности меньше оптимальных. После чего наш консилиум заключил, что приложение нерационально использует оперативную память сервера.
MSDN говорит:
.NET CLR Memory\% Time in GC
Threshold: This counter should average about 5 percent for most applications when the CPU is 70 percent busy, with occasional peaks. As the CPU load increases, so does the percentage of time spent performing garbage collection. Keep this in mind when you measure the CPU.
Significance: This counter indicates the percentage of elapsed time spent performing a garbage collection since the last garbage collection cycle. The most common cause of a high value is making too many allocations, which may be the case if you are allocating on a per-request basis for ASP.NET applications. You need to study the allocation profile for your application if this counter shows a higher value.
За этим последовал небольшой code review, во время которого выяснилось, что приложение создает дофига коллекций. Коллекции использовались для переноса данных из BLL в Presentation Layer, и последующей конвертации их в DataTable (для байндига в GridView). Таким образом мы создали три анти-паттерна производительности (во завернул-то!): храним много данных в памяти, создаем большое количество циклов и конвертаций типов.
Пути решения
- Байндить коллекции сразу GridView, без конвертации в DataTable
- Создавать DataTable сразу из XML файла, минуя коллекции во избежания больших циклов и конвертации типов
- Использовать XSLT для трансофрмации XML в HTML таблицу (нафиг тогда вообще ASP.NET?)
- Использовать Response.Write(), как это предлагает сделать Майкрософт
Решили попробовать последнее.
В результате получили следующее:
| GridView |
Response.Write() |
|
|
Интересные результаты, неправда ли? И хотя мы выиграли в производительности мы проиграли как минимум в удобстве и безопасности: GridView не просто рендерит таблицу, а и реализует некий функционал для управления и взаимодействия с данными в ней. А также делает проверки на cross-site scripting и другие виды атак. Так, что настаивать на Response.Write не буду. Решение использовать зависит от конкретной ситуации и пожеланий вашего заказчика.
Полезные ссылки
Improving ASP.NET Performance
Measuring .NET Application Performance
Code Review: .NET Application Performance
Мы продолжаем двигаться по нашему уравнению производительности приложений в глобальных сетях. И на очереди у нас оптимизация количества файлов, загружаемых браузером.
AppTurns - число файлов ресурсов, требуемых страницей. Они включают файлы CSS, JS, изображений и любые другие файлы, извлекаемые обозревателем в процессе визуализации страницы. Уравнение учитывает страницу HTML отдельно, добавляя время приема-передачи (round-trip time – RTT) перед выражением AppTurns.
Использование множества CSS- и JS-файлов небольшого размера, является хорошей практикой с точки зрения удобства в разработке веб-приложения. Но плохо, с точки зрения производительности вашего приложения. Каждый запрос к серверу требует определенных ресурсов как браузера, так и самого сервера: ожидание в очереди в браузере, время на инициализацию и отправку запроса, передача соответствующих заголовков и cookies, время на обработку запроса и формирования ответа. Время, необходимое для обработки/формирования запроса, называется latency(серенькая область на рисунках ниже). Каждый ресурс, помещенный в отдельный файл, будет бесцеремонно занимать это драгоценное для клиента время.
Поэтому имеет смысл объединять однотипные файлы ресурсов в один. Таким образом, мы экономим на этот самом latency каждого файла ресурсов.
Так как мы говорим о CSS или JS коде, то объединение будет представлять собой конкатенацию строк. Проще всего написать HttpHandler, который бы смог объединять файлы ресурсов в runtime режиме, оставляя разработчику возможность оперировать множеством файлов. При этом не стоит забывать о таких способах оптимизации как GZIP и кэширование ваших ресурсов. Код такого HttpHandler'a может быть таким:
1: public void ProcessRequest (HttpContext context) {
2:
3: HttpRequest request = context.Request;
4:
5: // Читаем параметры: имя ключа в конфиге(содержит список файлов для объединения), тип конетнат и версию конента.
6: //Все эти поля обязательны, т.к. они используются как ключ для кэширования.
7: string setName = request["s"] ?? string.Empty;
8: string contentType = request["t"] ?? string.Empty;
9: string version = request["v"] ?? string.Empty;
10:
11: // Определяем, позволяет ли браузер сжатие контента
12: bool isCompressed = DO_GZIP && this.CanGZip(context.Request);
13:
14: // Ответ формируем в UTF8 кодировке
15: UTF8Encoding encoding = new UTF8Encoding();
16:
17: // Если ресурс есть в кэше, то берем из кэша.
18: // В противном случае, считываем файлы, объединяем и кладем в кэш
19: if (!this.WriteFromCache(context, setName, version, isCompressed, contentType))
20: {
21: using (MemoryStream memoryStream = new MemoryStream(5000))
22: {
23: // Определяем тип потока, в который будем писать результат
24: using (Stream writer = isCompressed ?
25: (Stream)(new GZipStream(memoryStream, CompressionMode.Compress)) :
26: memoryStream)
27: {
28:
29: // Загружаем файлы, основываясь на ключе в <appSettings>
30: string setDefinition =
31: System.Configuration.ConfigurationManager.AppSettings[setName] ?? "";
32: string[] fileNames = setDefinition.Split(new char[] { ',' },
33: StringSplitOptions.RemoveEmptyEntries);
34: string delimeter = "\r\n";
35: foreach (string fileName in fileNames)
36: {
37: //Добавляем разделитель, чтобы в двух соседних файлах не произошло объединения ключевых слов.
38: byte[] bytes = encoding.GetBytes(delimeter);
39: writer.Write(bytes, 0, bytes.Length);
40:
41: string text = GetFileText(context, fileName.Trim(), encoding);
42: byte[] fileBytes = encoding.GetBytes(text);
43: writer.Write(fileBytes, 0, fileBytes.Length);
44: }
45:
46: writer.Close();
47: }
48:
49: //Кэшируем результат
50: byte[] responseBytes = memoryStream.ToArray();
51: context.Cache.Insert(GetCacheKey(setName, version, isCompressed),
52: responseBytes, null, System.Web.Caching.Cache.NoAbsoluteExpiration,
53: CACHE_DURATION);
54:
55: // Генерируем ответ
56: this.WriteBytes(responseBytes, context, isCompressed, contentType);
57: }
58: }
59: }
Функция GetFileText читает содержимое файла и возвращает его в виде строки (ввиду простоты функции, я не стал приводить листинг ее кода).
Второй путь уменьшить количество компонентов страницы — упростить ее дизайн. А есть ли способ сохранить внешний вид, при этом уменьшив время загрузки? Ниже следует несколько приемов, позволяющих добиться уменьшения количества запросов к серверу, сохраняя функциональность и внешний вид.
Image Map‘ы объединяют несколько картинок в одну большую. Общий объем такой картинки примерно равен сумме объемов маленьких картинок, но уменьшение количества запросов к серверу сокращает общее время загрузки страницы. Image Map будет работать, если картинки на странице находятся рядом друг с другом, например в случае полосы навигации. Определение координат для Image Map’ов — занятие довольно утомительное и зачастую приводит к ошибкам (вообще-то для этого есть специальный софт, однако ничего не могу сказать про качество его работы).
CSS-спрайты являются предпочтительным методом сокращения количества запросов на сервер. Объедините все картинки веб-страницы в одну большую картинку и используйте CSS-свойства для отображения нужного участка картинки.
Inline-картинки используют URL-схему data: для встраивания картинки в саму страницу. Это, однако, увеличит размер HTML-документа. Встраивайте inline-картинки в ваши (кешированные) таблицы стилей — и вы добьетесь уменьшения запросов к серверу, а размер HTML останется прежним.
Полезные ссылки
Быстрые веб-страницы: Формирование страницы из нескольких источников
Быстрые веб-страницы: Сжатие и Очистка веб-страниц
Performance Research, Part 6: Less is More — Serving Files Faster by Combining Them
Combine JavaScript with ASP.NET Web forms
CSS Sprites: What They Are, Why They’re Cool, and How To Use Them
Недавно мы рассмотрели механизмы оптимизации размера страницы. Следующим нашим объектом оптимизации будет пропускная способность сервера.
Пропускная способность - скорость передачи данных обозревателю и от него. Может быть асимметрична и представлять несколько скоростей, если данная страница создается из нескольких источников. Обычно усредняется для создания единого выражения пропускной способности в байтах в секунду.
Зачастую, мы ложим статический контент (картинки, javascript, css-код) в тот же самый веб-проект или веб-сайт. Это приводит к тому, что эти ресурсы скачиваются браузером с одного домена. Такой подход может привести к следующим проблемам:
- Все ресурсы создают подключения к вашему домену. Поэтому другие, более важные запросы (например запросы к веб-сервису) не могут быть осуществлены, т.к. браузер может создать только два подключения к одному домену.
- Если вы используете ASP.NET Forms Authentication, то гигансткий Forms Authentication cookie будет посылаться с каждым запросом на ваш домен. Эти cookie будут отправляться при запросе картинки, css-кода или javascript файла, хотя все эти ресурсы, зачастую, не нуждаются в cookie.
- Такой подход также увеличит размер IIS лога, т.к. он сохраняет cookie для каждого запроса вашего статического контента. Больше того, если вы используете Google Analytics, то к вашим cookie прибавятся еще и четыре больших cookie от компании Google.
Давайте рассмотрим первую проблему более детально
Этот график показывает, что одновременно закачиваются только два файла. Все это происходит только для одного домена. Обратите внимание на загрузку скрипта. Браузер ничего не закачивает параллельно со скриптом. Это объясняется особенностью браузеров в обработке тэга <script>. Таким образом, закачка следующих ресурсов начнется только после завершения закачки и выполнения скрипта.
Но если браузеру удастся открыть еще одно подключение, то закачка той же страницы будет уже выглядеть по-другому
Как видите, время загрузки уменшилось примерно на 40%. Помимо графики, мы можем загружать подобным образом CSS и Javascript-код.
Другая проблема, возникающая при загрузке статического контента с того же домена – это огромные Forms Authentication и Google Analytics cookie:
Таким образом дополнительные 2-3 килобайта будут сопровождать каждый запрос к вашему домену. А оно вам надо?
Решить данну проблему можно двумя способами. Первый, и самый неудобный – располагать статический контент в другом домене и сразу ссылаться на него, при разработке проекта. Второй, более приемлемый – написать свой HttpFilter. Этот фильтр будет просматривать содрежимое сраницы, перед отправкой контента пользователю, выявлять href/src атрибуты и прибавляет к ним некий префикс. Скажем если у меня на странице есть следующий код:
1: <img src="\images\myphoto.jpg" alt="my photo" />
То фильтр может его трансформировать в следующее:
1: <img src="http://images.podlipensky.com/myphoto.jpg" alt="my photo" />
После чего вам необходимо будет создать Url Rewritting модуль (или воспользоваться встроенным в IIS 7), который будет изменять url на свой прежний эквивалент. Для высоконагруженных систем, особенно если они оперируют в основном статическим контентом (например видео- или фото- галлереи) полезно действительно размещать этот контент на других серверах. В этом случае можно еще подключить новую фичу IIS 7 – Application Request Routing. Либо воспользоваться Content Delivery Network сервисом.
Впрочем, все вышеописанные методы не новы, и давно используются в коммерческих проектах. Пример подобных оптимизаций, с хорошо описанными исходными кодами, вы сможете найти у Omar AL Zabir’a(который любезно разрешил мне воспользоваться диаграммами, приведенными выше).
Полезные ссылки
Smart caching for websites and blogs
IIS URL Rewriting and ASP.NET routing
Omar AL Zabir blog on ASP.NET Ajax and .NET 3.5
Несмотря на стремительное развитие интернета, проблема оптимизации веб-приложений стоит также остро, как и во времена dialup-подключений по советской вермишели.
В сентябре 2006 года Питер Севчик (Peter Sevcik) и Ребекка Ветцель (Rebecca Wetzel) из компании NetForecast опубликовали документ, именуемый "Field Guide to Application Delivery Systems" («Полевое руководство по системам доставки приложений»). Основное внимание в документе было уделено улучшению производительности приложений в глобальных сетях (wide area network – WAN), и в нем было приведено уравнение
, где
- R - время ответа. Общее время с запроса страницы пользователем (обычно переходом по ссылке и т.п.) до полной визуализации страницы на его компьютере. Обычно измеряется в секундах.
- Полезная нагрузка - общее число байтов, отправленных обозревателю, включая разметку и все ресурсы (такие как файлы CSS, JS и изображений).
- Пропускная способность - скорость передачи данных обозревателю и от него. Может быть асимметрична и представлять несколько скоростей, если данная страница создается из нескольких источников. Обычно усредняется для создания единого выражения пропускной способности в байтах в секунду.
- AppTurns - число файлов ресурсов, требуемых страницей. Они включают файлы CSS, JS, изображений и любые другие файлы, извлекаемые обозревателем в процессе визуализации страницы. Уравнение учитывает страницу HTML отдельно, добавляя время приема-передачи (round-trip time – RTT) перед выражением AppTurns.
- RTT - время, необходимое для приема и передачи данных, вне зависимости от числа переданных байтов. Каждый запрос тратит минимум одно RTT для самой страницы. Обычно измеряется в миллисекундах.
- Cs - время вычислений на сервере. Время, уходящее у кода на запуск, извлечение данных из базы данных и составление ответа, отправляемого обозревателю. Измеряется в миллисекундах.
- Cc - время вычислений на клиенте. Время, уходящее у обозревателя на визуализацию HTML на экране, исполнение JavaScript, применение правил CSS, итд.
Этот пост будет началом серии статей по оптимизации производительности вашего веб-приложения.
Рассмотрим первый параметр, влияющий на время загрузки страницы – полезная нагрузка или объем полезных данных.
Архивирование(сжатие) контента страницы
Первое, что мы можем сделать - это сжать наш html, css и js код с помощью GZip или Deflate(~40% лучше, чем gzip) сжатия. Не все браузеры принимают сжатый контент, поэтому необходимо проверять, содержит ли запрос заголовок Accept-Encoding со значением “gzip” или “deflate”:
1: private bool CanGZip(HttpRequest request)
2: {
3: string acceptEncoding = request.Headers["Accept-Encoding"];
4: if (!string.IsNullOrEmpty(acceptEncoding) &&
5: (acceptEncoding.Contains("gzip") || acceptEncoding.Contains("deflate")))
6: return true;
7: return false;
8: }
Программно сжать контент позволяет пространство имен System.IO.Compression
1: public static byte[] Compressor(byte[] buffer, string encodingType)
2: {
3: using (MemoryStream memStream = new MemoryStream())
4: {
5: Stream compress = null;
6:
7: // Choose the compression type and make the compression
8: if (String.Equals(encodingType, "gzip", StringComparison.Ordinal))
9: {
10: compress = new GZipStream(memStream, CompressionMode.Compress);
11: }
12: else if (String.Equals(encodingType, "deflate", StringComparison.Ordinal))
13: {
14: compress = new DeflateStream(memStream, CompressionMode.Compress);
15: }
16: else
17: {
18: // No compression
19: return buffer;
20: }
21:
22: compress.Write(buffer, 0, buffer.Length);
23: compress.Dispose();
24:
25: return memStream.ToArray();
26: }
27: }
После чего вам остается лишь добавить сжатый поток в ответ пользователю:
1: HttpContext.Response.OutputStream.Write(compressedData, 0, compressedData.Length);
Если же у вас есть доступ к IIS консоли, то вы можете настроить сжатие на уровне IIS(версии 7):
Очистка JS и CSS кода
Также мы можем очистить наш css и js код от лишних символов.
Например, следующий css код
1: body {
2: font-size:14px;
3: font-family:Arial;
4: }
5: h1 {
6: font-size:14px;
7: font-family:Arial;
8: }
можно преобразовать в следующую строку
1: body,h1{font:14px arial}
Основной принцип подобных алгоритмов – это превращение человеко-читемого кода в сжатый код понятный лишь компилятору.
Для javascript кода наибольшей популярностью пользуются два инструмента JSMin (автор: Douglas Crockford) и Packer (автор: Dean Edward). Алгоритмы упаковки обоих инструментов обрабатывают(obfuscate) любой js код, но только при условии, что вы корректно завершаете все javascript-выражения символом “;” (включая объявление функций).
Для css кода вы можете воспользоваться утилитой под названием CSSTidy или одним из множества подобных онлайн-сервисов.
Оптимизация графики
Допустим в вашей базе данных, на жестком диске или любом другом хранилище расположена ваша любимая фотография размером 200x400 пикселей (24 Kb). Но вам необходимо отобразить ее на странице в виде изображения размером 100х200 пикселей. В таком случае, подобный код
1: <img src="/images/mybestphoto.png" width="100" height="200" alt="My Best Photo" />
решит вашу проблему, но при этом размер вашей фотографии останется прежним - 24 Kb. Хотя, если бы вы сжали(изменили размер) ее в фотопопе, то "вес" файла существенно бы уменьшился.
Для того, чтобы достичь подобного эффекта, вам необходимо программно, на сервере сжать вашу графику. Примерно таким образом:
1: public void SaveResizedImage(string imagePath, int width, int height)
2: {
3: int newResizeimageId;
4: // Читаем графический файл с жесткого диска
5: FileStream fs = File.OpenRead(imagePath);
6: byte[] data = new byte[fs.Length];
7: fs.Read(data, 0, data.Length);
8: MemoryStream ms = new MemoryStream(data);
9: Bitmap bmp = new Bitmap(ms);
10: System.Drawing.Image imgPhoto = (Image)bmp;
11: // Настраиваем Encoder, для преобразования графики
12: Encoder qualityEncoder = Encoder.Quality;
13: EncoderParameters parameterList = new EncoderParameters(1);
14: EncoderParameter qualityParameter = new EncoderParameter(qualityEncoder, 50L);
15: parameterList.Param[0] = qualityParameter;
16: // По расширению файла определяем кодек
17: ImageCodecInfo codec = GetCodecFromExtension(Path.GetExtension(imagePath)); //GetCodecFromExtension в этом листинге не приведена.
18: // Создаем пустую канву. Измененная графика будет записана в эту канву.
19: Bitmap bmPhoto = new Bitmap(width, height);
20: bmPhoto.SetResolution(10, 10);
21: Graphics grPhoto = Graphics.FromImage(bmPhoto);
22: grPhoto.SmoothingMode = SmoothingMode.HighSpeed;
23: grPhoto.InterpolationMode = InterpolationMode.High;
24: grPhoto.PixelOffsetMode = PixelOffsetMode.HighSpeed;
25: grPhoto.DrawImage(imgPhoto, new Rectangle(0, 0, width, height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel);
26: // Сохраняем файл в память, а затем убеждаемся, что все ресурсы освобождены корректно.
27: MemoryStream mm = new MemoryStream();
28: bmPhoto.Save(mm, codec, parameterList);
29: imgPhoto.Dispose();
30: bmPhoto.Dispose();
31: grPhoto.Dispose();
32: //Сохраняем массив байт измененной графики куда необходимо.
33: newResizeimageId = SaveImageToDb2(mm.GetBuffer());
34: mm.Dispose();
35: }
Хочу обратить ваше внимание на строчку
1: bmPhoto.SetResolution(10, 10);
Где мы можем изменить качество нашей графики, установив новое разрешение. Результаты могут быть примерно такими
 |
 |
 |
 |
 |
| 2.58 kB |
2.86 kB (+10%) |
1.98 kB
(-23%) |
1.55 kB
(-39%) |
672 bytes
(-74%) |
Обратите внимание на то, что после небольшого ухудшения качества, размер изображения наоборот - возрос. Дальнейшее же уменьшения разрешения привело к уменьшению размера файла. Также учтите, что, чем больше первоначальный размер изображения, тем эффективней будет сжатие без визуальных потерь качества. Ухудшать качество небольших картинок, как видите, смысла нет.
Отключаем ViewState
Возможно вы подумали, о нехитром свойстве страницы EnableViewState="false". Отключение ViewState с помощью вышеупомянутого свойства действительно уменьшит размер скрытого поля, но не избавит вас от него. В отдельных случаях полезно и вовсе избавиться от ViewState. Сделать это можно, переопределив метод SavePageStateToPersistenceMedium, и “забыв” вызвать в нем базовый метод.
1: protected override void SavePageStateToPersistenceMedium(object state)
2: {
3: //base.SavePageStateToPersistenceMedium(state);
4: }
Избавляемся от длинных ID, сгенерированных ASP.NET
К сожалению, не существует способа избавиться от длинных ID в debug-версии вашего веб-сайта. Но когда ваш проект уже готов и скомпилирован, то вы можете обработать сборки, обфускатором. При этом, следует указать обфускатору заменять все имена контролов на более короткие. Таким образом вместо ctl00_body_WidgetPanelsLayout_WidgetContainer65_WidgetBodyPanel, ваша панель будет иметь id равное, например А. Будьте осторожны с подобной оптимизацией – ваш javascript код может не работать, если функционал привязан к заранее заданным ID. Хотя, если вы использовали что-то похожее на следующий код
1: $get('<%= TextBox1.ClientID %>')
то обфускатор не нарушит вашего функционала.
Полезные ссылки
Reduce the weight of stylesheets by 35% at runtime
HTTP compression in ASP.NET 2.0
Compression and performance - GZip vs. Deflate
Compress your pages, css, js and WebResources.axd files for better performance
Online Tool compares Packer, JSMin, Dojo, and YUI Compressor
Speed Up Your Site: Optimize your CSS
CSS Optimization: Make Your Sites Load Faster for Free
Недавно у меня возникла необходимость динамически подгружать пользовательские контролы на страницу. Это довольно просто реализовывается с помощью UpdatePanel. Но мне этот вариант не подошел, так как страница должна весить как можно меньше и работать как можно быстрее. Поэтому выбор пал именно на связку ASP.NET и JQuery + Web Service. Для начала давайте разберемся каким образом можно вызвать Web Method с помощью JQuery.
function getJsonAjaxObject(webServiceURL, jsonData) {
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: serviceURL,
data: jsonData,
success:
function(msg){
//код обработки успешного вызова веб-метода
},
error:
function(XMLHttpRequest, textStatus, errorThrown){
//код обработки ошибки выполнения веб-метода
}
});
}
Из всего множества методов (load, get, post, getJSON, ajax) я выбрал именно ajax т.к. этот метод позволяет наиболее полно контролировать процесс создания асинхронного запроса к веб-сервису.
| Имя |
Тип |
Описание параметра |
| type |
string |
возможные варианты – GET или POST. Если параметр упущен, то по умолчанию будет использоваться метод GET. Типы запроса PUT или DELETE тоже могут быть использованы, но они поддерживаются не всеми браузерами. |
| contentType |
string |
при передаче данных серверу сообщает content-type. По умолчанию - application/x-www-form-urlencoded. |
| url |
string |
адрес веб-сервиса. Например, http://mysite.com/myservice.asmx/mywebmethod |
| data |
object |
данные, передаваемые на сервер. Добавляются в URL GET-запроса. Если используется объект, он должен представлять собой пары ключ/значение. Если значения представлены в виде массива, jQuery сериализует значения с некоторым ключом. Например: {foo:["bar1", "bar2"]} будет представлено как "&foo=bar1&foo=bar2". |
| success |
function |
указывается функция, которая будет вызвана в случае успешного завершения запроса. Функция принимает два аргумента: данные, возвращенные с сервера и отформатированные с учетом параметра dataType и строка, описывающая статус ответа(в нашем примере это строка всегда будет “success”). |
| error |
function |
определяет функцию, которая будет вызвана в случае неуспешного выполнения запроса. |
| dataType |
string |
строка, описывает тип данных, которые ожидаются в качестве ответа сервера. Если не определена, то jQuery сам попытается определить тип, основываясь на MIME-типе ответа сервера. Доступные типы: - xml: вернет XML документ, который может быть обработан через jQuery. - html: вернет HTML как plain text. - script: расценивает ответ как JavaScript и возвращает его как plain text. - json: расценивает ответ как данные в формате JSON и возвращает JavaScript объект. - jsonp: загружает данные в формате JSON используя JSONP. Необходимо дополнительно добавить "?callback=?" в конец строки запроса в URL, чтобы определить вызов. (Добавлено в jQuery 1.2). - text: строка plain text.
|
| async |
boolean
|
по умолчанию все запросы выполняются в асинхронном режиме (значение true). Если необходимо выполнить синхронный запрос, следует установить значение false для этой опции. Помните, что синхронный запрос может временно заблокировать браузер, запретив любые действия во время выполнения запроса. |
| beforeSend |
function
|
может содержать функцию, которая должна быть вызвана до передачи запроса. beforeSend - это Ajax Event (события рассмотрены в другой статье). |
| cache |
boolean
|
опция добавлена в jQuery 1.2. По умолчанию - true. Если установить в false, запрашиваемая страница не будет кэшироваться браузером. |
| complete |
function
|
определяет функцию, которая будет вызвана по окончанию запроса (успешного и ошибочного выполнения). Принимает два аргумента: объект XMLHttpRequest и строку, описывающую "успешность" запроса. complete - это Ajax Event (события рассмотрены в другой статье). |
| global |
boolean
|
true или false. Вызывать или нет глобальные обработчики событий Ajax для этого запроса. Может быть использована для контроля различных Ajax событий. По умолчанию - true. |
| ifModified |
boolean
|
запрос будет успешным только в случае, если данные ответа изменились со времени последнего запроса. По умолчанию - false. |
| processData |
boolean
|
по умолчанию true. В опции data данные представлены в виде объекта, который преобразуется в строку запроса, подходящую для content-type по умолчанию - "application/x-www-form-urlencoded". Если необходимо иное, установите опцию processData в false. |
| timeout |
number
|
устанавливает локальный тайм-аут для запроса в миллисекундах. Отменяет глобальный тайм-аут для этого запроса, если тот определен через $.ajaxSetup |
Необходимо также учесть, что ASP.NET Ajax enabled веб-сервис разрешает только HTTP POST (по умолчанию) для вызова метода, который возвращает JSON.
В большинстве случаев IIS 6 и выше требует наличия content-length элемента в запросе POST, даже если данных в запросе нет. Content-length для запроса без данных должен быть равен 0, но JQuery, в таком случае, этот элемент не добавляет. Поэтому для readonly запросов POST необходимо указывать пустой JSON объект:
data: “{}”
Это натолкнет JQuery на мысль указать корректный content-length и ваш веб-сервис сможет правильно распознать readonly-запрос, отбросив при этом пустой параметр.
Хочу еще обратить ваше внимание на то, что contentType установлен в "application/json;charset=utf-8". Этого требует ASP.NET AJAX. JSON-запросы, не имеющие подобного заголовка будут проигнорированы ASP.NET.
Еще одна проблема, с которой мне пришлось столкнуться, это “Maximum length exceed” ошибка. Она возникает в случае, когда длина JSON-ответа превышает длину по умолчанию. Во избежание этой ошибки необходимо добавить в web.config следующее:
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="5000000" />
</webServices>
</scripting>
</system.web.extensions>
Вот так выглядит код моей страницы
<%@ Page Language="C#" EnableViewState="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="head" runat="server">
<title>Add ASP.NET User Control dynamically with JQuery</title>
<script language="javascript" type="text/javascript" src="Scripts/jquery-1.2.6.pack.js"></script>
<style type="text/css">
body
{
width:95%;
padding-left:20px;
font-family:Arial;
font-size:10pt;
padding-right:20px;
}
</style>
</head>
<body>
<form id="form" runat="server">
<input type="button" value="Load GridView" onclick="getData('ScriptService.asmx/GetControlHtml','~/Controls/GridView.ascx');" />
<input type="button" value="Load Login Control" onclick="getData('ScriptService.asmx/GetControlHtml','~/Controls/LoginControl.ascx');" />
<div id="testDiv"></div>
</form>
<script type="text/javascript">
…
</script>
</body>
</html>
JavaScript для вызова веб-метода и динамического добавления пользовательского контрола на страницу
function getData(serviceURL, controlLocation) {
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
url: serviceURL,
data: "{'controlLocation':'" + controlLocation + "'}",
success:
function(msg){
$('#testDiv').html(msg.d);
},
error:
function(XMLHttpRequest, textStatus, errorThrown){
alert( "Error Occured!" );
}
});
}
Для товарищей, малознакомых с JQuery (к коим отношусь пока и я), хочу
объяснить следующую строку javascript-кода
$('#testDiv').html(msg.d);
Собственно в этой строке и происходит все чудо, а именно нам возвращается
некий JSON-объект (msg), содержащий в себе строку. Часть строки $(‘#testDiv’)
вернет нам набор объектов с id=’testDiv’ (в нашем случае это будет один div),
далее мы вызываем метод html, который превратит нашу строку в дерево объектов и
поместит его в коллекцию дочерних элементов нашего div’a. Эту операцию можно
было записать несколько иначе:
$("#someElement")[0].innerHTML = msg.d;
Возможно, такая запись для кого-то будет понятней.
Данный пример доступен для скачивания. Исходные коды на C#.
Подобного рода шпаргалки полезны для запоминания синтаксиса языка, визуального представления взаимосвязей и просто украшают ваш офис. Пользуйтесь на здоровье.
HTML/XHTML
CSS
JavaScript
Miscellaneous
С недавних пор начал интересоваться оптимизацией сайтов не только с точки зрения производительности, но и с точки зрения оптимизации под поисковые машины. Чтож поделаешь, заказчики хотят зарабатывать на рекламе, а я по-прежнему хочу зарабатывать на заказчиках...
Для всех давно не секрет, что такое PR, и с помощью каких систем его
можно определить. А все ли знают как он опредляется? Напомню, что PR опредляется
не для всего сайта целиком, а для каждой отдельной страницы.
При определении PageRank необходимо учесть все внутренние и внешние
ссылки на страницу.
Формула опредления:
PR(A) = (1-d) + d(PR(t1)/C(t1) +… + PR(tn)/C(tn)),
где PR(t1…tn) - вес страницы, ссылающейся на страницу A,
C - количество исходящих ссылок со страницы А,
d - коэффициент затухания, обычно принимаемый 0.85.
Из уравнения следует, что одна ссылка со страницы с PR4 и пятью
исходящими ссылками передаст больший вес, чем ссылка со страницы с PR8
и сотней исходящих ссылок. Чем больше исходящих ссылок на странице, тем
меньший PageRank будет передан по такой ссылке, и это не зависит от
того - внешние ли это будут ссылки или внутренние. При этом у страниц
отдающих свой "голос" за другие страницы PR не уменьшается!!!
Что делать если у вас много исходыщих ссылок на всех страницах?
Неоходимо все ссылки которые только можно - скрывать от поисковых
систем. О том как это сделать в ASP.NET хорошо написал Дмитрий.
Согласно вышеуказанной формуле можно делать прогнозы на будущее!
Можно самому написать скрипт по пронозу, но зачем делать то - что
кто-то уже сделал это раньше… Данный сервис помимо прогноза показывает общее количество бэков на ваш
сайт.
Подводя итоги: делайте
как можно больше ссылок не только на главную страницу, но также и на
остальные внутренние страницы, в своих постах ставьте тематические
ссылки на ваши другие (ранее написанные) посты, всё лишнее скрывайте от
“глаз” поисковика…