Skip to content

12. خدمات وب

12.1. Introduction

در فصل قبلی، ما چندین برنامهٔ کلاینت-سرور TCP/IP را معرفی کردیم. تا زمانی که کلاینت‌ها و سرور خطوط متنی را مبادله می‌کنند، می‌توانند به هر زبانی نوشته شوند. کلاینت تنها باید پروتکل ارتباطی مورد انتظار سرور را بشناسد.

خدمات وب نیز برنامه‌های سرور مبتنی بر TCP/IP هستند. آنها دارای ویژگی‌های زیر هستند:

  • آنها توسط وب‌سرورها میزبانی می‌شوند و پروتکل ارتباطی کلاینت-سرور HTTP (پروتکل حمل‌ونقل HyperText) است، پروتکلی که بر روی TCP-IP قرار دارد.
  • خدمات وب صرف‌نظر از سرویس ارائه‌شده، دارای یک پروتکل ارتباطی استاندارد هستند. یک سرویس وب خدمات مختلفی ارائه می‌دهد: S1، S2، …، Sn. هر یک از این سرویس‌ها انتظار دریافت پارامترهایی را از سمت کلاینت دارد و یک نتیجه را به کلاینت بازمی‌گرداند. برای هر سرویس، کلاینت باید بداند:
    • نام دقیق سرویس، اگر
    • فهرست پارامترهایی که باید ارائه شوند و نوع آن‌ها
    • نوع نتیجه‌ای که توسط سرویس بازگردانده می‌شود

پس از مشخص شدن این جزئیات، دیالوگ کلاینت-سرور صرف‌نظر از اینکه کدام سرویس وب در حال پرس‌وجو است، از همان قالب پیروی می‌کند. این امر نحوه نوشتن کلاینت‌ها را استاندارد می‌کند.

  • به دلایل امنیتی و برای محافظت در برابر حملات اینترنتی، بسیاری از سازمان‌ها شبکه‌های خصوصی دارند و تنها پورت‌های خاصی را روی سرورهای خود به اینترنت باز می‌کنند: عمدتاً پورت ۸۰ برای سرویس وب. سایر پورت‌ها مسدود هستند. در نتیجه، برنامه‌های کلاینت-سرور مانند نمونه‌های توصیف‌شده در فصل قبل، در شبکه خصوصی (اینترانت) ساخته می‌شوند و عموماً از خارج قابل دسترسی نیستند. میزبانی یک سرویس روی یک وب‌سرور، آن را برای کل جامعه اینترنتی قابل دسترسی می‌سازد.
  • سرویس وب را می‌توان به صورت یک شیء راه دور مدل‌سازی کرد. سرویس‌های ارائه‌شده سپس به متدهای این شیء تبدیل می‌شوند. یک کلاینت می‌تواند به این شیء راه دور دسترسی داشته باشد، گویی که محلی است. این کار کل لایه ارتباطی شبکه را پنهان می‌کند و به کلاینت اجازه می‌دهد تا به صورت مستقل از آن لایه ساخته شود. اگر این لایه تغییر کند، نیازی به اصلاح کلاینت نیست.
  • مانند برنامه‌های کلاینت-سرور TCP/IP که در فصل قبلی ارائه شدند، کلاینت و سرور می‌توانند به هر زبانی نوشته شوند. آنها خطوط متنی را با یکدیگر مبادله می‌کنند. این خطوط از دو بخش تشکیل شده‌اند:
    • سربرگ‌های مورد نیاز پروتکل HTTP
    • بدنه پیام. برای پاسخ از سرور به کلاینت، بدنه پیام در قالب XML (زبان نشانه‌گذاری eXtensible) است. برای درخواست از کلاینت به سرور، بدنه پیام می‌تواند چندین شکل داشته باشد، از جمله XML. درخواست XML کلاینت ممکن است دارای فرمت خاصی باشد که به نام SOAP (پروتکل دسترسی ساده به شیء) شناخته می‌شود. در این صورت، پاسخ سرور نیز از فرمت SOAP پیروی می‌کند.

معماری یک برنامهٔ کلاینت/سرور مبتنی بر سرویس وب به شرح زیر است:

این یک توسعه از معماری سه‌لایه است که کلاس‌های ارتباط شبکه تخصصی به آن اضافه شده‌اند. ما قبلاً با معماری مشابهی در بخش 11.9.1 در مورد برنامه مالیات‌دهی گرافیکی کلاینت/سرور TCP ویندوز مواجه شده‌ایم.

بیایید این اصول کلی را با یک مثال اولیه نشان دهیم.

12.2. یک سرویس وب اول با استفاده از Visual Web Developer

ما قصد داریم اولین برنامهٔ کلاینت/سرور را با معماری ساده‌شدهٔ زیر بسازیم:

12.2.1. سمت سرور

ما اشاره کردیم که یک سرویس وب توسط یک وب‌سرور میزبانی می‌شود. نوشتن یک سرویس وب در حوزه کلی برنامه‌نویسی سمت سرور وب قرار می‌گیرد. قبلاً فرصت نوشتن کلاینت‌های وب را داشتیم که آن نیز نوعی برنامه‌نویسی وب است، اما این بار در سمت کلاینت. اصطلاح «برنامه‌نویسی وب» اغلب به برنامه‌نویسی سمت سرور اشاره دارد تا برنامه‌نویسی سمت کلاینت. ویژوال سی‌شارپ ابزار مناسبی برای توسعه سرویس‌های وب یا به طور کلی‌تر، برنامه‌های وب نیست. ما از Visual Developer، یکی از نسخه‌های Express ویژوال استودیو ۲۰۰۸، استفاده خواهیم کرد که می‌توان آن را از [2] به آدرس زیر دانلود کرد: [1]: [http://msdn.microsoft.com/fr-fr/express/future/bb421473(en-us).aspx] (مه ۲۰۰۸):

  • [1]: لینک دانلود
  • [2]: زبانه دانلودها
  • [3]: دانلود Visual Developer 2008

برای ایجاد اولین سرویس وب خود، پس از راه‌اندازی ویژوال دویلپر، این مراحل را دنبال کنید:

  • [1]: File / New Web Site را انتخاب کنید
  • [2]: نوع برنامه «Web Service» را انتخاب کنید ASP.NET
  • [3]: زبان توسعه را انتخاب کنید: C#
  • [4]: پوشه‌ای را که پروژه باید در آن ایجاد شود مشخص کنید
  • [5]: پروژه‌ای که در Visual Web Developer ایجاد شده است
  • [6]: پوشه پروژه روی هارد دیسک

یک برنامه وب در Web Developer به شکل زیر ساختاردهی شده است:

  • یک دایرکتوری ریشه که حاوی فایل‌های وب‌سایت است (صفحات وب HTML ایستا، تصاویر، صفحات وب پویا با پسوند .aspx، سرویس‌های وب .asmx و غیره). همچنین شامل فایل [web.config] است که فایل پیکربندی برنامه وب می‌باشد. این فایل همانند فایل [App.config] در برنامه‌های ویندوز عمل می‌کند و ساختار مشابهی دارد.
  • یک پوشه به نام [App_Code]، حاوی کلاس‌ها و رابط‌های وب‌سایت که برای کامپایل شدن در نظر گرفته شده‌اند.
  • یک پوشه به نام [App_Data] که داده‌های مورد استفاده توسط کلاس‌های موجود در [App_Code] در آن قرار می‌گیرند. به عنوان مثال، ممکن است حاوی یک پایگاه داده *.mdf سرور SQL باشد.

[Service.asmx] سرویس وبی است که ما درخواست کردیم ایجاد شود. این فایل تنها شامل خط زیر است:


<%@ WebService Language="C#" CodeBehind="~/App_Code/Service.cs" Class="Service" %>

کد منبع بالا برای وب‌سروری است که میزبان برنامه خواهد بود. در حالت تولید، این سرور معمولاً IIS (Internet Information Server)، وب‌سرور مایکروسافت است. Visual Web Developer شامل یک وب‌سرور سبک است که در حالت توسعه استفاده می‌شود. دستورالعمل قبلی به وب‌سرور دستور می‌دهد:

  • [Service.asmx] یک سرویس وب (دستور WebService) است
  • نوشته شده با C# (ویژگی Language)
  • که کد C# برای سرویس وب در فایل [~/App_Code/Service.cs] (ویژگی CodeBehind) قرار دارد. این همان جایی است که وب سرور برای کامپایل کردن آن را از آنجا بازیابی می‌کند.
  • کلاسی که سرویس وب را پیاده‌سازی می‌کند، Service نام دارد (ویژگی Class)

کد C# [Service.cs] برای سرویس وب تولید شده توسط Visual Developer به شرح زیر است:


using System.Web.Services;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// برای اینکه این سرویس وب از طریق اسکریپت با استفاده از ASP.NET AJAX فراخوانی شود، خط زیر را از حالت توضیحی خارج کنید. 
// [System.Web.Script.Services.ScriptService]
public class Service : System.Web.Services.WebService
{
    public Service () {

        //اگر از اجزای از پیش طراحی‌شده استفاده می‌کنید، تگ # را از خط زیر حذف کنید 
        //InitializeComponent(); 
    }

    [WebMethod]
    public string HelloWorld() {
        return "Hello World";
    }
    
}

کلاس Service شبیه یک کلاس استاندارد C# است، اگرچه چند نکته وجود دارد که باید به آنها توجه کرد:

  • خط ۷: کلاس از کلاس WebService تعریف‌شده در فضای نام System.Web.Services ارث می‌برد. این ارث‌بری همیشه اجباری نیست. در این مثال خاص، می‌توان آن را حذف کرد.
  • خط ۳: خود کلاس با یک ویژگی [WebService(Namespace="http://tempuri.org/")] که برای ارائه یک فضای نام (namespace) برای سرویس وب در نظر گرفته شده است، پیش‌فرض شده است. یک فروشنده کلاس برای کلاس‌های خود یک فضای نام تعیین می‌کند تا به آن‌ها نامی منحصربه‌فرد بدهد و در نتیجه از تداخل با کلاس‌های فروشندگان دیگر که ممکن است نام یکسانی داشته باشند، جلوگیری کند. همین امر در مورد سرویس‌های وب نیز صدق می‌کند. هر سرویس وب باید با یک نام منحصربه‌فرد قابل شناسایی باشد، در این مورد http://tempuri.org/. این نام می‌تواند هر چیزی باشد. لزوماً به شکل یک URI HTTP نیست.
  • خط ۱۵: متد HelloWorld با یک ویژگی [WebMethod] پیش‌روی شده است، که به کامپایلر می‌گوید این متد باید برای کلاینت‌های راه دور سرویس وب قابل مشاهده باشد. متدی که این ویژگی قبل از آن قرار نگرفته باشد، برای مشتریان سرویس وب قابل مشاهده نیست. این می‌تواند یک متد داخلی باشد که توسط متدهای دیگر استفاده می‌شود اما برای انتشار در نظر گرفته نشده است.
  • خط ۹: سازنده سرویس وب. در برنامه ما مورد نیاز نیست.

کلاس تولیدشده [Service.cs] به شرح زیر تبدیل می‌شود:


using System.Web.Services;

[WebService(Namespace = "http://st.istia.univ-angers.fr")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
    [WebMethod]
    public string DisBonjourALaDame(string nomDeLaDame) {
        return string.Format("Bonjour Mme {0}", nomDeLaDame);
    }
    
}

فایل پیکربندی [web.config] تولید شده برای برنامه وب به شرح زیر است:


<?xml version="1.0"?>
<!-- 
    Note: As an alternative to hand editing this file you can use the 
    web admin tool to configure settings for your application. Use
    the Website->Asp.Net Configuration option in Visual Studio.
    A full list of settings and comments can be found in 
    machine.config.comments usually located in 
    \Windows\Microsoft.Net\Framework\v2.x\Config 
-->
<configuration>


    <configSections>
      <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
...
      </sectionGroup>
    </configSections>  


    <appSettings/>
    <connectionStrings/>
...
</configuration>

این فایل ۱۴۰ خط طول دارد. پیچیده است و ما در مورد آن توضیحی نمی‌دهیم. آن را همان‌طور که هست باقی می‌گذاریم. در بالا، تگ‌های <configuration> را می‌بینیم، <configSections>, <sectionGroup>, <appSettings> و <connectionString> که در فایل [App.config] برای برنامه‌های ویندوز با آنها مواجه شدیم.

ما یک سرویس وب کاربردی داریم که قابل اجرا است:

  • [1,2]: روی [Service.asmx] کلیک راست کرده و «مشاهده صفحه در مرورگر» را انتخاب کنید
  • [3]: ویژوال وب دیولپر سرور وب داخلی خود را راه‌اندازی می‌کند و آیکون آن را در گوشهٔ پایین سمت راست نوار وظیفه قرار می‌دهد. سرور وب روی یک پورت تصادفی راه‌اندازی می‌شود، در این مورد پورت ۱۹۰۶. URI نمایش‌داده‌شده /WsHello نام وب‌سایت [4] است.

Visual Web Developer همچنین یک مرورگر برای نمایش صفحهٔ درخواستی راه‌اندازی کرده است، یعنی [Service.asmx]:

  • در [1]، URI صفحه. این شامل URI سایت، [http://localhost:1906/WsHello]، به دنباله URI صفحه، /Service.asmx است.
  • در [2]، پسوند .asmx به وب سرور نشان می‌دهد که این یک صفحه وب معمولی (با پسوند .aspx) که یک صفحه HTML تولید می‌کند نیست، بلکه یک صفحه سرویس وب است. سپس به‌طور خودکار یک صفحه وب تولید می‌کند که برای هر یک از متدهای سرویس وب، لینکی با ویژگی [WebMethod] نمایش می‌دهد. این لینک‌ها امکان تست متدها را فراهم می‌کنند.

کلیک روی لینک [2] در بالا ما را به صفحه زیر می‌برد:

  • در [1]، به URI صفحه جدید [http://localhost:1906/WsHello/Service.asmx?op=DisBonjourALaDame] توجه کنید. این URI سرویس وب با پارامتر op=M است، که در آن M نام یکی از متدهای سرویس وب است.
  • بیایید امضای متد [DisBonjourALaDame] را به یاد بیاوریم:

    public string DisBonjourALaDame(string nomDeLaDame) ;

این متد یک پارامتر از نوع string را می‌پذیرد و همچنین یک نتیجه از نوع string را برمی‌گرداند. این صفحه به ما امکان می‌دهد متد [DisBonjourALaDame] را اجرا کنیم: در [2]، مقدار پارامتر nomDeLaDame را تنظیم می‌کنیم و در [3]، درخواست اجرای متد را می‌دهیم. ما نتیجه زیر را به دست می‌آوریم:

  • در [1]، توجه کنید که URI پاسخ با URI درخواست یکسان نیست. این تغییر کرده است.
  • در [2]، پاسخ وب‌سرور. به نکات زیر توجه کنید:
    • این یک پاسخ با URI XML است، نه HTML
    • نتیجه متد [DisBonjourALaDame] در داخل یک تگ <string> که نمایانگر نوع آن است، قرار گرفته است.
    • تگ <string> دارای یک ویژگی xmlns (فضا نام XML) است که همان فضا نامی است که ما به سرویس وب خود اختصاص داده‌ایم (خط ۱ زیر).

[WebService(Namespace = "http://st.istia.univ-angers.fr")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService

برای اینکه بفهمید مرورگر وب چگونه درخواست خود را ارسال کرده است، باید کد HTML فرم آزمایشی را بررسی کنید:

...
<span>
  <p class="intro">Click <a href="Service.asmx">here</a> for a complete list of operations.</p>
  <h2>DisBonjourALaDame</h2>
  <p class="intro"></p>

  <h3>Test</h3>

         To test the operation using the HTTP POST protocol, click the 'Invoke' button.

   <form action='http://localhost:1906/WsHello/Service.asmx/DisBonjourALaDame' method="POST">                                      
      <table>
              <tr>
                    <td>Parameter</td>
                    <td>Value</td>
                </tr>
                <tr>
               <td>nomDeLaDame:</td>
               <td><input type="text" size="50" name="nomDeLaDame"></td>
           </tr>
                <tr>
                   <td></td>
                  <td align="right"> <input type="submit" value="Invoke" class="button"></td>
           </tr>
      </table>
    </form>
<span>
...
  • خط ۱۱: مقادیر فرم (برچسب فرم) به URL [ http://localhost:1906/WsHello/Service.asmx/DisBonjourALaDame] (ویژگی action) ارسال (post) خواهند شد.
  • خط ۱۹: فیلد ورودی nomDeLaDame نامیده می‌شود (ویژگی name).

فراخوانی سرویس وب [/Service.asmx] به ما امکان داد تا متدهای آن را آزمایش کرده و درک پایه‌ای از ارتباط کلاینت-سرور به دست آوریم.

12.2.2. طرف کلاینت

امکان پیاده‌سازی کلاینت برای سرویس وب راه دور فوق با استفاده از یک کلاینت پایه‌ای TCP/IP وجود دارد. در اینجا، به عنوان مثال، دیالوگ کلاینت/سرور که با استفاده از کلاینتی به نام putty که به سرویس وب راه دور (localhost,1906) متصل است، انجام می‌شود، آورده شده است:

POST /WsHello/Service.asmx/DisBonjourALaDame HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 23

HTTP/1.1 100 Continue
Server: ASP.NET Development Server/9.0.0.0
Date: Sat, 10 May 2008 08:36:41 GMT
Content-Length: 0

nomDeLaDame=Carla+Bruni
HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Sat, 10 May 2008 08:36:47 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Content-Length: 119
Connection: Close

<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://st.istia.univ-angers.fr">Bonjour Mme Carla Bruni</string>
  • خطوط ۱–۵: پیام‌های ارسال‌شده توسط کلاینت putty
  • خط ۱: فرمان POST
  • خطوط ۶–۱۰: پاسخ سرور. این بدان معناست که کلاینت می‌تواند مقادیر را برای POST ارسال کند.
  • خط ۱۱: مقادیری که در فرم ارسال شده‌اند param1=val1&param2=val2& .... برخی کاراکترها باید در قالبی باشند که در یک URL قابل قبول است. این همان چیزی است که قبلاً به آن URL رمزگذاری‌شده گفته می‌شد. در اینجا، فرم تنها یک پارامتر با نام nomDeLaDame دارد. مقدار ارسال‌شده در مجموع شامل ۲۳ کاراکتر است. این طول باید در هدر HTTP در خط ۴ اعلام شود.
  • خطوط ۱۲–۲۲: پاسخ سرور
  • خط ۲۲: نتیجه متد وب [DisBonjourALaDame].

با ویژوال سی‌شارپ، می‌توان از یک جادوگر برای تولید کلاینت یک سرویس وب از راه دور استفاده کرد. این چیزی است که اکنون به آن خواهیم پرداخت.

لایه [1] که در بالا نشان داده شده است، توسط یک پروژه C# ویژوال استودیو از نوع Windows Application به نام ClientWsHello پیاده‌سازی شده است:

  • در [1]، پروژه ClientWsHello در ویژوال سی‌شارپ
  • به [2]؛ فضای نام پیش‌فرض پروژه Client خواهد بود (روی پروژه کلیک راست کنید / Properties / Application). این فضای نام برای ساخت فضای نام کلاینتی که قرار است تولید شود، استفاده خواهد شد.
  • در [3]، روی پروژه کلیک راست کرده و یک مرجع به سرویس وب راه دور اضافه کنید
  • در [4]، URI سرویس وب ایجاد شده در مرحله قبل را وارد کنید
  • در [4b]، ویژوال سی‌شارپ را به سرویس وب مشخص‌شده در [4] متصل کنید. ویژوال سی‌شارپ توضیحات سرویس وب را بازیابی کرده و با استفاده از این توضیحات، قادر به تولید یک کلاینت خواهد بود.
  • در [5]، پس از بازیابی توصیف سرویس وب، ویژوال سی‌شارپ می‌تواند متدهای عمومی آن را نمایش دهد.
  • در [6]، یک فضای نام برای کلاینت مشخص کنید. این به فضای نام تعریف‌شده در [2] اضافه خواهد شد. بنابراین فضای نام کلاینت Client.WsHello خواهد بود.
  • در [6b]، جادوگر را تأیید کنید.
  • در [7]، ارجاع به سرویس وب WsHello در پروژه ظاهر می‌شود. علاوه بر این، یک فایل پیکربندی [app.config] ایجاد شده است.
  • در [8]، تمام فایل‌های پروژه را مشاهده کنید.
  • در [9]، ارجاع به سرویس وب WsHello شامل فایل‌های مختلفی است که به تفصیل وارد آن‌ها نمی‌شویم. با این حال، نگاهی به فایل [Reference.cs]، که کد کلاینت C# تولیدشده است، می‌اندازیم:

namespace Client.WsHello {
...
    public partial class ServiceSoapClient : System.ServiceModel.ClientBase<Client.WsHello.ServiceSoap>, Client.WsHello.ServiceSoap {
        
        public ServiceSoapClient() {
        }
...        
        public string DisBonjourALaDame(string nomDeLaDame) {
            Client.WsHello.DisBonjourALaDameRequest inValue = new Client.WsHello.DisBonjourALaDameRequest();
            inValue.Body = new Client.WsHello.DisBonjourALaDameRequestBody();
            inValue.Body.nomDeLaDame = nomDeLaDame;
            Client.WsHello.DisBonjourALaDameResponse retVal = ((Client.WsHello.ServiceSoap)(this)).DisBonjourALaDame(inValue);
            return retVal.Body.DisBonjourALaDameResult;
        }
    }
}
  • خط ۱: فضای نام کلاینت تولید شده Client.WsHello است. اگر می‌خواهید این فضای نام را تغییر دهید، اینجا جایی است که باید این کار را انجام دهید.
  • خط ۳: کلاس ServiceSoapClient کلاس کلاینت تولید شده است. این یک کلاس پروکسی است به این معنی که پنهان می‌کند که یک سرویس وب راه دور از برنامه ویندوز استفاده می‌شود. برنامهٔ Windows از طریق کلاس محلی Client.WsHello.ServiceSoapClient از کلاس راه دور WsHello استفاده خواهد کرد. برای ایجاد یک نمونه از کلاینت، از سازنده در خط ۵ استفاده کنید:
Client.WsHello.ServiceSoapClient client=new Client.WsHello.ServiceSoapClient();
  • خط ۸: متد DisBonjourALaDame معادل سمت کلاینت متد DisBonjourALaDame سرویس وب است. برنامهٔ ویندوزی متد راه دور DisBonjourALaDame را از طریق متد محلی Client.WsHello.ServiceSoapClient.DisBonjourALaDame به شرح زیر فراخوانی خواهد کرد:
string bonjour=client.DisBonjourALaDame("Carla Bruni");

فایل تولیدشده [app.config] به شرح زیر است:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
         ....
        </bindings>
        <client>
            <endpoint address="http://localhost:1906/WsHello/Service.asmx"... />
        </client>
    </system.serviceModel>
</configuration>

از این فایل، تنها خط ۸ را که حاوی URI سرویس وب است، حفظ می‌کنیم. اگر URI تغییر کند، نیازی به بازسازی کلاینت ویندوز نیست. کافی است URI را در فایل [app.config] تغییر دهید.

بیایید به معماری برنامه‌ی ویندوزی که قصد ساخت آن را داریم بازگردیم:

ما لایه [client] سرویس وب را ساخته‌ایم. لایه [ui] به شرح زیر خواهد بود:

خیر.
نوع
نام
نقش
1
TextBox
textBoxNomDame
نام زن
۲
دکمه
buttonSalutations
برای اتصال به سرویس وب راه دور WsHello و فراخوانی متد DisBonjourALaDame.
۳
برچسب
labelBonjour
نتیجه بازگردانده شده توسط سرویس وب

کد فرم برای [Form1.cs] به شرح زیر است:


using System;
using System.Windows.Forms;
using Client.WsHello;

namespace ClientSalutations {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void buttonSalutations_Click(object sender, EventArgs e) {
            // ساعت شنی
            Cursor=Cursors.WaitCursor;
            // پرس‌وجوی سرویس وب
            labelBonjour.Text = new ServiceSoapClient().DisBonjourALaDame(textBoxNomDame.Text.Trim());
            // نمایندهٔ استاندارد
            Cursor = Cursors.Arrow;
        }
    }
}
  • خط ۱۵: کلاینت سرویس وب ایجاد می‌شود. نوع آن Client.WsHello.ServiceSoapClient است. فضای نام Client.WsHello در خط ۳ اعلام شده است. متد محلی ServiceSoapClient().DisBonjourALaDame فراخوانی می‌شود. می‌دانیم که این متد خود متد راه دوری با همین نام را در سرویس وب فراخوانی می‌کند.

12.3. یک سرویس وب برای عملیات حسابداری

ما قصد داریم یک برنامهٔ کاربردی دوم مشتری/سرور بسازیم، دوباره با معماری ساده‌شدهٔ زیر:

وب سرویس قبلی یک متد واحد ارائه می‌داد. اکنون در حال بررسی وب سرویسی هستیم که چهار عمل اصلی را ارائه خواهد داد:

  1. جمع(a,b)، که a+b را بازمی‌گرداند
  2. تفریق(a,b)، که a-b را برمی‌گرداند
  3. ضرب(a,b)، که a*b را برمی‌گرداند
  4. divide(a,b)، که a/b را برمی‌گرداند

و این سرویس توسط رابط کاربری گرافیکی زیر فراخوانی خواهد شد:

  • در [1]، عملیاتی که باید انجام شود
  • در [2,3]: عملوندها
  • در [4]، دکمه‌ای برای فراخوانی سرویس وب
  • در [5]، نتیجه بازگردانده‌شده توسط سرویس وب

12.3.1. سوی سرور

ما در حال ساخت یک پروژه سرویس وب با استفاده از Visual Web Developer هستیم:

  • در [1]، وب‌اپلیکیشن WsOperations تولید شده
  • در [2]، برنامهٔ وب WsOperations به شرح زیر بازطراحی شده است:
  • صفحه وب [Service.asmx] به [Operations.asmx] تغییر نام یافته است
  • کلاس [Service.cs] به [Operations.cs] تغییر نام داده شده است
  • فایل [web.config] برای نشان دادن اینکه ضروری نیست، حذف شده است.

صفحه وب [Service.asmx] شامل خط زیر است:


<%@ WebService Language="C#" CodeBehind="~/App_Code/Operations.cs" Class="Operations" %>

خدمات وب توسط کلاس زیر ارائه می‌شود: [Operations.cs]:


using System.Web.Services;

[WebService(Namespace = "http://st.istia.univ-angers.fr/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Operations : System.Web.Services.WebService
{
    [WebMethod]
    public double Ajouter(double a, double b)
    {
        return a + b;
    }

    [WebMethod]
    public double Soustraire(double a, double b)
    {
        return a - b;
    }

    [WebMethod]
    public double Multiplier(double a, double b)
    {
        return a * b;
    }

    [WebMethod]
    public double Diviser(double a, double b)
    {
        return a / b;
    }

}

برای استقرار سرویس وب، ما رویه‌ای را که در [3] تشریح شده است دنبال می‌کنیم. سپس صفحه آزمایشی را برای چهار روش سرویس وب WsOperations دریافت می‌کنیم:

Image

از خوانندگان دعوت می‌شود تا چهار روش را آزمایش کنند.

12.3.2. سمت کلاینت

با استفاده از ویژوال سی‌شارپ، یک برنامه ویندوزی ClientWsOperations ایجاد می‌کنیم:

  • در [1]، پروژه ClientWsOperations در ویژوال سی‌شارپ
  • در [2]، فضای نام پیش‌فرض پروژه Client خواهد بود (روی پروژه کلیک راست کنید / Properties / Application). این فضای نام برای ساخت فضای نام کلاینتی که قرار است تولید شود، استفاده خواهد شد.
  • در [3]، روی پروژه کلیک راست کرده و یک مرجع به سرویس وب موجود اضافه کنید
  • در [4]، URI سرویس وب ایجاد شده در مرحله قبل را وارد کنید. برای این کار، نوار آدرس مرورگری را که صفحه آزمایشی سرویس وب را نمایش می‌دهد، بررسی کنید.
  • در [4b]، ویژوال سی‌شارپ را به سرویس وب مشخص‌شده در [4] متصل کنید. ویژوال سی‌شارپ توضیحات سرویس وب را بازیابی کرده و با استفاده از این توضیحات، قادر به تولید یک کلاینت خواهد بود.
  • در [5]، پس از بازیابی توصیف سرویس وب، ویژوال سی‌شارپ می‌تواند متدهای عمومی آن را نمایش دهد.
  • در [6]، یک فضای نام (namespace) برای کلاینتِ تولیدشده مشخص کنید. این فضا به فضای نام تعریف‌شده در [2] اضافه خواهد شد. بنابراین، فضای نام کلاینت، Client.WsOperations خواهد بود.
  • در [6b]، جادوگر را تأیید کنید.
  • در [7]، ارجاع به سرویس وب WsOperations در پروژه ظاهر می‌شود. علاوه بر این، یک فایل پیکربندی [app.config] ایجاد شده است.

توجه داشته باشید که کلاینت تولیدشده از نوع Client.WsOperations.OperationsSoapClient است، جایی که

  • Client.WsOperations فضای نام کلاینت سرویس وب است
  • Operations کلاس سرویس وب راه دور است.

اگرچه راهی منطقی برای ساخت این نام وجود دارد، اما اغلب یافتن آن در فایل [Reference.cs] که به‌طور پیش‌فرض پنهان است، آسان‌تر است. محتوای آن به شرح زیر است:


namespace Client.WsOperations {
 ...
    public partial class OperationsSoapClient : System.ServiceModel.ClientBase<Client.WsOperations.OperationsSoap>, Client.WsOperations.OperationsSoap {
        
        public OperationsSoapClient() {
        }
...
        public double Ajouter(double a, double b) {
            ...
        }
        
        public double Soustraire(double a, double b) {
            ...
        }
        
        public double Multiplier(double a, double b) {
            ...
        }
        
        public double Diviser(double a, double b) {
            ...
        }
    }
}

متدهای Ajouter، Soustraire، متدهای Multiplier و Diviser از سرویس وب راه دور از طریق متدهای پروکسی با همین نام (خطوط 8، 12، 16، 20) در کلاینت از نوع Client.WsOperations.OperationsSoapClient (خط 3) فراخوانی خواهند شد.

تنها کاری که باقی مانده، ساخت رابط گرافیکی است:

خیر.
نوع
name
نقش
۱
ComboBox
comboBoxOperations
فهرست عملیات حساب
2
TextBox
textBoxA
عدد a
۳
TextBox
textBoxB
شماره b
۴
دکمه
buttonExécuter
پرس‌وجو از سرویس وب راه دور
۵
برچسب
labelRésultat
نتیجهٔ عملیات

کد برای [Form1.cs] به شرح زیر است:


using System;
using System.Windows.Forms;
using Client.WsOperations;

namespace ClientWsOperations {
    public partial class Form1 : Form {
        // جدول عملیات
        private string[] opérations = { "Ajouter", "Soustraire", "Multiplier", "Diviser" };
        // سرویس وب برای تماس
        private OperationsSoapClient opérateur = new OperationsSoapClient();

        // سازنده
        public Form1() {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e) {
            // منوی بازشو برای عملیات
            comboBoxOperations.Items.AddRange(opérations);
            comboBoxOperations.SelectedIndex = 0;
        }

        private void buttonExécuter_Click(object sender, EventArgs e) {
            //بررسی پارامترهای a و b عملیات
            textBoxMessage.Text = "";
            bool erreur = false;
            Double a = 0;
            if (!Double.TryParse(textBoxA.Text, out a)) {
                textBoxMessage.Text += "Nombre a erroné...";
            }
            Double b = 0;
            if (!Double.TryParse(textBoxB.Text, out b)) {
                textBoxMessage.Text += String.Format("{0}Nombre b erroné...", Environment.NewLine);
            }
            if (erreur) {
                return;
            }
            // اجرای عملیات
            Double c=0;
            try {
                switch (comboBoxOperations.SelectedItem.ToString()) {
                    case "Ajouter":
                        c=opérateur.Ajouter(a, b);
                        break;
                    case "Soustraire":
                        c=opérateur.Soustraire(a, b);
                        break;
                    case "Multiplier":
                        c=opérateur.Multiplier(a, b);
                        break;
                    case "Diviser":
                        c=opérateur.Diviser(a, b);
                        break;
                }
                // نمایش نتیجه
                labelRésultat.Text = c.ToString();
            } catch (Exception ex) {
                textBoxMessage.Text = ex.Message;
            }
        }
    }
}
  • خط ۳: فضای نام مشتری سرویس وب راه دور
  • خط ۱۰: کلاینت سرویس وب راه دور همزمان با بارگذاری فرم ایجاد می‌شود
  • خطوط ۱۷–۲۱: فهرست کشویی عملیات هنگام بارگذاری اولیه فرم پر می‌شود
  • خط ۲۳: اجرای عملیاتی که توسط کاربر درخواست شده است
  • خطوط ۲۵–۳۷: بررسی می‌کنیم که ورودی‌های a و b در واقع اعداد حقیقی هستند
  • خطوط ۴۱–۵۴: یک عبارت switch برای اجرای عملیات راه دوری که کاربر درخواست کرده است
  • خطوط ۴۳، ۴۶، ۴۹ و ۵۲: از کلاینت محلی پرس‌وجو می‌شود. به‌طور شفاف برای کاربر، کلاینت سپس از سرویس وب راه دور پرس‌وجو می‌کند.

12.4. یک سرویس وب برای محاسبه مالیات

ما در حال بازنگری در برنامهٔ محاسبهٔ مالیات آشنا هستیم. آخرین باری که روی آن کار کردیم، آن را به یک سرور TCP راه دور تبدیل کردیم که می‌توانست از طریق اینترنت فراخوانی شود. اکنون آن را به یک سرویس وب تبدیل می‌کنیم.

معماری نسخه ۸ به شرح زیر بود:

معماری نسخه ۹ مشابه خواهد بود:

این معماری مشابه معماری نسخه ۸ است که در بخش ۱۱.۹.۱ به آن پرداخته شده، اما در آن سرور و کلاینت TCP با یک سرویس وب و کلاینت پروکسی آن جایگزین شده‌اند. ما به‌طور کامل لایه‌های [ui]، [metier] و [dao] را از نسخهٔ ۸ اتخاذ خواهیم کرد.

12.4.1. سمت سرور

ما در حال ایجاد یک پروژه سرویس وب با استفاده از Visual Web Developer هستیم:

  • در [1]، وب‌اپلیکیشن WsImpot تولید شده
  • به [2]، برنامه وب WsImpot به شرح زیر بازطراحی شده است:
    • صفحه وب [Service.asmx] به [ServiceImpot.asmx] تغییر نام یافته است
    • کلاس [Service.cs] به [ServiceImpot .cs] تغییر نام داده شده است

صفحه وب [ServiceImpot.asmx] شامل خط زیر است:


<%@ WebService Language="C#" CodeBehind="~/App_Code/ServiceImpot.cs" Class="ServiceImpot" %>

خدمات وب توسط کلاس زیر ارائه می‌شود: [ServiceImpot.cs]:


using System.Web.Services;

[WebService(Namespace = "http://st.istia.univ-angers.fr/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class ServiceImpot : System.Web.Services.WebService
{

    [WebMethod]
    public int CalculerImpot(bool marié, int nbEnfants, int salaire)
    {
        return 0;
    }

}

سرویس وب تنها متد CalculerImpot را در خط ۹ در دسترس قرار می‌دهد.

بیایید به معماری کلاینت/سرور نسخه ۸ بازگردیم:

پروژه ویژوال استودیو برای سرور [1] به شرح زیر بود:

  • در [1]، پروژه. این شامل اجزای زیر بود:
    • [ServeurImpot.cs]: سرور محاسبه مالیات TCP/IP به صورت یک برنامه کنسول.
    • [dbimpots.sdf]: پایگاه داده فشرده سرور SQL (نسخه ۷) که در بند ۹.۸.۵ توصیف شده است.
    • [App.config]: فایل پیکربندی برنامه.
  • در [2]، پوشه [lib] شامل فایل‌های DLL مورد نیاز برای پروژه است:
    • [ImpotsV7-dao]: لایه [dao] برای نسخه ۷
    • [ImpotsV7-metier]: لایه [metier] برای نسخه 7
    • [antlr.runtime, CommonLogging, Spring.Core] برای Spring
  • در [3]، پروژه به

لایه‌های [metier] و [dao] برای این نسخه از قبل وجود دارند: این‌ها همان لایه‌هایی هستند که در نسخه‌های ۷ و ۸ استفاده می‌شوند. آن‌ها به شکل DLL هستند که ما آن را به صورت زیر در پروژه ادغام می‌کنیم:

  • به صورت [1]؛ پوشه [lib] از سرور نسخه ۸ به پروژه سرویس وب نسخه ۹ کپی شده است.
  • در [2]، ما ویژگی‌های صفحه را اصلاح می‌کنیم تا فایل‌های DLL را از پوشه‌های [lib] و [4] به مراجع پروژه [3] اضافه کنیم.

پس از این عملیات، تمام لایه‌های مورد نیاز برای سرور [1] در زیر فهرست شده‌اند:

در حالی که کامپوننت‌های سرور [1]، [serveur]، [metier]، [dao]، [entites]، [spring] همگی در پروژه Visual Studio موجود هستند، اما عنصری که هنگام راه‌اندازی برنامه وب آن‌ها را ایجاد می‌کند، وجود ندارد. در نسخهٔ ۸، یک کلاس اصلی حاوی متد استاتیک [Main] مسئول ایجاد لایه‌ها با استفاده از Spring بود. در یک برنامهٔ وب، کلاسی که قادر به انجام وظیفه‌ای مشابه است، کلاسی است که با فایل [Global.asax] مرتبط است:

  • در [1]، یک عنصر جدید به پروژه وب اضافه می‌شود
  • در [2]، نوع «کلاس برنامهٔ جهانی» را انتخاب کنید
  • در [3]، نام پیش‌فرض پیشنهادی برای این عنصر
  • در [4]، افزودن را تأیید کنید
  • در [5]، عنصر جدید به پروژه اضافه شده است

بیایید محتویات فایل [Global.asax] را بررسی کنیم:


<%@ Application Language="C#" %>

<script runat="server">

    void Application_Start(object sender, EventArgs e) 
    {
        // کدی که هنگام راه‌اندازی برنامه اجرا می‌شود
    }
    
    void Application_End(object sender, EventArgs e) 
    {
        //  کدی که هنگام بستن برنامه اجرا می‌شود
    }
        
    void Application_Error(object sender, EventArgs e) 
    { 
        // کدی که هنگام رخ دادن یک خطای بدون رسیدگی اجرا می‌شود
    }

    void Session_Start(object sender, EventArgs e) 
    {
        // کدی که هنگام شروع یک جلسه جدید اجرا می‌شود
    }

    void Session_End(object sender, EventArgs e) 
    {
        // کدی که هنگام پایان یک جلسه اجرا می‌شود. 
    }
       
</script>

این فایل ترکیبی از تگ‌های مخصوص سرور وب (خطوط ۱، ۳، ۳۰) و کد C# است. این روش تنها روشی بود که با ASP، پیشین ASP.NET که فناوری کنونی مایکروسافت برای برنامه‌نویسی وب است، استفاده می‌شد. با ASP.NET، این روش هنوز قابل استفاده است اما روش پیش‌فرض نیست. روش پیش‌فرض، روش موسوم به «CodeBehind» است که ما در صفحات سرویس وب با آن مواجه شده‌ایم، برای مثال اینجا در [ServiceImpot.asmx]:


<%@ WebService Language="C#" CodeBehind="~/App_Code/ServiceImpot.cs" Class="ServiceImpot" %>

ویژگی CodeBehind مشخص می‌کند که کد منبع صفحه [ServiceImpot.asmx] در کجا قرار دارد. بدون این ویژگی، کد منبع در صفحه [ServiceImpot.asmx] قرار می‌گرفت، با نحوی مشابه آنچه در [Global.asax] یافت می‌شود. ما فایل [Global.asax] را به همان شکلی که تولید شده بود حفظ نخواهیم کرد، اما کد آن به ما امکان می‌دهد تا بفهمیم برای چه منظوری استفاده می‌شود:

  • کلاسی که با Global.asax مرتبط است، هنگام شروع برنامه کاربردی ایجاد می‌شود. عمر آن کل برنامه را در بر می‌گیرد. به طور عملی، این کلاس تنها زمانی از بین می‌رود که وب‌سرور خاموش شود.
  • سپس متد Application_Start اجرا می‌شود. این تنها باری است که اجرا خواهد شد. بنابراین برای ایجاد اشیایی که در میان همه کاربران مشترک هستند، استفاده می‌شود. این اشیا قرار داده می‌شوند:
    • یا در فیلدهای ایستا کلاس مرتبط با Global.asax. از آنجا که این کلاس به‌طور دائم وجود دارد، هر کاربری می‌تواند اطلاعات آن را بخواند.
    • یا کانتینر Application. این کانتینر نیز هنگام راه‌اندازی برنامه ایجاد می‌شود و عمر آن با عمر برنامه یکسان است.
      • برای قرار دادن داده در این کانtejner، عبارت `Application["clé"]=value` را بنویسید؛
      • برای بازیابی آن، بنویسید T value=(T)Application["clé"]; که در آن T نوع valeur است.
  • متد Session_Start هر بار که کاربر جدیدی درخواستی ارسال می‌کند، اجرا می‌شود. چگونه کاربر جدید شناسایی می‌شود؟ هر کاربر (معمولاً یک مرورگر) پس از اولین درخواست خود، یک توکن جلسه دریافت می‌کند که رشته‌ای منحصربه‌فرد از کاراکترها برای هر کاربر است. کاربر سپس توکن جلسه‌ای را که دریافت کرده است، در هر درخواست جدید ارسال می‌کند. این امر به سرور وب اجازه می‌دهد تا او را شناسایی کند. از آنجا که یک کاربر واحد درخواست‌های متعددی ارسال می‌کند، داده‌های اختصاصی آن کاربر را می‌توان در کانتینر Session ذخیره کرد:
    • برای ذخیره داده‌ها در این کانتینر، می‌نویسیم `Session["clé"]=value
    • برای بازیابی آن، بنویسید `T value=(T)Session["clé"];` که در آن `T` نوع `valeur` است.

دوره عمر جلسه به طور پیش‌فرض به ۲۰ دقیقه عدم فعالیت کاربر محدود می‌شود (c.a.d، یعنی کاربر به مدت ۲۰ دقیقه توکن جلسه خود را ارسال نکرده است).

  • روش Application_Error زمانی اجرا می‌شود که یک استثنا که توسط برنامه وب مدیریت نشده است تا وب سرور بالا برده شود.
  • سایر متدها کمتر استفاده می‌شوند.

پس از پرداختن به این نکات کلی، Global.asax چگونه می‌تواند برای ما مفید باشد؟ ما از متد آن، Application_Start، برای راه‌اندازی لایه‌های [metier] استفاده خواهیم کرد، [dao] و [entites] که در DLL و [ImpotsV7-metier, ImpotsV7-dao] قرار دارند. ما از Spring برای ایجاد نمونه‌های آن‌ها استفاده خواهیم کرد. ارجاعات به لایه‌هایی که بدین ترتیب ایجاد شده‌اند سپس در فیلدهای ایستا کلاس مرتبط با Global.asax ذخیره خواهند شد.

ابتدا، کد C# را از Global.asax به یک کلاس جداگانه منتقل می‌کنیم. پروژه اکنون به شکل زیر است:

در [1]، فایل [Global.asax] با کلاس [Global.cs] [2] که شامل یک خط زیر است، مرتبط خواهد شد:


<%@ Application Language="C#" Inherits="WsImpot.Global"%>

ویژگی Inherits=&quot;WsImpot.Global&quot; نشان می‌دهد که کلاسی که با Global.asax مرتبط است، از کلاس WsImpot.Global ارث می‌برد. این کلاس در [Global.cs] به صورت زیر تعریف شده است:


using System;
using Metier;
using Spring.Context.Support;
namespace WsImpot
{
    public class Global : System.Web.HttpApplication
    {
        //لایه کسب‌وکار
        public static IImpotMetier Metier;

        // متدی که هنگام شروع برنامه اجرا می‌شود
        private void Application_Start(object sender, EventArgs e)
        {
            //نمونه‌های لایه‌های [metier] و [dao]
            Metier = ContextRegistry.GetContext().GetObject("metier") as IImpotMetier;
        }
    }
}
  • خط ۴: فضای نام کلاس
  • خط ۶: کلاس Global. می‌توانید هر نامی که دوست دارید برای آن انتخاب کنید. نکته مهم این است که این کلاس از کلاس System.Web.HttpApplication ارث می‌برد.
  • خط ۹: یک فیلد استاتیک عمومی که حاوی مرجعی به لایه [metier] خواهد بود.
  • خط ۱۲: متد Application_Start که هنگام شروع برنامه اجرا می‌شود.
  • خط ۱۵: از Spring برای پردازش فایل [web.config] استفاده می‌شود که در آن اشیایی برای نمونه‌سازی پیدا می‌کند تا لایه‌های [metier] و [dao] ایجاد شوند. هیچ تفاوتی بین استفاده از Spring با [App.config] در یک برنامه ویندوزی و استفاده از Spring با [web.config] در یک برنامه وب وجود ندارد. [web.config] و [App.config] نیز ساختار یکسانی دارند. خط ۱۵ مرجع لایه [metier] را در فیلد ایستا در خط ۹ ذخیره می‌کند، به طوری که این مرجع برای تمام پرس‌وجوها از همه کاربران در دسترس باشد.

فایل [web.config] به شرح زیر خواهد بود:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
    </sectionGroup>
  </configSections>

  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object name="dao" type="Dao.DataBaseImpot, ImpotsV7-dao">
        <constructor-arg index="0" value="MySql.Data.MySqlClient"/>
        <constructor-arg index="1" value="Server=localhost;Database=bdimpots;Uid=admimpots;Pwd=mdpimpots;"/>
        <constructor-arg index="2" value="select limite, coeffr, coeffn from tranches"/>
      </object>
      <object name="metier" type="Metier.ImpotMetier, ImpotsV7-metier">
        <constructor-arg index="0" ref="dao"/>
      </object>
    </objects>
  </spring>
</configuration>

این فایل [App.config] است که در نسخه ۷ برنامه استفاده می‌شود و در بخش ۹.۸.۴ مورد بحث قرار گرفته است.

  • خطوط 16–20: لایه [dao] را تعریف می‌کند که با پایگاه داده MySQL5 کار می‌کند. این پایگاه داده در بخش 9.8.1 توضیح داده شده است.
  • خطوط ۲۱–۲۳: لایه [metier] را تعریف کنید

بیایید به معمای سرور بازگردیم:

هنگامی که برنامه کاربردی راه‌اندازی می‌شود، لایه‌های [metier] و [dao] نمونه‌سازی می‌شوند. عمر لایه‌ها با خود برنامه کاربردی یکسان است. سرویس وب چه زمانی نمونه‌سازی می‌شود؟ در واقع، هر بار که درخواستی به آن ارسال می‌شود. در پایان درخواست، ابجکتی که آن را پردازش کرده است، حذف می‌شود. بنابراین، یک سرویس وب از نظر اول بی‌حالت است. این سرویس نمی‌تواند اطلاعات را بین درخواست‌ها در فیلدهایی که به خود آن تعلق دارد ذخیره کند. با این حال، می‌تواند اطلاعات را در جلسه (session) کاربر ذخیره کند. برای این کار، متدهایی که ارائه می‌دهد باید با یک ویژگی (attribute) خاص تگ شوند:


    [WebMethod(EnableSession=true)]
    public int CalculerImpot(bool marié, int nbEnfants, int salaire)
....

در مثال بالا، خط ۱ متد CalculerImpot را مجاز می‌کند تا به کانtejner Session که قبلاً در مورد آن بحث کردیم، دسترسی داشته باشد. ما در برنامه خود نیازی به استفاده از این ویژگی نداریم. بنابراین سرویس وب WsImpot با هر درخواست ایجاد خواهد شد و بدون حالت خواهد بود.

اکنون می‌توانیم کد [ServiceImpot.cs] را که سرویس وب را پیاده‌سازی می‌کند، بنویسیم:


using System.Web.Services;
using WsImpot;

[WebService(Namespace = "http://st.istia.univ-angers.fr/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class ServiceImpot : System.Web.Services.WebService
{

    [WebMethod]
    public int CalculerImpot(bool marié, int nbEnfants, int salaire)
    {
        return Global.Metier.CalculerImpot(marié, nbEnfants, salaire);
    }

}
  • خط ۱۰: تنها متد سرویس وب
  • خط ۱۲: از روش CalculerImpot از لایه [metier] استفاده می‌شود. یک ارجاع به این لایه در فیلد ثابت Metier از کلاس Global یافت می‌شود. این مربوط به فضای نام WsImpot است (خط ۲).

اکنون آماده راه‌اندازی سرویس وب هستیم. پیش از آن، باید SGBD و MySQL5 را راه‌اندازی کنیم تا پایگاه داده bdimpots در دسترس باشد. پس از انجام این کار، سرویس وب [1] را راه‌اندازی می‌کنیم:

سپس مرورگر صفحه [2] را نمایش می‌دهد. ما لینک را دنبال می‌کنیم:

ما برای هر یک از سه پارامتر متد CalculerImpot مقداری را تعیین می‌کنیم و درخواست اجرای این متد را می‌دهیم. نتیجه زیر را به دست می‌آوریم که صحیح است:

Image

12.4.2. یک کلاینت گرافیکی ویندوزی برای سرویس وب راه دور

اکنون که سرویس وب نوشته شده است، به سمت کلاینت می‌رویم. بیایید معماری برنامهٔ کلاینت/سرور را دوباره مرور کنیم:

ما باید کلاینت [2] را بنویسیم. رابط گرافیکی آن دقیقاً مشابه نسخه ۸ خواهد بود:

برای نوشتن بخش [client] نسخه ۹، کار را با بخش [client] نسخه ۸ شروع کرده و سپس تغییرات لازم را اعمال می‌کنیم. ما پروژه Visual Studio مورد بحث در بخش 11.9.4.1 را کپی می‌کنیم، آن را با نام ClientWsImpot بازنامه‌گذاری کرده و در Visual Studio باز می‌کنیم:

راه‌حل Visual Studio از نسخهٔ ۸ شامل دو پروژه بود:

  • پروژه [metier] [1] که یک کلاینت TCP برای سرور TCP محاسبه مالیات بود
  • پروژه‌های [ui] و [2] برای رابط کاربری گرافیکی.

تغییراتی که باید انجام شوند به شرح زیر است:

  • پروژه [metier] اکنون باید به‌عنوان کلاینت یک سرویس وب عمل کند
  • پروژه [ui] باید از لایه جدید [metier] به DLL ارجاع دهد
  • پیکربندی لایه [metier] در داخل [App.config] باید تغییر کند.

12.4.2.1. لایه جدید [metier]

  • در [1]، IImpotMetier رابط لایه [metier] است و ImpotMetierTcp پیاده‌سازی آن توسط یک کلاینت TCP است
  • در [2]، پیاده‌سازی ImpotMetierTcp را حذف می‌کنیم. ما باید پیاده‌سازی دیگری از رابط IImpotMetier ایجاد کنیم که به‌عنوان کلاینت برای یک سرویس وب عمل خواهد کرد.
  • در [3]، ما Client را به عنوان فضای نام پیش‌فرض برای پروژه [metier] نام‌گذاری می‌کنیم. DLL که تولید خواهد شد، [ImpotsV9-metier.dll] نامیده خواهد شد.
  • در [4]، ما یک مرجع به سرویس وب WsImpot ایجاد می‌کنیم.
  • در [5]، آن را پیکربندی و اعتبارسنجی می‌کنیم.
  • در [6]، مرجع سرویس وب WsImpot ایجاد شده و فایلی به نام [app.config] تولید شده است.

در فایل مخفی [Reference.cs]:

  • فضای نام Client.WsImpot است
  • کلاس مشتری ServiceImpotSoapClient نام دارد
  • این کلاس یک متد با امضای واحد دارد:

        public int CalculerImpot(bool marié, int nbEnfants, int salaire) ;

اکنون باید رابط IImpotMetier را پیاده‌سازی کنیم:


namespace Metier {
    public interface IImpotMetier {
        int CalculerImpot(bool marié, int nbEnfants, int salaire);
    }
}

ما آن را با استفاده از کلاس زیر پیاده‌سازی می‌کنیم، ImpotMetierWs:


using System.Net.Sockets;
using System.IO;
using Client.WsImpot;

namespace Metier {
    public class ImpotMetierWs : IImpotMetier {

        // کلاینت سرویس وب از راه دور
        private ServiceImpotSoapClient client = new ServiceImpotSoapClient();

        //محاسبه مالیات
        public int CalculerImpot(bool marié, int nbEnfants, int salaire) {
            return client.CalculerImpot(marié, nbEnfants, salaire);
        }

    }
}
  • خط ۶: کلاس ImpotMetierWs رابط IImpotMetier را پیاده‌سازی می‌کند.
  • خط ۹: هنگامی که یک نمونه از ImpotMetierWs ایجاد می‌شود، فیلد client با یک نمونه از کلاینت سرویس وب محاسبه مالیات مقداردهی اولیه می‌شود.
  • خط ۱۲: تنها متدی از رابط IImpotMetier که باید پیاده‌سازی شود.
  • خط ۱۳: از روش CalculerImpot در کلاینت سرویس وب محاسبه مالیات از راه دور استفاده می‌شود. در نهایت، روش CalculerImpot در سرویس وب از راه دور فراخوانی خواهد شد.

می‌توان DLL برای پروژه تولید شود:

  • تا [1]، پروژه [client] در وضعیت نهایی خود
  • به [2]، تولید DLL پروژه
  • به [3]؛ DLL و ImpotsV9-metier.dll در پوشه /bin/Release پروژه قرار دارند.

12.4.2.2. لایه جدید [ui]

لایه [client] مشتری نوشته شده است. هنوز باید لایه [ui] را بنویسیم. بیایید به پروژه Visual Studio بازگردیم:

  • در [1]، پروژه [ui] از نسخه ۸
  • به [2]، DLL و ImpotsV8-metier از لایه قدیمی [metier] با ImpotsV9-metier از لایه جدید DLL جایگزین شده‌اند
  • در [3]، DLL و ImpotsV9-metier به مراجع پروژه اضافه شده‌اند.

تغییر دوم بر [App.config] تأثیر می‌گذارد. باید توجه داشت که این فایل توسط Spring برای ایجاد لایه [metier] استفاده می‌شود. از آنجا که این لایه تغییر کرده است، پیکربندی [App.config] باید به‌روزرسانی شود. علاوه بر این، [App.config] باید پیکربندی لازم برای اتصال به سرویس وب محاسبه مالیات راه دور را داشته باشد. این پیکربندی در فایل [app.config] در پروژه [metier] زمانی که ارجاع به سرویس وب راه دور به آن اضافه شد، تولید گردید.

بنابراین فایل [App.config] به شرح زیر درمی‌آید:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <configSections>
        <sectionGroup name="spring">
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
    </configSections>

    <spring>
        <context>
            <resource uri="config://spring/objects" />
        </context>
        <objects xmlns="http://www.springframework.net">
            <object name="metier" type="Metier.ImpotMetierWs, ImpotsV9-metier">
            </object>
        </objects>
    </spring>

    <!--سرویس وب -->
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="ServiceImpotSoap" closeTimeout="00:01:00" openTimeout="00:01:00"
                        receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
                        bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                        maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                        messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                        useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                                realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:2172/WsImpot/ServiceImpot.asmx"
                    binding="basicHttpBinding" bindingConfiguration="ServiceImpotSoap"
                    contract="WsImpot.ServiceImpotSoap" name="ServiceImpotSoap" />
        </client>
    </system.serviceModel>
</configuration>
  • خطوط ۱۵–۱۸: Spring تنها یک شیء را ایجاد می‌کند، لایه [metier]
  • خط 16: لایه [metier] توسط کلاس [Metier.ImpotMetierWs] که در داخل DLL ImpotsV9-metier قرار دارد، نمونه سازی می‌شود.
  • خطوط ۲۲–۴۶: پیکربندی کلاینت سرویس وب راه دور. این یک کپی و پیست از محتویات فایل [app.config] از پروژه [metier] است.

آماده هستیم. با فشردن Ctrl-F5 برنامه را اجرا کنید (سرویس وب باید در حال اجرا باشد، فرآیندهای SGBD و MySQL5 باید در حال اجرا باشند و پورت مشخص‌شده در خط ۴۲ بالا باید صحیح باشد):

  

12.5. یک کلاینت وب برای سرویس وب محاسبه مالیات

بیایید معماری برنامهٔ کلاینت/سرور را که همین‌الان نوشته شده است، دوباره بررسی کنیم:

لایه [ui] فوق با استفاده از یک کلاینت گرافیکی ویندوز پیاده‌سازی شده بود. اکنون آن را با استفاده از یک رابط وب پیاده‌سازی خواهیم کرد:

 

این یک تغییر مهم برای کاربران است. در حال حاضر، برنامه کلاینت/سرور ما، نسخه ۹، می‌تواند همزمان به چندین کلاینت خدمت‌رسانی کند. این یک بهبود نسبت به نسخه ۸ است که فقط می‌توانست همزمان به یک کلاینت خدمت‌رسانی کند. محدودیت این است که کاربرانی که می‌خواهند از سرویس وب محاسبه مالیات استفاده کنند، باید کلاینت ویندوزی که ما نوشته‌ایم را روی رایانه خود نصب داشته باشند. در این نسخه جدید که آن را نسخه ۱۰ می‌نامیم، کاربران قادر خواهند بود از طریق مرورگر خود به سرویس وب محاسبه مالیات دسترسی پیدا کنند.

در معماری نشان داده شده در بالا:

  • سوی سرور بدون تغییر باقی می‌ماند. این دقیقاً همان‌طور است که در نسخه ۹ بود.
  • در سمت کلاینت، لایه [client du service web] بدون تغییر باقی می‌ماند. این لایه در داخل DLL و [ImpotsV9-metier] قرار گرفته است. ما این DLL را مجدداً استفاده خواهیم کرد.
  • در نهایت، تنها تغییر شامل جایگزینی رابط کاربری گرافیکی ویندوز با یک رابط وب است.

ما به برخی از مفاهیم جدید در برنامه‌نویسی وب سمت سرور خواهیم پرداخت. از آنجایی که هدف این سند آموزش برنامه‌نویسی وب نیست، سعی می‌کنیم رویکردی را که در پیش خواهیم گرفت بدون ورود به جزئیات زیاد توضیح دهیم. بنابراین، این بخش کمی جنبه «جادویی» خواهد داشت. با این حال، ما معتقدیم که اتخاذ این رویکرد برای ارائه یک نمونه جدید از معماری چندلایه که در آن یکی از لایه‌ها تغییر کرده است، ارزشمند است.

بنابراین معماری نسخه ۱۰ به شرح زیر است:

ما در حال حاضر تمام لایه‌ها را داریم، به جز لایه [web]. برای درک بهتر آنچه قرار است انجام شود، باید معماری سمت کلاینت را با جزئیات بیشتری توضیح دهیم. این معماری به شرح زیر خواهد بود:

  • کاربر وب یک فرم وب در مرورگر خود دارد
  • این فرم به وب سرور ۱ ارسال می‌شود که آن را از طریق لایه [web] پردازش می‌کند
  • لایه [web] به خدمات کلاینت سرویس وب راه دور که در [ImpotsV9-metier.dll] احاطه شده است، نیاز خواهد داشت.
  • کلاینت سرویس وب راه دور با وب‌سرور ۲ که میزبان سرویس وب راه دور است، ارتباط برقرار خواهد کرد.
  • پاسخ سرویس وب راه دور به لایه وب کلاینت بازمی‌گردد و توسط آن به صورت صفحه‌ای برای ارسال به کاربر رندر می‌شود.

بنابراین وظیفه ما در اینجا این است:

  • ساخت فرم وبی که کاربر در مرورگر خود خواهد دید
  • نوشتن برنامه وبی که درخواست کاربر را پردازش کرده و پاسخی به شکل یک صفحه وب جدید برای او ارسال می‌کند. این در واقع همان فرم خواهد بود که به آن مبلغ مالیات قابل پرداخت اضافه شده است.
  • نوشتن «چسب» که همه این‌ها را با هم کار می‌اندازد.

تمام این کارها با استفاده از یک وب‌سایت جدید که با Visual Web Developer ایجاد شده است، انجام خواهد شد:

  • [1]: گزینهٔ File / New Web Site را انتخاب کنید
  • [2]: نوع برنامه «وب‌سایت» را انتخاب کنید (ASP.NET)
  • [3]: زبان توسعه را انتخاب کنید: C#
  • [4]: پوشه‌ای را که پروژه باید در آن ایجاد شود مشخص کنید
  • [5]: پروژه ایجاد شده در Visual Web Developer
    • [Default.aspx] یک صفحه وب است که به عنوان صفحه پیش‌فرض شناخته می‌شود. این صفحه‌ای است که در صورتی نمایش داده می‌شود که URL http://.../ClientAspImpot بدون مشخص کردن سند درخواست شود. این صفحه حاوی فرم محاسبه مالیات است که کاربر در مرورگر خود خواهد دید.
    • [Default.aspx.cs] کلاسی است که با این صفحه مرتبط است؛ این کلاس فرم ارسال‌شده به کاربر را تولید می‌کند و پس از تکمیل و ارسال آن توسط کاربر، آن را پردازش می‌کند.
    • [web.config] فایل پیکربندی برنامه است. برخلاف دفعات قبل، این فایل را نگه می‌داریم.

بازگشت به معماری که باید بسازیم:

  • [1] توسط [Default.aspx] پیاده‌سازی خواهد شد
  • [2] توسط [Default.aspx.cs] پیاده‌سازی خواهد شد
  • [3] توسط DLL و [ImpotV9-metier] پیاده‌سازی خواهد شد

بیایید با پیاده‌سازی لایه [3] شروع کنیم. چند مرحله وجود دارد:

  • در [1]، پوشه [lib] از کلاینت گرافیکی ویندوز نسخه ۹ به پوشه پروژه وب [ClientAspWsImpot] کپی می‌شود. این کار با استفاده از اکتشافگر ویندوز انجام می‌شود. برای اینکه این پوشه در راه‌حل Web Developer ظاهر شود، باید با استفاده از دکمه [2] راه‌حل را تازه‌سازی کنید.
  • سپس آن‌ها را به مراجع پروژه [3,4,5] اضافه کنید. DLLهای مرجع به طور خودکار به پوشه /bin پروژه [6] کپی می‌شوند.

اکنون ما DLLهای مورد نیاز برای عملکرد Spring را داریم و لایه کلاینت سرویس وب راه دور نیز پیاده‌سازی شده است. اگرچه کد مربوط به این بخش موجود است، اما هنوز باید پیکربندی شود. در نسخه ۹، این پیکربندی از طریق فایل زیر انجام می‌شد: [App.config]:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <configSections>
        <sectionGroup name="spring">
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
    </configSections>

    <spring>
        <context>
            <resource uri="config://spring/objects" />
        </context>
        <objects xmlns="http://www.springframework.net">
            <object name="metier" type="Metier.ImpotMetierWs, ImpotsV9-metier">
            </object>
        </objects>
    </spring>

    <!--سرویس وب -->
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
...
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:2172/WsImpot/ServiceImpot.asmx"
                    binding="basicHttpBinding" bindingConfiguration="ServiceImpotSoap"
                    contract="WsImpot.ServiceImpotSoap" name="ServiceImpotSoap" />
        </client>
    </system.serviceModel>
</configuration>

ما این پیکربندی را دقیقاً به همین شکل کپی کرده و آن را به صورت زیر در فایل [web.config] وارد می‌کنیم:


<?xml version="1.0"?>
<configuration>


    <configSections>
      <sectionGroup name="system.web.extensions"...>
...
      </sectionGroup>
      <!-- آغاز بخش بهار -->
        <sectionGroup name="spring">
          <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
          <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
      <!--پایان بخش بهار -->
    </configSections>

  <!-- آغاز پیکربندی بهار -->
  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object name="metier" type="Metier.ImpotMetierWs, ImpotsV9-metier">
      </object>
    </objects>
  </spring>
  <!--پایان پیکربندی بهار -->

  <!-- شروع پیکربندی کلاینت سرویس وب راه دور -->
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
...
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:2172/WsImpot/ServiceImpot.asmx"
                    binding="basicHttpBinding" bindingConfiguration="ServiceImpotSoap"
                    contract="WsImpot.ServiceImpotSoap" name="ServiceImpotSoap" />
    </client>
  </system.serviceModel>
  <!--پایان پیکربندی کلاینت سرویس وب راه دور -->

   <!-- سایر پیکربندی‌های موجود در web.config تولیدشده -->
...
</configuration>

توجه داشته باشید که خط ۳۷ به پورت سرویس وب راه دور اشاره دارد. این پورت ممکن است تغییر کند زیرا Visual Developer سرویس وب را روی یک پورت تصادفی راه‌اندازی می‌کند.

بیایید به معماری کلاینت وبی که باید بسازیم بازگردیم:

  • [1] توسط [Default.aspx] پیاده‌سازی خواهد شد
  • [2] توسط [Default.aspx.cs] پیاده‌سازی خواهد شد
  • [3] توسط DLL و [ImpotV9-metier] پیاده‌سازی شده است

ما به‌تازگی لایه [3] را پیاده‌سازی کرده‌ایم. اکنون به رابط وب [1] می‌رویم که توسط صفحه [Default.aspx] پیاده‌سازی شده است. برای رفتن به حالت طراحی، روی صفحه [Default.aspx] دوبار کلیک کنید.

دو روش برای ساخت یک صفحه وب وجود دارد:

  • به صورت گرافیکی، مانند [2]. برای این کار، حالت [Design] را در [1] انتخاب کنید. این نوار دکمه‌ها را می‌توان در پایین نوار وضعیت ویرایشگر صفحه وب یافت.
  • با استفاده از یک زبان نشانه‌گذاری، مانند [3]. سپس باید حالت [Source] را در [1] انتخاب کنید.

مودهای [Design] و [Source] دوطرفه هستند: تغییری که در مود [Design] ایجاد می‌شود، منجر به تغییری در مود [Source] می‌شود، و بالعکس. لطفاً توجه داشته باشید که فرم وبی که باید در مرورگر نمایش داده شود به شرح زیر است:

  • در [1]، فرم نمایش داده شده در یک مرورگر
  • در [2]، مؤلفه‌هایی که برای ساخت آن استفاده می‌شوند
  • [3]، صفحهٔ طراحی فرم. این صفحه شامل عناصر زیر است:
    • ردیف A، دو دکمه رادیویی به نام‌های RadioButtonOui و RadioButtonNon
    • ردیف B، یک فیلد ورودی به نام TextBoxEnfants و یک برچسب به نام LabelErreurEnfants
    • ردیف C، یک فیلد ورودی به نام TextBoxSalaire و یک برچسب به نام LabelErreurSalaire
    • ردیف D، یک برچسب با نام LabelImpot
    • ردیف E، دو دکمه با نام‌های ButtonCalculer و ButtonEffacer

پس از قرار دادن یک کامپوننت روی سطح طراحی، می‌توان به ویژگی‌های آن دسترسی داشت:

  • در [1]، دسترسی به ویژگی‌های یک کامپوننت
  • در [2]، برگهٔ ویژگی‌ها برای کامپوننت [LabelErreurEnfants ]
  • در [3]، (ID) نام کامپوننت است
  • در [4]، متن برچسب را به رنگ قرمز تنظیم کرده‌ایم.

تنها قرار دادن کامپوننت‌ها روی فرم و سپس تنظیم ویژگی‌های آن‌ها کافی نیست. چیدمان آن‌ها نیز باید سازماندهی شود. در رابط کاربری گرافیکی ویندوز، این چیدمان مطلق است: شما کامپوننت را به جایی که می‌خواهید، می‌کشید. در یک صفحه وب، این موضوع متفاوت است – پیچیده‌تر اما در عین حال قدرتمندتر. این جنبه در اینجا پوشش داده نخواهد شد.

کد منبع [Default.aspx] تولید شده توسط این طرح به شرح زیر است:


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_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">
  <title>Calculer votre impôt</title>
</head>
<body bgcolor="#ffff99">
  <h2>
    Calculer votre impôt</h2>
  <form id="form1" runat="server">
  <asp:ScriptManager ID="ScriptManager2" runat="server" EnablePartialRendering="true" />
  <asp:UpdatePanel runat="server" ID="UpdatePanelPam">
    <ContentTemplate>
      <div>
      </div>
      <table>
        <tr>
          <td>
            Etes-vous marié(e)
          </td>
          <td>
            <asp:RadioButton ID="RadioButtonOui" runat="server" GroupName="statut" Text="Oui" />
            <asp:RadioButton ID="RadioButtonNon" runat="server" GroupName="statut" Text="Non"
              Checked="True" />
          </td>
        </tr>
        <tr>
          <td>
            Nombre d&#39;children
          </td>
          <td>
            <asp:TextBox ID="TextBoxEnfants" runat="server" Columns="3"></asp:TextBox>
          </td>
          <td>
            <asp:Label ID="LabelErreurEnfants" runat="server" ForeColor="#FF3300"></asp:Label>
          </td>
        </tr>
        <tr>
          <td>
            Salaire annuel
          </td>
          <td>
            <asp:TextBox ID="TextBoxSalaire" runat="server" Columns="8"></asp:TextBox>
          </td>
          <td>
            <asp:Label ID="LabelErreurSalaire" runat="server" ForeColor="#FF3300"></asp:Label>
          </td>
        </tr>
        <tr>
          <td>
            Impôt à payer
          </td>
          <td>
            <asp:Label ID="LabelImpot" runat="server" BackColor="#99CCFF"></asp:Label>
          </td>
        </tr>
      </table>
      <br />
      <table>
        <tr>
          <td>
            <asp:Button ID="ButtonCalculer" runat="server" Text="Calculer" OnClick="ButtonCalculer_Click" />
          </td>
          <td>
            <asp:Button ID="ButtonEffacer" runat="server" Text="Effacer" OnClick="ButtonEffacer_Click" />
          </td>
          <td>
            &nbsp;
          </td>
        </tr>
      </table>
      </div>
    </ContentTemplate>
  </asp:UpdatePanel>
  </form>
</body>
</html>

کامپوننت‌های فرم را می‌توان در خطوط ۲۳، ۲۴، ۳۳، ۳۶، ۴۴، ۴۷، ۵۵، ۶۳ و ۶۶ شناسایی کرد. بقیه اساساً مربوط به قالب‌بندی است.

بیایید به معماری‌ای که باید بسازیم بازگردیم:

  • [1] توسط [Default.aspx] پیاده‌سازی شده است
  • [2] توسط [Default.aspx.cs] پیاده‌سازی خواهد شد
  • [3] توسط DLL و [ImpotV9-metier] پیاده‌سازی شده است

لایه‌های [1] و [3] اکنون پیاده‌سازی شده‌اند. ما هنوز باید لایه [2] را بنویسیم که فرم را تولید می‌کند، آن را برای کاربر ارسال می‌کند، وقتی کاربر آن را تکمیل‌شده بازمی‌گرداند آن را پردازش می‌کند، از لایه [3] برای محاسبه مالیات استفاده می‌کند، صفحه وب حاوی پاسخ را برای کاربر تولید می‌کند و آن را برایشان بازمی‌فرستد. این کد [Default.aspx.cs] است که تمام این کارها را انجام می‌دهد:


using System;
using WsImpot;

public partial class _Default : System.Web.UI.Page
{
    protected void ButtonCalculer_Click(object sender, EventArgs e)
    {
  ...
    }
    protected void ButtonEffacer_Click(object sender, EventArgs e)
    {
...
    }
}

این کد بسیار شبیه به کد یک فرم استاندارد ویندوز است. این بزرگترین مزیت فناوری ASP.NET است: هیچ گسستی بین مدل برنامه‌نویسی ویندوز و برنامه‌نویسی وب ASP.NET وجود ندارد. شما فقط باید همیشه ساختار زیر را در نظر داشته باشید:

وقتی در [1] کاربر روی دکمه [Calculer] کلیک می‌کند، رویه ButtonCalculer_Click در خط ۶ از [Default.aspx.cs] اجرا خواهد شد. اما در این میان:

  • مقادیر فرم تکمیل‌شده از طریق پروتکل HTTP از مرورگر به سرور وب ارسال خواهند شد
  • سرور ASP.NET درخواست را تحلیل کرده و آن را به صفحه [Default.aspx] فوروارد می‌کند
  • صفحه [Default.aspx] نمونه برداری خواهد شد.
  • اجزای آن (RadioButtonOui, RadioButtonNon, TextBoxEnfants, TextBoxSalaire, LabelErreurEnfants, LabelErreurSalaire, LabelImpot) با مقادیری که هنگام ارسال اولیه فرم به مرورگر داشتند، با استفاده از مکانیزمی به نام «ViewState» مقداردهی اولیه خواهند شد.
  • مقادیر ارسال‌شده به اجزای متناظر خود (RadioButtonOui, RadioButtonNon, TextBoxEnfants, TextBoxSalaire) تخصیص داده خواهند شد. بنابراین، اگر کاربر عدد ۲ را به عنوان تعداد فرزندان وارد کرده باشد، خواهیم داشت TextBoxEnfants.Text="2".
  • اگر صفحه [Default.aspx] دارای متد [Page_Load] باشد، این متد اجرا خواهد شد
  • روش [ButtonCalculer_Click] در خط 6 اجرا خواهد شد اگر دکمه [Calculer] کلیک شده باشد
  • روش [ButtonEffacer_Click] در خط ۱۰ اجرا خواهد شد اگر دکمه [Effacer] کلیک شده باشد

بین لحظه‌ای که کاربر یک رویداد را در مرورگر خود فعال می‌کند و لحظه‌ای که در [Default.aspx.cs] پردازش می‌شود، پیچیدگی زیادی وجود دارد. این پیچیدگی پنهان است و هنگام نوشتن رویدادپردازها برای صفحه وب می‌توان آن را طوری در نظر گرفت که گویی وجود ندارد. با این حال، هرگز نباید فراموش کرد که شبکه بین رویداد و رسیدگی‌کننده آن قرار دارد، و بنابراین رسیدگی به رویدادهای ماوس مانند Mouse_Move، که منجر به رفت‌وآمدهای پرهزینه کلاینت-سرور می‌شود، منتفی است...

کد رویدادگیرهای کلیک برای دکمه‌های [Calculer] و [Effacer] همان چیزی است که برای یک برنامهٔ سنتی ویندوز می‌نویسیم:


protected void ButtonCalculer_Click(object sender, EventArgs e)
    {
        // تأیید داده‌ها
        int nbEnfants;
        bool erreur = false;
        if (!int.TryParse(TextBoxEnfants.Text.Trim(), out nbEnfants) || nbEnfants < 0)
        {
            LabelErreurEnfants.Text = "Valeur incorrecte...";
            erreur = true;
        }
        int salaire;
        if (!int.TryParse(TextBoxSalaire.Text.Trim(), out salaire) || salaire < 0)
        {
            LabelErreurSalaire.Text = "Valeur incorrecte...";
            erreur = true;
        }
        // خطا؟
        if (erreur) return;
        // هرگونه خطا پاک می‌شود
        LabelErreurEnfants.Text = "";
        LabelErreurSalaire.Text = "";
        //وضعیت تأهل
        bool marié = RadioButtonOui.Checked;
        // محاسبه مالیات
        try
        {
            LabelImpot.Text = String.Format("{0} euros",Global.Metier.CalculerImpot(marié, nbEnfants, salaire));
        }
        catch (Exception ex)
        {
            LabelImpot.Text = ex.Message;
        }
    }
  • برای درک این کد، باید بدانید
    • که در ابتدای اجرای آن، فرم [Default.aspx] در وضعیتی است که کاربر آن را پر کرده است. بنابراین، فیلدها (RadioButtonOui, RadioButtonNon, TextBoxEnfants, TextBoxSalaire) حاوی مقادیری هستند که کاربر وارد کرده است.
    • و اینکه پس از اجرای آن، همان صفحه [Default.aspx] به کاربر بازگردانده می‌شود. این کار به‌طور خودکار انجام می‌شود.

بنابراین رویه ButtonCalculer_Click باید بر اساس مقادیر فعلی فیلدها (RadioButtonOui, RadioButtonNon, TextBoxEnfants, TextBoxSalaire) برای تعیین مقادیر تمام فیلدها (RadioButtonOui, RadioButtonNon, TextBoxEnfants, TextBoxSalaire, LabelErreurEnfants, LabelErreurSalaire, LabelImpot) از صفحه جدید [Default.aspx] که به کاربر بازگردانده خواهد شد.

در این کد مشکل خاصی وجود ندارد. تنها خط ۲۷ نیاز به توضیح دارد. این خط از متد CalculerImpot برای فیلدی به نام Global.Metier استفاده می‌کند که هنوز با آن مواجه نشده‌ایم. به زودی به این موضوع باز خواهیم گشت.

روش ButtonEffacer_Click به شرح زیر است:


    protected void ButtonEffacer_Click(object sender, EventArgs e)
    {
        // بازنشانی فرم
        TextBoxEnfants.Text = "";
        TextBoxSalaire.Text = "";
        LabelImpot.Text = "";
        LabelErreurEnfants.Text = "";
        LabelErreurSalaire.Text = "";
}

بیایید به معماری‌ای که باید بسازیم بازگردیم:

  • [1] توسط [Default.aspx] پیاده‌سازی شده است
  • [2] توسط [Default.aspx.cs] پیاده‌سازی شد
  • [3] توسط DLL و [ImpotV9-metier] پیاده‌سازی شده است

تنها کاری که باقی مانده افزودن «چسب» بین این سه لایه است. اساساً این شامل موارد زیر است:

  • ایجاد لایه [3] هنگام راه‌اندازی برنامه
  • قرار دادن مرجعی به آن در مکانی که صفحه وب [Default.aspx.cs] بتواند هر زمان که این لایه ایجاد شد و از آن خواسته شد مالیات را محاسبه کند، آن را بازیابی نماید.

این یک مشکل جدید نیست. این مشکل قبلاً در حین توسعه سرویس وب راه دور با آن مواجه شده و در بخش 12.4.1 مورد بحث قرار گرفته است. ما می‌دانیم که راه‌حل شامل موارد زیر است:

  • ایجاد یک فایل به نام [Global.asax] که با کلاسی به نام [Global.cs] مرتبط است
  • برای نمونه‌سازی لایه [3] در متد Application_Start از کلاس [Global.cs]
  • برای قرار دادن مرجع لایه [3] در یک فیلد ایستا از کلاس [Global.cs]، زیرا چرخه عمر این کلاس، چرخه عمر خود برنامه است.

در نتیجه، پروژه وب ما به شرح زیر در حال تکامل است:

  • به [1]، فایل [Global.asax].
  • به [2]، با کد مرتبط [Global.cs]. پوشه [App_Code] که حاوی این فایل است، به طور پیش‌فرض در راه‌حل وب وجود ندارد. برای ایجاد آن از [3] استفاده کنید.

فایل Global.asax به شرح زیر است:


<%@ Application Language="C#" Inherits="WsImpot.Global"%>

کد [Global.cs] به شرح زیر است:


using System;
using Metier;
using Spring.Context.Support;
namespace WsImpot
{
    public class Global : System.Web.HttpApplication
    {
        // لایه کسب‌وکار
        public static IImpotMetier Metier;

        // روش اجرا شده در شروع به کار برنامه
        private void Application_Start(object sender, EventArgs e)
        {
            // نمونه سازی لایه‌ها [metier] و [dao]
            Metier = ContextRegistry.GetContext().GetObject("metier") as IImpotMetier;
        }
    }
}
  • خط ۶: کلاس Global نام دارد و بخشی از فضای نام WsImpot (خط ۴) است. بنابراین، نام کامل آن WsImpot.Global است و این نامی است که باید در ویژگی Inherits از Global.asax وارد شود.
  • خط ۶: می‌دانیم که کلاسی که با Global.asax مرتبط است باید از کلاس System.Web.HttpApplication ارث ببرد.
  • خط ۱۲: متد Application_Start هنگام راه‌اندازی برنامه وب اجرا می‌شود.
  • خط ۱۵: لایه [metier] (لایه [3] از برنامه‌ای که در حال توسعه است) با استفاده از Spring و پیکربندی زیر در [web.config] ایجاد می‌شود:

  <!-- ابجکت‌های Spring -->
  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object name="metier" type="Metier.ImpotMetierWS, ImpotsV9-metier">
      </object>
    </objects>
</spring>

کلاس [Metier.ImpotMetierWS] از خط (g) بالا در [ImpotsV9-metier.dll] قرار دارد.

اشاره به لایه ایجادشده [metier] در فیلد ثابت در خط ۹ قرار داده شده است. این فیلد است که در خط ۲۷ رویه ButtonCalculer_Click استفاده می‌شود:


LabelImpot.Text = String.Format("{0} euros",Global.Metier.CalculerImpot(marié, nbEnfants, salaire));

ما برای آزمایش آماده‌ایم. SGBD و MySQL5، سرویس وب راه دور را اجرا کنید و پورتی را که روی آن در حال اجراست، یادداشت کنید:

  

پس از انجام این کار، بررسی کنید که پورت سرویس وب راه دور در فایل [web.config] کلاینت وب صحیح باشد:

پس از انجام این کار، می‌توان کلاینت وب سرویس راه دور را با فشردن Ctrl-F5 راه‌اندازی کرد:

  

12.6. یک کلاینت کنسول جاوا برای سرویس وب محاسبه مالیات

برای نشان دادن اینکه سرویس‌های وب برای کلاینت‌های نوشته شده به هر زبانی قابل دسترسی هستند، یک کلاینت کنسول پایه جاوا خواهیم نوشت. معماری برنامه کلاینت/سرور به شرح زیر خواهد بود:

  • کلاینت [1] به زبان جاوا نوشته خواهد شد
  • سرور [2] به زبان C# نوشته شده است

ابتدا قصد داریم جزئیاتی را در سرویس وب محاسبه مالیات خود تغییر دهیم. تعریف فعلی آن در [ServiceImpot.cs] به شرح زیر است:


...
public class ServiceImpot : System.Web.Services.WebService
{

    [WebMethod]
    public int CalculerImpot(bool marié, int nbEnfants, int salaire)
    {
        return Global.Metier.CalculerImpot(marié, nbEnfants, salaire);
    }

}

آزمایش‌ها نشان داده‌اند که تأکید بر پارامتر marié در خطوط ۶ و ۸ می‌تواند باعث مشکل در تعامل‌پذیری Java/C# شود. ما تعریف جدید زیر را اتخاذ خواهیم کرد:


...
public class ServiceImpot : System.Web.Services.WebService
{

    [WebMethod]
    public int CalculerImpot(bool marie, int nbEnfants, int salaire)
    {
        return Global.Metier.CalculerImpot(marie, nbEnfants, salaire);
    }

}

این سرویس در یک پروژه جدید Web Developer به نام WsImpotsSansAccents قرار داده خواهد شد. سپس سرویس وب دارای URL [/WsImpotSansAccents/ServiceImpot.asmx] خواهد بود.

Image

برای نوشتن کلاینت جاوا، از NetBeans استفاده خواهیم کرد:

  • در [1]، یک پروژه جدید ایجاد کنید
  • در [2,3]، یک پروژه Java از نوع Java Application انتخاب کنید.
  • در [4]، به مرحله بعدی بروید
  • در [5]، برای پروژه نام‌گذاری کنید
  • در [6]، پوشه‌ای را مشخص کنید که زیرپوشه‌ای با نام پروژه در آن ایجاد شود
  • در [7]، نامی برای کلاسی که متد main را در هنگام شروع برنامه اجرا می‌کند، تعیین کنید
  • در [8]، جادوگر را تکمیل کنید
  • در [9]: پروژهٔ جاوای تولیدشده
  • در [10]: روی پروژه کلیک راست کرده و کلاینت سرویس وب محاسبه مالیات را تولید کنید
  • به [11]، آدرس اینترنتی (URL) فایلی که سرویس وب محاسبه مالیات را توصیف می‌کند:

http://localhost:1089/WsImpotSansAccents/ServiceImpot.asmx?WSDL

این URL مربوط به سرویس [ServiceImpot.asmx] است که پارامتر ؟WSDL به آن اضافه شده است. سند موجود در این URL به زبان XML شرح می‌دهد که سرویس [15] چه قابلیت‌هایی دارد. این یک جزء استاندارد از یک سرویس وب است.

  • در [12]، بسته‌ای (معادل namespace در C#) که کلاس‌های تولیدشده در آن قرار می‌گیرند
  • در [13]، مقدار پیش‌فرض را بدون تغییر باقی بگذارید
  • در [14]، جادوگر را تکمیل کنید
  • در [16]، سرویس وب واردشده در پروژهٔ جاوا یکپارچه شده است. این سرویس از دو پروتکل ارتباطی پشتیبانی می‌کند: Soap و Soap12.
  • در [17]، کلاس [Main] که در آن از کلاینت تولیدشده استفاده خواهیم کرد
  • در [18]، ما کد را در متد [main] وارد خواهیم کرد. مکان نما را در جایی که کد باید وارد شود قرار دهید، کلیک راست کرده و گزینه [19] را انتخاب کنید
  • در [20]، مشخص کنید که می‌خواهید کد فراخوانی برای تابع CalculerImpot سرویس محاسبه مالیات از راه دور را تولید کنید، سپس روی OK کلیک کنید.

کد تولید شده در [Main] به شرح زیر است:

public class Main {

    public static void main(String[] args) {
         // TODO کد منطق برنامه در اینجا
      try { //فراخوانی عملیات سرویس وب
        wsimpot.ServiceImpot service = new wsimpot.ServiceImpot();
        wsimpot.ServiceImpotSoap port = service.getServiceImpotSoap();
         // TODO پارامترهای عملیات WS را اینجا مقداردهی اولیه کنید
        boolean marie = false;
        int nbEnfants = 0;
        int salaire = 0;
         // TODO نتیجه را اینجا پردازش کنید
        int result = port.calculerImpot(marie, nbEnfants, salaire);
        System.out.println("Result = "+result);
      } catch (Exception ex) {
         // TODO خطاهای سفارشی را اینجا مدیریت کنید
      }
    }
}

کد تولید شده نشان می‌دهد که چگونه تابع CalculerImpot سرویس محاسبه مالیات از راه دور را فراخوانی کنیم. با توجه به مشابهتی که با آنچه در C# دیده‌ایم وجود دارد، متغیر port در خط ۷ معادل کلاینت (client) مورد استفاده در C# است. ما در مورد این کد توضیح بیشتری ارائه نخواهیم داد. ما آن را به شکل زیر بازسازی خواهیم کرد:

import wsimpot.ServiceImpot;
public class Main {
    public static void main(String[] args) {
      try {
         //تابع CalculerImpot سرویس وب فراخوانی می‌شود
        System.out.println(String.format("Montant à payer : %d euros", new ServiceImpot().getServiceImpotSoap().calculerImpot(true, 2, 60000)));
      } catch (Exception ex) {
        System.out.println(String.format("L'erreur suivante s'est produite %s",ex.getMessage()));
      }
    }
}
  • خط ۱: ما کلاس ServiceImpot را وارد می‌کنیم که نمایانگر کلاینت تولید شده توسط ویزارد است.
  • خط ۶: ما متد راه دور CalculerImpot را فراخوانی می‌کنیم، مطابق روندی که در کد تولیدشده در `main` مشخص شده است.

خروجی‌های نمایش‌داده‌شده در کنسول پس از اجرا (F6) به شرح زیر است:

init:
deps-jar:
wsimport-init:
wsimport-client-check-ServiceImpot.asmx:
wsimport-client-ServiceImpot.asmx:
wsimport-client-generate:
wsimport-client-compile:
Compiling 1 source file to C:\data\2007-2008\netbeans\ClientNetbeansPourServiceImpotDotNet\build\classes
compile:
run:
Montant à payer : 4282 euros
BUILD SUCCESSFUL (total time: 7 seconds)