این روزها هیچکدام از فناوریهای دسترسی به داده بدون امکان یکپارچگی آنها با سیستمها و روشهای متفاوت caching ، مطلوب شمرده نمیشوند. ایده اصلی caching هم به زبان ساده به این صورت است : فراهم آوردن روشهایی جهت میسر ساختن دسترسی سریعتر به دادههایی که به صورت متناوب در برنامه مورد استفاده قرار میگیرند، بجای مراجعه مستقیم به بانک اطلاعاتی و خواندن اطلاعات از دیسک سخت.
یکی از تفاوتهای مهم NHibernate با اکثر ORM های موجود داشتن دو سطح متفاوت cache است : first level cache & second level cache .
برای نمونه Entity framework (در زمان نگارش این مطلب) تنها first level caching را پشتیبانی میکند و پروایدر توکار و یکپارچهای را جهت second level caching ارائه نمیدهد.
در این قسمت قصد داریم First Level Cache را بررسی کنیم.
سطح اول caching در NHibernate چیست؟
سطح اول caching در تمام ORM هایی که آنرا پشتیبانی میکنند مانند NHibernate ، در طول عمر یک تراکنش تعریف میگردد. در این حالت در طی یک تراکنش و طول عمر یک سشن، دریافت اطلاعات هر رکورد از بانک اطلاعاتی، تنها یکبار انجام خواهد شد؛ صرفنظر از اینکه کوئری دریافت اطلاعات آن چندبار فراخوانی میگردد. یکی از دلایل این روش هم آن است که هیچ دو شیء متفاوتی که هم اکنون در حافظه قرار دارند نباید بیانگر یک رکورد واحد از بانک اطلاعاتی باشند.
در NHibernate به صورت پیش فرض هر زمانیکه از شیء استاندارد session استفاده میکنید، سطح اول caching نیز فعال است. درست در زمانیکه سشن خاتمه مییابد، این سطح از caching نیز به صورت خودکار تخلیه خواهد گردید.
به first level caching اصطلاحا thought-out cache system یا Cache Through pattern و یا identity map هم گفته میشود.
مثال:
روش متداول و استاندارد کار با NHibernate عموما به صورت زیر است:
الف) دریافت شیء Session از Session Factory
ب) شروع یک تراکنش با فراخوانی متد BeginTransaction شیء Session
ج) برای مثال دریافت اطلاعات رکوردی با ID مساوی یک به کمک متد Get مرتبط با شیء Session : این اطلاعات مستقیما از بانک اطلاعاتی دریافت خواهد شد.
د) سپس مجددا سعی در دریافت رکوردی با ID مساوی یک. اینبار اطلاعات این شیء مستقیما از cache خوانده میشود و رفت و برگشتی به بانک اطلاعاتی نخواهیم داشت. به همین جهت به این روش identity map هم گفته میشود، زیرا NHibernate بر اساس ID منحصربفرد این اشیاء ، identity map خود را تشکیل میدهد.
ه) خاتمهی سشن با فراخوانی متد Close آن
بلافاصله
الف) دریافت شیء Session از Session Factory
ب) شروع یک تراکنش با فراخوانی متد BeginTransaction شیء Session
ج) برای مثال دریافت اطلاعات رکوردی با ID مساوی یک به کمک متد Get مرتبط با شیء Session : این اطلاعات مستقیما از بانک اطلاعاتی دریافت خواهد شد (زیرا در یک سشن جدید قرار داریم و همچنین سشن قبلی بسته شده و کش آن تخلیه گشته است).
د) خاتمهی سشن با فراخوانی متد Close آن
سؤال: آیا استفاده از یک سشن سراسری در برنامه صحیح است؟
پاسخ: خیر!
توضیحات: زمانیکه از یک سشن سراسری استفاده میکنید، کش NHibernate را در اختیار تمام کاربران همزمان سیستم قرار دادهاید. در طی یک سشن، همانطور که عنوان شد، بر اساس IDهای اشیاء، یک identity map تشکیل میشود و در این حالت به ازای هر رکورد بانک اطلاعاتی فقط و فقط یک شیء در حافظه وجود خواهد داشت که این روش در محیطهای چندکاربره مانند برنامههای وب به زودی تبدیل به نشت اطلاعات و یا تخریب اطلاعات میگردد. به همین جهت در این نوع برنامهها روش session-per-request بهترین حالت کاری است.
سؤال: حین به روز رسانی اشیاء جدید، به خطا بر میخورم. مشکل در کجاست؟
فرض کنید شیء مفروض Customer را توسط متد session.Get از بانک اطلاعاتی دریافت و تعدادی از خواص آنرا جهت ساخت شیء جدیدی از کلاس Customer استفاده کردهایم. اکنون اگر بخواهیم این شیء جدید را در بانک اطلاعاتی ذخیره یا به روز رسانی کنیم، NHibernate این اجازه را نمیدهد! چرا؟
پاسخ:
خطای متداول این حالت عموما به صورت زیر است:
a different object with the same identifier value was already associated with the session
همانطور که عنوان شد، در طول یک سشن، نمیتوان دو شیء با یک ID را به عنوان یک رکورد بانک اطلاعاتی مورد استفاده قرار داد. اولین فراخوانی Get ، سبب کش شدن آن شیء در identity map سطح اول caching میگردد.
راه حل:
الف) از چندین و چند شیء استفاده نکنید. هر رکورد باید تنها با یک وهله از شیءایی متناظر باشد.
ب) میتوان پیش از update، کش سطح اول را به صورت دستی خالی کرد. برای این منظور از متد Clear شیء سشن استفاده کنید.
ج) بجای استفاده از متد saveOrUpdate شیء سشن، از متد Merge آن استفاده کنید. به این صورت شیء جدید ایجاد شده با شیء موجود در کش یکی خواهد شد.
د) میتوان بجای تخلیه کل کش (حالت ب)، کش مرتبط با شیء Customer را به صورت دستی خالی کرد. برای این منظور از متد Evict شیء سشن استفاده نمائید.
و لازم به ذکر است که متد Flush سبب تخلیه کش نمیگردد. کار این متد اعمال کلیه تغییرات اعمالی موجود در کش به بانک اطلاعاتی است و بیشتر جهت هماهنگ سازی این دو مورد استفاده قرار میگیرد.
سؤال: آیا میتوان سطح اول caching را غیرفعال کرد؟
پاسخ:بله.
توضیحات:
عموما کلیه ORMs جهت Batching یا Bulk data operations (برای مثال ثبت تعداد زیادی رکورد یا به روز رسانی تعداد بالایی از آنها، یا نمایش فقط خواندنی تعداد زیادی رکورد و گزارشگیری از آنها) کارآیی مطلوبی ندارند. نمونهای از آنرا در مبحث جاری ملاحظه کردهاید. هر شیءایی که به نحوی به سشن جاری وارد میشود تحت نظر قرار میگیرد و این مورد در تعداد بالای ثبت یا به روز رسانی رکوردها، یعنی کاهش سرعت و کارآیی، به علاوه مصرف بالای حافظه. به همین جهت باید به خاطر داشت که ORMs جهت سناریوهای OLTP مناسب هستند و کسانی که سرعت و کارآیی ORMs را با Batch processing اندازه گیری میکنند، کلا درکی از فلسفهی وجودی ORMs و ساختار درونی آنها ندارند!
خوشبختانه NHibernate با معرفی Stateless Sessions بر این مشکل فائق آمده است. در اینجا بجای ISession تنها کافی است از IStatelessSession استفاده گردد:
using (IStatelessSession statelessSession = sessionFactory.OpenStatelessSession())
using (ITransaction transaction = statelessSession.BeginTransaction())
{
//now insert 1,000,000 records!
}
تنها باید به خاطر داشت که در این حالت lazy loading پشتیبانی نمیشود و همچنین رخدادهای درونی NHibernate نیز لغو خواهند شد.
netsh http add sslcert ipport=0.0.0.0:6001 certhash=<thumbprint of certificate> appid={new guid here} certstorename=Root
SSL Certificate add failed, Error: 1312 A specified logon session does not exist. It may already have been terminated.
در واقع Sequence روشی برای تولید اعداد ترتیبی با قابلیت افزایش یا کاهش عددهای دلخواه میباشد که توسط کاربر یا برنامه نویس ایجاد میشود. بنابراین Sequenceها User-Defined میباشند.
در اینجا ممکن است سئوالی پیش بیاید که اینکار توسط Identity هم قابل انجام است، اما چرا استفاده از Sequence توسط مایکروسافت پیشنهاد میشود.
بدلایل زیر استفاده از Sequence بهتر میباشد:
- ممکن است Application شما قبل از درج رکورد، درون یک جدول نیاز به عدد منحصربفردی داشته باشد.
- عدد تولید شده بوسیله Sequence را میتوانید بین جداول یا ستونهای مختلف یک جدول به اشتراک بگذارید.
- میتوانید روند تولید اعداد ترتیبی را Restart نمایید. به عبارت دیگر قابلیت Restart نمودن Sequence وجود دارد.
- میتوانید Sequence خود را براساس Sort یک یا چند فیلد، تنظیم نمایید.
Syntax آن به شرح ذیل میباشد:
CREATE SEQUENCE [schema_name . ] sequence_name [ AS [ built_in_integer_type | user-defined_integer_type ] ] [ START WITH <constant> ] [ INCREMENT BY <constant> ] [ { MINVALUE [ <constant> ] } | { NO MINVALUE } ] [ { MAXVALUE [ <constant> ] } | { NO MAXVALUE } ] [ CYCLE | { NO CYCLE } ] [ { CACHE [ <constant> ] } | { NO CACHE } ] [ ; ]
شرح Syntax :
- در زمان ایجاد Sequence، نوع آن میبایست عددی باشد، چنانچه ،Type آن را مشخص ننمایید، SQL Server، نوع آن را bigint در نظر میگیرد.
- Start With: بدین مفهوم میباشد، که Sequence ایجاد شده از چه عددی آغاز شود.
- INCREMENT BY: مفهومش این است که Sequence به چه مقداری افزایش یا کاهش یابد. به عبارت دیگری عدد تولید شده براساس مقدار Increment by تولید میشود.
- Minvalue: کمترین مقداری که Sequence میتواند ایجاد نماید.
- Maxvalue :بیشترین مقداری که Sequence میتواند ایجاد نماید.
- Cycle :مقداری را که برای Cycle تعیین مینماییم، بدین مفهوم است که Sequence پس از چه عددی میبایست Restart شود.
- Cache :عددی که برای Cache در نظر میگیریم، مفهومش این است که چه تعداد از اعداد تولید شده توسط Sequence، قبل از استفاده، میتواند در Cache قرار گیرد.
در ادامه با یک مثال ساده، یک Sequence ایجاد مینماییم:
CREATE SEQUENCE [dbo].[SequenceTest] AS [int] START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 30 CYCLE CACHE GO
برای اینکه بتوانیم مقدار Sequence را بدست آوریم، کافیست از Syntax زیر استفاده نمایید:
NEXT VALUE FOR [ database_name . ] [ schema_name . ] sequence_name [ OVER (<over_order_by_clause>) ]
اگر Select بالا را تا 30 بار انجام دهید، برای دفعه 31 مقدار آن یک میشود، چون در زمان تعریف Cycle ،Sequence را انتخاب کرده بودیم. در غیر اینصورت برای دفعه 31 با خطا زیر مواجه میشوید.
Msg 11728, Level 16, State 1, Line 1 The sequence object 'SequenceTest' has reached its minimum or maximum value. Restart the sequence object to allow new values to be generated.
در ابتدا مطابق Script زیر جدولی را ایجاد و مقادیری را درون آن درج مینماییم:
create table Kids ( ID int, Name varchar(50) ); Go insert Kids values (1,'Emma') , (1,'Tabitha') , (2,'Kendall') , (3,'Delaney') , (4,'Kyle') , (5,'Jessica') , (6,'Josh') , (7,'Kirsten') , (8,'Amanda') , (9,'Jimmy') ;
CREATE SCHEMA Samples ; GO
CREATE SEQUENCE Samples.Test AS tinyint START WITH 1 INCREMENT BY 1 ; GO
SELECT NEXT VALUE FOR Samples.Test OVER (ORDER BY Name) AS NutID, ID, Name FROM test1.Kids WHERE Name LIKE '%e%' ;
امیدوارم مطلب فوق مفید واقع شده باشد.
Professional REST API design with ASP.NET Core and WebAPI
This project is an example of lightweight and extensible infrastructure for building RESTful Web API with ASP.NET Core.
This example contains a number of tricks and techniques which I've learned while building APIs in ASP.NET Core.
Techniques and Features
- JWT Authentication
- Secure JWT using Encryption (JWE)
- Logging to File, Console and Database using Elmah & NLog
- Logging to sentry.io (Log Management System)
- Exception Handling using Custom Middleware
- Automatic Validation
- Standard API Resulting
- Dependency Injection using Autofac
- Map resources using AutoMapper
- Async/Await Best Practices
- Versioning Management
- Using Swagger (Swashbuckle)
- Auto Document Generator for Swagger
- Integrate Swagger and Versioning
- Integrate Swagger and JWT/OAuth Authentication
- Best Practices for Performance and Security
چگونه با استفاده از لوسین مطالب را ایندکس کنیم؟
چگونه از افزونه jQuery Auto-Complete استفاده کنیم؟
نحوه استفاده صحیح از لوسین در ASP.NET
اگر به جستجوی سایت دقت کرده باشید، قابلیت ارائه پیشنهاداتی به کاربر توسط یک Auto-Complete به آن اضافه شدهاست. در مطلب جاری به بررسی این مورد به همراه دو مثال Web forms و MVC پرداخته خواهد شد.
قسمت عمده مطلب جاری با پیشنیازهای یاد شده فوق یکی است. در اینجا فقط به ذکر تفاوتها بسنده خواهد شد.
الف) دریافت لوسین
از طریق NuGet آخرین نگارش را دریافت و به پروژه خود اضافه کنید. همچنین Lucene.NET Contrib را نیز به همین نحو دریافت نمائید.
ب) ایجاد ایندکس
کدهای این قسمت با مطلب برجسته سازی قسمتهای جستجو شده، یکی است:
using System.Collections.Generic; using System.IO; using Lucene.Net.Analysis.Standard; using Lucene.Net.Documents; using Lucene.Net.Index; using Lucene.Net.Store; using LuceneSearch.Core.Model; using LuceneSearch.Core.Utils; namespace LuceneSearch.Core { public static class CreateIndex { static readonly Lucene.Net.Util.Version _version = Lucene.Net.Util.Version.LUCENE_30; public static Document MapPostToDocument(Post post) { var postDocument = new Document(); postDocument.Add(new Field("Id", post.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); var titleField = new Field("Title", post.Title, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS); titleField.Boost = 3; postDocument.Add(titleField); postDocument.Add(new Field("Body", post.Body.RemoveHtmlTags(), Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); return postDocument; } public static void CreateFullTextIndex(IEnumerable<Post> dataList, string path) { var directory = FSDirectory.Open(new DirectoryInfo(path)); var analyzer = new StandardAnalyzer(_version); using (var writer = new IndexWriter(directory, analyzer, create: true, mfl: IndexWriter.MaxFieldLength.UNLIMITED)) { foreach (var post in dataList) { writer.AddDocument(MapPostToDocument(post)); } writer.Optimize(); writer.Commit(); writer.Close(); directory.Close(); } } } }
ج) تهیه قسمت منبع داده Auto-Complete
namespace LuceneSearch.Core.Model { public class SearchResult { public int Id { set; get; } public string Title { set; get; } } }
using System.Collections.Generic; using System.IO; using Lucene.Net.Index; using Lucene.Net.Search; using Lucene.Net.Store; using LuceneSearch.Core.Model; using LuceneSearch.Core.Utils; namespace LuceneSearch.Core { public static class AutoComplete { private static IndexSearcher _searcher; /// <summary> /// Get terms starting with the given prefix /// </summary> /// <param name="prefix"></param> /// <param name="maxItems"></param> /// <returns></returns> public static IList<SearchResult> GetTermsScored(string indexPath, string prefix, int maxItems = 10) { if (_searcher == null) _searcher = new IndexSearcher(FSDirectory.Open(new DirectoryInfo(indexPath)), true); var resultsList = new List<SearchResult>(); if (string.IsNullOrWhiteSpace(prefix)) return resultsList; prefix = prefix.ApplyCorrectYeKe(); var results = _searcher.Search(new PrefixQuery(new Term("Title", prefix)), null, maxItems); if (results.TotalHits == 0) { results = _searcher.Search(new PrefixQuery(new Term("Body", prefix)), null, maxItems); } foreach (var doc in results.ScoreDocs) { resultsList.Add(new SearchResult { Title = _searcher.Doc(doc.Doc).Get("Title"), Id = int.Parse(_searcher.Doc(doc.Doc).Get("Id")) }); } return resultsList; } } }
برای نمایش Auto-Complete نیاز به منبع داده داریم که نحوه ایجاد آنرا در کدهای فوق ملاحظه میکنید. در اینجا توسط جستجوی سریع لوسین و امکانات PrefixQuery آن، به تعدادی مشخص (maxItems)، رکوردهای یافت شده را بازگشت خواهیم داد. خروجی حاصل لیستی است از SearchResultها شامل عنوان مطلب و Id آن. عنوان را به کاربر نمایش خواهیم داد؛ از Id برای هدایت او به مطلبی مشخص استفاده خواهیم کرد.
د) نمایش Auto-Complete در ASP.NET MVC
using System.Text; using System.Web.Mvc; using LuceneSearch.Core; using System.Web; namespace LuceneSearch.Controllers { public class HomeController : Controller { static string _indexPath = HttpRuntime.AppDomainAppPath + @"App_Data\idx"; public ActionResult Index(int? id) { if (id.HasValue) { //todo: do something } return View(); //Show the page } public virtual ActionResult ScoredTerms(string q) { if (string.IsNullOrWhiteSpace(q)) return Content(string.Empty); var result = new StringBuilder(); var items = AutoComplete.GetTermsScored(_indexPath, q); foreach (var item in items) { var postUrl = this.Url.Action(actionName: "Index", controllerName: "Home", routeValues: new { id = item.Id }, protocol: "http"); result.AppendLine(item.Title + "|" + postUrl); } return Content(result.ToString()); } } }
@{ ViewBag.Title = "جستجو"; var scoredTermsUrl = Url.Action(actionName: "ScoredTerms", controllerName: "Home"); var bulletImage = Url.Content("~/Content/Images/bullet_shape.png"); } <h2> جستجو</h2> <div align="center"> @Html.TextBox("term", "", htmlAttributes: new { dir = "ltr" }) <br /> جهت آزمایش lu را وارد نمائید </div> @section scripts { <script type="text/javascript"> EnableSearchAutocomplete('@scoredTermsUrl', '@bulletImage'); </script> }
function EnableSearchAutocomplete(url, img) { var formatItem = function (row) { if (!row) return ""; return "<img src='" + img + "' /> " + row[0]; } $(document).ready(function () { $("#term").autocomplete(url, { dir: 'rtl', minChars: 2, delay: 5, mustMatch: false, max: 20, autoFill: false, matchContains: false, scroll: false, width: 300, formatItem: formatItem }).result(function (evt, row, formatted) { if (!row) return; window.location = row[1]; }); }); }
- ابتدا ارجاعاتی را به jQuery، افزونه Auto-Complete و اسکریپت سفارشی تهیه شده، در فایل layout پروژه تعریف خواهیم کرد.
در اینجا سه قسمت را مشاهده میکنید: کدهای کنترلر، View متناظر و اسکریپتی که Auto-Complete را فعال خواهد ساخت.
- قسمت مهم کدهای کنترلر، دو سطر زیر هستند:
result.AppendLine(item.Title + "|" + postUrl); return Content(result.ToString());
return Content هم سبب بازگشت این اطلاعات به افزونه خواهد شد.
- کدهای View متناظر بسیار ساده هستند. تنها نام TextBox تعریف شده مهم میباشد که در متد جاوا اسکریپتی EnableSearchAutocomplete استفاده شده است. به علاوه، نحوه مقدار دهی آدرس دسترسی به اکشن متد ScoredTerms نیز مهم میباشد.
- در متد EnableSearchAutocomplete نحوه فراخوانی افزونه autocomplete را ملاحظه میکنید.
جهت آن، به راست به چپ تنظیم شده است. با 2 کاراکتر ورودی فعال خواهد شد با وقفهای کوتاه. نیازی نیست تا انتخاب کاربر از لیست ظاهر شده حتما با عبارت جستجو شده صد در صد یکی باشد. حداکثر 20 آیتم در لیست ظاهر خواهند شد. اسکرول بار لیست را حذف کردهایم. عرض آن به 300 تنظیم شده است و نحوه فرمت دهی نمایشی آنرا نیز ملاحظه میکنید. برای این منظور از متد formatItem استفاده شده است. آرایه row در اینجا در برگیرنده اعضای Title و Id ارسالی به افزونه است. اندیس صفر آن به عنوان دریافتی اشاره میکند.
همچنین نحوه نشان دادن عکس العمل به عنصر انتخابی را هم ملاحظه میکنید (در متد result مقدار دهی شده). window.location را به عنصر دوم آرایه row هدایت خواهیم کرد. این عنصر دوم مطابق کدهای اکشن متد تهیه شده، به آدرس یک صفحه اشاره میکند.
ه) نمایش Auto-Complete در ASP.NET WebForms
قسمت عمده مطالب فوق با وب فرمها نیز یکی است. خصوصا توضیحات مرتبط با متد EnableSearchAutocomplete ذکر شده.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="LuceneSearch.WebForms.Default" %> <!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 runat="server"> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>جستجو</title> <link href="Content/Site.css" rel="stylesheet" type="text/css" /> <script src="Scripts/jquery-1.7.1.min.js" type="text/javascript"></script> <script src="Scripts/jquery.autocomplete.js" type="text/javascript"></script> <script src="Scripts/custom.js" type="text/javascript"></script> </head> <body dir="rtl"> <h2> جستجو</h2> <form id="form1" runat="server"> <div align="center"> <asp:TextBox runat="server" dir="ltr" ID="term"></asp:TextBox> <br /> جهت آزمایش lu را وارد نمائید </div> </form> <script type="text/javascript"> EnableSearchAutocomplete('Search.ashx', 'Content/Images/bullet_shape.png'); </script> </body> </html>
using System.Text; using System.Web; using LuceneSearch.Core; namespace LuceneSearch.WebForms { public class Search : IHttpHandler { static string _indexPath = HttpRuntime.AppDomainAppPath + @"App_Data\idx"; public void ProcessRequest(HttpContext context) { string q = context.Request.QueryString["q"]; if (string.IsNullOrWhiteSpace(q)) { context.Response.Write(string.Empty); context.Response.End(); } var result = new StringBuilder(); var items = AutoComplete.GetTermsScored(_indexPath, q); foreach (var item in items) { var postUrl = "Default.aspx?id=" + item.Id; result.AppendLine(item.Title + "|" + postUrl); } context.Response.ContentType = "text/plain"; context.Response.Write(result.ToString()); context.Response.End(); } public bool IsReusable { get { return false; } } } }
در اینجا بجای Controller از یک Generic handler استفاده شده است (Search.ashx).
result.AppendLine(item.Title + "|" + postUrl); context.Response.Write(result.ToString());
کدهای کامل مثال فوق را از اینجا میتوانید دریافت کنید:
همچنین باید دقت داشت که پروژه MVC آن از نوع MVC4 است (VS2010) و فرض براین میباشد که IIS Express 7.5 را نیز پیشتر نصب کردهاید.
کلمه عبور فایل: dotnettips91
ASP.NET Web API فریم ورکی برای ساختن APIهای وب بر روی فریم ورک دات نت است. در این مقاله با استفاده از این فریم ورک، API وبی خواهیم ساخت که لیستی از محصولات را بر میگرداند. صفحه وب کلاینت، با استفاده از jQuery نتایج را نمایش خواهد داد.
یک پروژه Web API بسازید
در ویژوال استودیو 2013 پروژه جدیدی از نوع ASP.NET Web Application بسازید و نام آن را "ProductsApp" انتخاب کنید.
در دیالوگ New ASP.NET Project قالب Empty را انتخاب کنید و در قسمت "Add folders and core references for" گزینه Web API را انتخاب نمایید.
می توانید از قالب Web API هم استفاده کنید. این قالب با استفاده از ASP.NET MVC صفحات راهنمای API را خواهد ساخت. در این مقاله از قالب Empty استفاده میکنیم تا تمرکز اصلی، روی خود فریم ورک Web API باشد. بطور کلی برای استفاده از این فریم ورک لازم نیست با ASP.NET MVC آشنایی داشته باشید.
افزودن یک مدل
یک مدل (model) آبجکتی است که داده اپلیکیشن شما را معرفی میکند. ASP.NET Web API میتواند بصورت خودکار مدل شما را به JSON, XML و برخی فرمتهای دیگر مرتب (serialize) کند، و سپس داده مرتب شده را در بدنه پیام HTTP Response بنویسد. تا وقتی که یک کلاینت بتواند فرمت مرتب سازی دادهها را بخواند، میتواند آبجکت شما را deserialize کند. اکثر کلاینتها میتوانند XML یا JSON را تفسیر کنند. بعلاوه کلاینتها میتوانند فرمت مورد نظرشان را با تنظیم Accept header در پیام HTTP Request مشخص کنند.
بگذارید تا با ساختن مدلی ساده که یک محصول (product) را معرفی میکند شروع کنیم.
کلاس جدیدی در پوشه Models ایجاد کنید.
نام کلاس را به "Product" تغییر دهید، و خواص زیر را به آن اضافه کنید.
namespace ProductsApp.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; } } }
افزودن یک کنترلر
در Web API کنترلرها آبجکت هایی هستند که درخواستهای HTTP را مدیریت کرده و آنها را به اکشن متدها نگاشت میکنند. ما کنترلری خواهیم ساخت که میتواند لیستی از محصولات، یا محصولی بخصوص را بر اساس شناسه برگرداند. اگر از ASP.NET MVC استفاده کرده اید، با کنترلرها آشنا هستید. کنترلرهای Web API مشابه کنترلرهای MVC هستند، با این تفاوت که بجای ارث بری از کلاس Controller از کلاس ApiController مشتق میشوند.
کنترلر جدیدی در پوشه Controllers ایجاد کنید.
در دیالوگ Add Scaffold گزینه Web API Controller - Empty را انتخاب کرده و روی Add کلیک کنید.
در دیالوگ Add Controller نام کنترلر را به "ProductsController" تغییر دهید و روی Add کلیک کنید.
توجه کنید که ملزم به ساختن کنترلرهای خود در پوشه Controllers نیستید، و این روش صرفا قراردادی برای مرتب نگاه داشتن ساختار پروژهها است. کنترلر ساخته شده را باز کنید و کد زیر را به آن اضافه نمایید.
using ProductsApp.Models; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Web.Http; namespace ProductsApp.Controllers { public class ProductsController : ApiController { Product[] products = new Product[] { new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 }, new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M }, new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M } }; public IEnumerable<Product> GetAllProducts() { return products; } public IHttpActionResult GetProduct(int id) { var product = products.FirstOrDefault((p) => p.Id == id); if (product == null) { return NotFound(); } return Ok(product); } }
کنترلر ما دو متد برای دریافت محصولات تعریف میکند:
- متد GetAllProducts لیست تمام محصولات را در قالب یک <IEnumerable<Product بر میگرداند.
- متد GetProductById سعی میکند محصولی را بر اساس شناسه تعیین شده پیدا کند.
همین! حالا یک Web API ساده دارید. هر یک از متدهای این کنترلر، به یک یا چند URI پاسخ میدهند:
URI | Controller Method |
api/products/ | GetAllProducts |
api/products/id/ | GetProductById |
برای اطلاعات بیشتر درباره نحوه نگاشت درخواستهای HTTP به اکشن متدها توسط Web API به این لینک مراجعه کنید.
فراخوانی Web API با جاوا اسکریپت و jQuery
در این قسمت یک صفحه HTML خواهیم ساخت که با استفاده از AJAX متدهای Web API را فراخوانی میکند. برای ارسال درخواستهای آژاکسی و بروز رسانی صفحه بمنظور نمایش نتایج دریافتی از jQuery استفاده میکنیم.
در پنجره Solution Explorer روی نام پروژه کلیک راست کرده و گزینه Add, New Item را انتخاب کنید.
در دیالوگ Add New Item قالب HTML Page را انتخاب کنید و نام فایل را به "index.html" تغییر دهید.
حال محتوای این فایل را با لیست زیر جایگزین کنید.
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Product App</title> </head> <body> <div> <h2>All Products</h2> <ul id="products" /> </div> <div> <h2>Search by ID</h2> <input type="text" id="prodId" size="5" /> <input type="button" value="Search" onclick="find();" /> <p id="product" /> </div> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script> <script> var uri = 'api/products'; $(document).ready(function () { // Send an AJAX request $.getJSON(uri) .done(function (data) { // On success, 'data' contains a list of products. $.each(data, function (key, item) { // Add a list item for the product. $('<li>', { text: formatItem(item) }).appendTo($('#products')); }); }); }); function formatItem(item) { return item.Name + ': $' + item.Price; } function find() { var id = $('#prodId').val(); $.getJSON(uri + '/' + id) .done(function (data) { $('#product').text(formatItem(data)); }) .fail(function (jqXHR, textStatus, err) { $('#product').text('Error: ' + err); }); } </script> </body> </html>
گرفتن لیستی از محصولات
برای گرفتن لیستی از محصولات، یک درخواست HTTP GET به آدرس "api/products/" ارسال کنید.
تابع getJSON یک درخواست آژاکسی ارسال میکند. پاسخ دریافتی هم آرایه ای از آبجکتهای JSON خواهد بود. تابع done در صورت موفقیت آمیز بودن درخواست، اجرا میشود. که در این صورت ما DOM را با اطلاعات محصولات بروز رسانی میکنیم.
$(document).ready(function () { // Send an AJAX request $.getJSON(apiUrl) .done(function (data) { // On success, 'data' contains a list of products. $.each(data, function (key, item) { // Add a list item for the product. $('<li>', { text: formatItem(item) }).appendTo($('#products')); }); }); });
گرفتن محصولی مشخص
برای گرفتن یک محصول توسط شناسه (ID) آن کافی است یک درخواست HTTP GET به آدرس "api/products/id/" ارسال کنید.
function find() { var id = $('#prodId').val(); $.getJSON(apiUrl + '/' + id) .done(function (data) { $('#product').text(formatItem(data)); }) .fail(function (jqXHR, textStatus, err) { $('#product').text('Error: ' + err); }); }
اجرای اپلیکیشن
اپلیکیشن را با F5 اجرا کنید. صفحه وب باز شده باید چیزی مشابه تصویر زیر باشد.
برای گرفتن محصولی مشخص، شناسه آن را وارد کنید و روی Search کلیک کنید.
اگر شناسه نامعتبری وارد کنید، سرور یک خطای HTTP بر میگرداند.
استفاده از F12 برای مشاهده درخواستها و پاسخ ها
هنگام کار با سرویسهای HTTP، مشاهدهی درخواستهای ارسال شده و پاسخهای دریافتی بسیار مفید است. برای اینکار میتوانید از ابزار توسعه دهندگان وب استفاده کنید، که اکثر مرورگرهای مدرن، پیاده سازی خودشان را دارند. در اینترنت اکسپلورر میتوانید با F12 به این ابزار دسترسی پیدا کنید. به برگه Network بروید و روی Start Capturing کلیک کنید. حالا صفحه وب را مجددا بارگذاری (reload) کنید. در این مرحله اینترنت اکسپلورر ترافیک HTTP بین مرورگر و سرور را تسخیر میکند. میتوانید تمام ترافیک HTTP روی صفحه جاری را مشاهده کنید.
به دنبال آدرس نسبی "api/products/" بگردید و آن را انتخاب کنید. سپس روی Go to detailed view کلیک کنید تا جزئیات ترافیک را مشاهده کنید. در نمای جزئیات، میتوانید headerها و بدنه درخواستها و پاسخها را ببینید. مثلا اگر روی برگه Request headers کلیک کنید، خواهید دید که اپلیکیشن ما در Accept header دادهها را با فرمت "application/json" درخواست کرده است.
اگر روی برگه Response body کلیک کنید، میتوانید ببینید چگونه لیست محصولات با فرمت JSON سریال شده است. همانطور که گفته شده مرورگرهای دیگر هم قابلیتهای مشابهی دارند. یک ابزار مفید دیگر Fiddler است. با استفاده از این ابزار میتوانید تمام ترافیک HTTP خود را مانیتور کرده، و همچنین درخواستهای جدیدی بسازید که این امر کنترل کاملی روی HTTP headers به شما میدهد.
قدمهای بعدی
تایپوگرافی
هدف از تایپوگرافی، چیدمان متن به نحوی است که واضح، خوانا و مشخص باشد؛ همچنین مباحث زیبایی ارائه را نیز به آن اضافه کنید. برای مثال تنظیم فاصله بین حروف و کلمات، فاصله بین خطوط و یا رعایت یک سری نسبتهای ویژه مانند نسبت طلایی جهت دعوت خواننده به مطالعه مطالب، بجای فراری دادن او، در مباحث تایپوگرافی رعایت میشوند. خوشبختانه Twitter Bootstrap به همراه یک سری تنظیمات تایپوگرافی پیش فرض است که در ادامه آنها را مرور خواهیم کرد.
پیش فرضهای ابتدایی آنرا مانند قلم با اندازه 14px، قلم پیش فرض Helvetica، فاصله بین خطوط و رنگ متن را در فایل bootstrap.css میتوانید مشاهده کنید:
body { margin: 0;"Helvetica Neue", Helvetica, Arial, sans-serif; color: #333333; background-color: #ffffff; }
<div class="row-fluid"> <div class="span12"> <h1> سرتیتر 1 </h1> <h2> سرتیتر 2 </h2> <h3> سرتیتر 3 </h3> <h4> سرتیتر 4 </h4> <h5> سرتیتر 5 </h5> <h6> سرتیتر 6 </h6> </div> </div> |
<div class="row-fluid"> <div class="span12"> <p class="lead"> تیتر</p> <p> متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن </p> <small>اندازه کوچک</small> <strong>متن ضخیم</strong> <em>متن ایتالیک</em> </div> </div>
روش دیگر جلب توجه به یک متن در bootstrap، استفاده از کلاسهای text مانند text-error و امثال آن میباشد:
<div class="row-fluid"> <div class="span12"> <p class="muted"> متن غیرفعال</p> <p class="text-warning"> نمایش اخطار به کاربر</p> <p class="text-error"> نمایش خطا به کاربر</p> <p class="text-info"> نمایش اطلاعات به کاربر</p> <p class="text-success"> نمایش موفقیت آمیز بودن عملیات</p> </div> </div> |
<div class="row-fluid"> <div class="span12"> <p> My <abbr title="منظور واحد پردازش مرکزی است"> CPU</abbr> has N Cores. </p> </div> </div> |
<div class="row-fluid"> <div class="span12"> <address> وحید نصیری <br /> ایران، تهران <br /> <abbr title="Phone"> P:</abbr> 12345678 <br /> <abbr title="Cell"> C:</abbr> 12345678 </address> </div> </div> |
<div class="row-fluid"> <div class="span12"> <blockquote> <p> جهت نمایش نقل قول </p> <small><cite title="کاربر شماره 2">از شخصی</cite> </small> </blockquote> </div> </div> |
<div class="row-fluid"> <div class="span6"> <ul class="unstyled"> <li>یک </li> <li>دو </li> <li>سه </li> </ul> </div> <div class="span6"> <ol> <li>یک </li> <li>دو </li> <li>سه </li> </ol> </div> </div> |
<div class="row-fluid"> <div class="span12"> <dl class="dl-horizontal"> <dt>عنوان</dt> <dd> توضیحات بلند در اینجا</dd> </dl> </div> </div> |
<div class="row-fluid"> <div class="span6"> <code>inline code</code> </div> <div class="span6"> <pre class="pre-scrollable"> code code code </pre> </div> </div> |
استفاده از جداول و تاثیر bootstrap بر آنها
در ادامه کدهای یک جدول متداول را که مزین شدهاست به کلاسهای bootstrap ملاحظه میکنید:
<div class="container-fluid"> <div class="row-fluid"> <div class="span12"> <table class="table table-striped table-hover table-bordered table-condensed"> <caption> عنوانی خاص در اینجا</caption> <thead> <tr> <th> ستون یک </th> <th> ستون دو </th> <th> ستون سه </th> </tr> </thead> <tbody> <tr class="error"> <td> 1 متن یک </td> <td> 1 متن دو </td> <td> 1 متن سه </td> </tr> <tr> <td> 2 متن یک </td> <td> 2 متن دو </td> <td> 2 متن سه </td> </tr> <tr> <td> 3 متن یک </td> <td> 3 متن دو </td> <td> 3 متن سه </td> </tr> </tbody> </table> </div> </div> </div>
در اینجا ذکر caption اختیاری است. وجود thead و tbody به تشخیص هدر و ردیفها جهت اعمال شیوهنامههای متناظر کمک میکنند. همچنین کلاسهای ذیل نیز به جدول اعمال شدهاند:
table: سبب میشود تا تنظیمات ابتدایی bootstrap به جدول طراحی شده، اعمال شوند.
table-striped: رنگ زمینه سطرها را یک در میان تغییر میدهد.
table-hover: سبب میشود تا با عبور اشارهگر ماوس از روی سطرها، رنگ زمینه آنها تغییر کنند.
table-bordered: حاشیهای را به جدول و ردیفها اعمال میکنند. همچنین سبب نمایش گوشههای گرد نیز میشود.
table-condensed: اندکی padding اعمال شده به سلولهای جداول را کاهش میدهد و جدول را فشردهتر میکند.
در جدول فوق، کلاس نمونه error به یک tr نیز اعمال شدهاست تا اثر آنرا بر روی یک ردیف بهتر بتوان ملاحظه کرد.
طراحی فرمها و تاثیر bootstrap بر آنها
قرار دادن برچسبها و عناصر صفحه، به نحوی کاربرپسند و دلپذیر، نیاز به رعایت یک سری اصول تایپوگرافی و طراحی ویژه دارد که این موارد نیز در bootstrap گنجانده شدهاند.
1) فرمهای عمودی
<div class="container-fluid"> <div class="row-fluid"> <div class="span12"> <form action="/signup" method="post"> <fieldset> <legend>ثبت نام</legend> <label> ایمیل:</label> <input name="email" type="text" /> <label> نام:</label> <input name="name" type="text" /> </fieldset> </form> </div> </div> </div> |
همانطور که ملاحظه میکنید، کلیه عناصر را به صورت یک پشته عمودی در صفحه قرار دادهاست و نکته مهم اینجا است که هیچگونه شیوه نامه خاص و اضافهتری به فرم فوق از طرف ما اعمال نشده است.
نکته: اگر میخواهید همان حالت پیش فرض مرورگر، یعنی قرار دادن تمام عناصر در پشت سر هم را فعال کنید، تنها کافی است کلاس form-inline را به form تعریف شده فوق اضافه نمائید.
2) نکاتی در مورد checkboxes و radio buttons
در حالت پیش فرض، با تعریف یک label و checkbox، برچسب متناظر با آن اندکی بالاتر از checkbox قرار گرفته شده در صفحه ظاهر میشود. برای رفع این مشکل تنها کافی است کلاس checkbox به label اعمال شود (و برای radio button از کلاس radio استفاده خواهد شد):
<label class="checkbox"> <input type="checkbox" name="isMale" /> مذکر</label>
نکته: برای قرار دادن چندین checkbox یا radio button در یک سطر (با توجه به حالت چیدمان عمودی پیش فرض فرمها)، ابتدا آنها را داخل یک div قرار دهید. سپس به تمام checkboxها یا radio buttonها کلاس inline را نیز اضافه نمائید. برای مثال:
<div> <label class="radio inline"> <input type="radio" name="isMale" /> مذکر</label> <label class="radio inline"> <input type="radio" name="isFemale" /> مؤنث</label> </div> |
4) تعیین اندازه فیلدها
اگر علاقمند هستید که یک textarea کل عرض صفحه را به خود اختصاص دهد، از کلاس input-block-level استفاده کنید.
5) فرمهای جستجو
<form class="search-form"> <fieldset> <legend>جستجو</legend> <input type="search" class="search-query" /> <button type="submit" class="btn" >بیاب</button> </fieldset> </form> |
- کلاس فرم بهتر است search-form تعیین شود
- نوع input بهتر است search وارد شود
- کلاس input جستجو نیز search-query انتخاب گردد
همانطور که ملاحظه میکنید، در این حالت گوشههای جعبه متنی جستجو، نسبت به حالتهای معمولی آنها گرد شده است. کلاس دکمه نیز btn درنظر گرفته شده است تا حالت ویژه دکمههای bootstrap را پیدا کند.
6) فرمهای افقی
تا اینجا، با فرمهای حالت پیش فرض یا فرمهایی که عناصر را به صورت پشتهای عمودی بر روی یکدیگر قرار میدهند، آشنا شدیم. حالت متداول دیگر طراحی فرمها، حالت افقی است. به این معنا که در هر سطر، یک برچسب و یک المان قرار گیرند، بجای اینکه ابتدا برچسب نمایش داده شود و در سطر بعدی، المان مرتبط با آن. Bootstrap برای طراحی بدون استفاده از جداول این نوع فرمها نیز تنظیمات خاصی را تدارک دیدهاست.
<form class="form-horizontal" action="/signup" method="post"> <fieldset> <legend>ثبت نام</legend> <div class="control-group"> <label class="control-label"> ایمیل:</label> <div class="controls"> <input name="email" type="text" /> </div> </div> <div class="control-group"> <label class="control-label"> نام:</label> <div class="controls"> <input name="name" type="text" /> </div> </div> <div class="control-group"> <div class="controls"> <label class="radio inline"> <input type="radio" name="isMale" /> مذکر</label> <label class="radio inline"> <input type="radio" name="isFemale" /> مؤنث</label> </div> </div> </fieldset> </form> |
- کلاس form-horizontal را به فرم جاری اضافه کنید.
- هر سطر مورد نظر را در div ایی با کلاس control-group محصور نمائید.
- به برچسبها، کلاس control-label را انتساب دهید.
- کنترلهای مدنظر را در div ایی با کلاس controls محصور کنید.
هر چند این روش نیاز به اندکی HTML نویسی دارد، اما بسیاری به این نوع فرمها بیشتر علاقمند هستند تا فرمهای عمودی ابتدای بحث.
7) جلب توجه کاربران به فیلدها برای نمایش خطاهای اعتبارسنجی
<div class="control-group error"> <label class="control-label"> ایمیل:</label> <div class="controls"> <input name="email" type="text" /> <span class="help-block">لطفا ایمیل را با فرمت صحیحی وارد نمائید</span> </div> </div> |
8) توسعه فیلدهای استاندارد
<div class="input-prepend input-append"> <span dir="ltr" class="add-on">.00</span> <input dir="ltr" type="text" /> <span class="add-on">ریال</span> </div> |
9) HTML Helpers مخصوص ASP.NET MVC برای کار با bootstrap
نکاتی را که در اینجا مطرح شدند، اگر علاقمند بودید که به شکلی strongly typed در ASP.NET MVC اعمال کنید، میتوان به پروژههایی مانند TwitterBootstrapMvc مراجعه کرد. تعداد این نوع پروژهها هم روز به روز بیشتر میشوند:
https://twitterbootstrapmvc.codeplex.com/
https://mvc4bootstaphelper.codeplex.com/
https://github.com/erichexter/twitter.bootstrap.mvc
http://bootstraphelpers.codeplex.com/
تنظیمات خاص دکمهها در حین استفاده از Twitter Bootstrap
در مثال ذیل، کلاسهای مرتبط با تزئین دکمهها را توسط bootstrap، ملاحظه میکنید:
<div class="container-fluid"> <div class="row-fluid"> <div class="span12"> <table class="table table-striped table-hover table-bordered table-condensed"> <thead> <tr> <th> دکمه </th> <th> لینک </th> <th> کلاس بکار گرفته شده </th> </tr> </thead> <tbody> <tr> <td> <button class="btn"> default</button> </td> <td> <a href="#" class="btn">default</a> </td> <td> <code>btn</code> </td> </tr> <tr> <td> <button class="btn btn-primary"> primary</button> </td> <td> <a href="#" class="btn btn-primary">primary</a> </td> <td> <code>btn btn-primary</code> </td> </tr> <tr> <td> <button class="btn btn-info"> info</button> </td> <td> <a href="#" class="btn btn-info">info</a> </td> <td> <code>btn btn-info</code> </td> </tr> <tr> <td> <button class="btn btn-success"> success</button> </td> <td> <a href="#" class="btn btn-success">success</a> </td> <td> <code>btn btn-success</code> </td> </tr> <tr> <td> <button class="btn btn-warning"> warning</button> </td> <td> <a href="#" class="btn btn-warning">warning</a> </td> <td> <code>btn btn-warning</code> </td> </tr> <tr> <td> <button class="btn btn-danger"> danger</button> </td> <td> <a href="#" class="btn btn-danger">danger</a> </td> <td> <code>btn btn-danger</code> </td> </tr> <tr> <td> <button class="btn btn-inverse"> inverse</button> </td> <td> <a href="#" class="btn btn-inverse">inverse</a> </td> <td> <code>btn btn-inverse</code> </td> </tr> <tr> <td> <button class="btn btn-link"> link</button> </td> <td> <a href="#" class="btn btn-link">link</a> </td> <td> <code>btn btn-link</code> </td> </tr> <tr> <td> <button class="btn btn-primary btn-large"> large</button> </td> <td> <a href="#" class="btn btn-primary btn-large">large</a> </td> <td> <code>btn btn-primary btn-large</code> </td> </tr> <tr> <td> <button class="btn btn-primary btn-small"> small</button> </td> <td> <a href="#" class="btn btn-primary btn-small">small</a> </td> <td> <code>btn btn-primary btn-small</code> </td> </tr> <tr> <td> <button class="btn btn-primary btn-mini"> mini</button> </td> <td> <a href="#" class="btn btn-primary btn-mini">mini</a> </td> <td> <code>btn btn-primary btn-mini</code> </td> </tr> <tr> <td> <button class="btn btn-primary btn-block"> block</button> </td> <td> <a href="#" class="btn btn-primary btn-block">block</a> </td> <td> <code>btn btn-primary btn-block</code> </td> </tr> <tr> <td> <button class="btn btn-primary disabled"> disabled</button> </td> <td> <a href="#" class="btn btn-primary disabled">disabled</a> </td> <td> <code>btn btn-primary disabled</code> </td> </tr> </tbody> </table> </div> </div> </div>
همانطور که ملاحظه کردید، الزامی ندارد که این کلاسها را حتما به دکمهها اعمال کرد. برای نمونه میتوان از یک span یا لینک نیز برای تعریف دکمهها بهره جست. برای یکپارچه سازی چنین دکمههایی (که در اصل دکمه نیستند) با ASP.NET MVC میتوان به مطلب تکمیلی «استفاده از دکمههای CSS توئیتر در ASP.NET MVC» مراجعه نمود.
یک نکته: اگر علاقمند هستید که تعدادی دکمه را به شکل یک toolbar نمایش دهید، آنها را در یک div محصور کرده و کلاس btn-group را به آن div اعمال نمائید.
کار با تصاویر و آیکونها در Twitter Bootstrap
کلاسهایی مانند img-rounded، img-circle، img-polaroid با اعمال به یک تصویر، سبب گردن شدن گوشههای آن، نمایش دایرهای و یا نمایش به همراه حاشیه یک تصویر خواهند شد.
Twitter Bootstrap به همراه صدها آیکون ارائه شده است. این آیکونها توسط glyphicons.com ایجاد شدهاند. روشی که برای استفاده از آنها توصیه شده است، استفاده از تگ i میباشد. برای مثال:
<i class="icon-music"></i>icon-music
برای مشاهده لیست کلاسهای قابل استفاده، کلمه icon-glass را در فایل bootstrap.css جستجو نمائید؛ تا شروع مدخل مرتبط با آیکونها را بتوانید مشاهده نمائید.
رنگ پیش فرض این آیکونها مشکی است. اگر علاقمند بودید که آنها را برای مثال با رنگ سفید نمایش دهید فقط کافی است کلاس icon-white را پس از کلاس آیکون مدنظر،ذکر کرد:
<i class="icon-music icon-white"></i>icon-music
امکان اعمال این آیکونها به دکمهها نیز وجود دارد. برای مثال:
<button class="btn"> <i class="icon-music"></i>دکمه</button>
<div class="input-prepend"> <span class="add-on"><i class="icon-envelope"></i></span> <input type="email" dir="ltr" /> </div> |