ในเมื่อ Microsoft ออกแบบ Web Service ให้อิงกับ Web Server ของ Microsoft ซึ่งก็คือ IIS นั้น ความสามารถต่างๆ มันจึงถูกจำกัดโดยความสามารถของ IIS ด้วย เรามาลองดูตัวอย่างนี้กัน
สมมุติว่าคุณต้องการเขียน Web Service ทำหน้าที่เป็น Server ของเกมส์ทายตัวเลข Server จะ Random ค่าหนึ่งเก็บไว้ และโปรแกรมของฝั่ง client จะทำการทายตัวเลขต่างๆ ถ้าถูกก็บอกว่าถูก แต่ถ้าผิดก็จะตอบกลับไปว่ามากไป หรือน้อยไป ทั้งนี้ Server ก็เก็บค่าไว้ด้วยว่าผู้เล่นได้ทายไปกี่ครั้งแล้ว เกมส์แบบนี้เป็นเกมส์ที่ผมหัดเขียนเป็นโปรแกรมแรกๆ ในชีวิตเมื่อเกือบ 20 ปีที่ผ่านมา
ผมก็เลขเขียน Web Service Class แบบนี้ครับ มาช่วยกันดูหน่อย
|
sguess.asmx |
<%@ WebService Language="C#" class="SGuess" %> using System; using System.Web.Services; public class SGuess : WebService
{
private int ptimes;
private int pnumber;
public enum GUESS_TYPE {
TooLow,
TooHigh,
Correct
};
[WebMethod]
public void Reset(int max)
{
Random rand = new Random();
ptimes = 0;
pnumber = rand.Next() % max + 1;
}
[WebMethod]
public GUESS_TYPE Play(int num)
{
++ptimes;
if (pnumber == num) {
return GUESS_TYPE.Correct;
} else if (pnumber < num) {
return GUESS_TYPE.TooHigh;
} else {
return GUESS_TYPE.TooLow;
}
}
[WebMethod]
public int Times() {
return ptimes;
}
}
|
ลองเอา file นี้เก็บบน web server IIS 5 ที่มี .Net Framework Install อยู่ แล้วลองดูว่า เรียกใช้ file นี้ดู ดังนี้ http://localhost/sguess.asmx ถ้าไม่มี error มันจะแสดงวิธีใช้ ทั้งหมดมีอยู่ 3 methods มีคำอธิบายดังนี้
Reset()
เป็น method
เอาไว้สำหรับเพื่อเริ่มเกมส์ใหม่
มันจะ set
จำนวนครั้งเป็น
0
และสร้างค่า
random ค่าใหม่
Play()
ให้เราใส่ค่าเข้าไปค่าหนึ่ง
มันจะเอาไปเปรียบเทียบแล้ว
return กลับมาว่า
ถูกต้องแล้ว
มากไป
หรือน้อยไป
และยังเพิ่มค่าจำนวนครั้งในการทายขึ้นอีกหนึ่งด้วย
Times()
return
จำนวนครั้งในการทายเกมส์ล่าสุด
คุณสามารถลองเล่นโดยการเรียกใช้ click link ในหน้า web page sguess.asmx หรือคุณสามารถสร้าง proxy client ดังนี้
|
DOS Prompt |
| C:\cs>webserviceutil /pa:http://localhost/sguess.asmx?sdl /protocol:soap
/out:sgu ess.cs /command:proxy Microsoft (R) Web Services Utility [Microsoft .NET Framework Version 1.0.2204.21] Copyright (C) Microsoft Corp 2000. All rights reserved. sguess.cs C:\cs> _ |
และเอาไปใช้กับ client class ดังนี้
|
cguess.asmx |
using System; class CGuess
{
public static void Main()
{
int number;
GUESS_TYPE result;
string str;
SGuess guess = new SGuess(); do {
guess.Reset(1000);
do {
Console.Write("Guess a number between 1 to 1000? ");
number = Console.ReadLine().ToInt32();
result = guess.Play(number);
if (result == GUESS_TYPE.Correct) {
Console.WriteLine("Correct");
} else if (result == GUESS_TYPE.TooLow) {
Console.WriteLine("Too Low");
} else {
Console.WriteLine("Tow High");
}
} while (result != GUESS_TYPE.Correct);
Console.WriteLine("You guess in {0} times", guess.Times());
Console.Write("Try Again (y/n)? ");
str = Console.ReadLine();
} while (str[0] == 'y' || str[0] == 'Y');
Console.WriteLine("Good bye!");
}
}
|
compile โปรแกรมทั้งสองรวมกัน และ run ดังนี้ครับ
|
DOS Prompt |
| C:\cs>csc /out:guess.exe cguess.cs sguess.cs /r:system.xml.serialization.dll;sy tem.web.services.dll Microsoft (R) Visual C# Compiler Version 7.00.9030 [CLR version 1.00.2204.21] Copyright (C) Microsoft Corp 2000. All rights reserved. C:\cs>guess Guess a number between 1 to 1000? 500 Tow High Guess a number between 1 to 1000? 250 Tow High Guess a number between 1 to 1000? 125 Tow High Guess a number between 1 to 1000? 75 Tow High Guess a number between 1 to 1000? 30 Tow High Guess a number between 1 to 1000? 15 Tow High Guess a number between 1 to 1000? 5 Tow High Guess a number between 1 to 1000? 0 Correct You guess in 0 times Try Again (y/n)? y Guess a number between 1 to 1000? 0 Correct You guess in 0 times Try Again (y/n)? n C:\cs> _ |
จะเห็นได้ว่าไม่ว่าคุณเรียกใช้งาน Web Method ผ่าน Browser หรือต่อกับ client โดยใช้ WebServiceUtil.exe มัน Random ค่าออกมาได้ 0 เสมอ ซ้ำร้ายจำนวนครั้งในการทาย ก็เป็น 0 ด้วย มันเกิดอะไรขึ้น
เรื่องนี้ตอนแรกผมก็มึนเหมือนกัน เลยต้องถามอ้อ ปาริชาต ก็ได้คำตอบมาว่าปัญหาเรื่องนี้มันเกิดจากคำว่า stateless คือ IIS มันไม่เก็บสถานะใดๆ ของ process ที่มันใช้รับการติดต่อจาก browser ของคุณ นั่นก็หมายความว่า เมื่อคุณเรียก web method ใดๆ มาใช้ เมื่อมันทำงานเสร็จ method นั้น มันก็จะตายไป เมื่อคุณเรียกใช้อีก 1 method ถึงแม้ว่าจะเป็น browser ใน session เดิม มันก็จะ new() class นั้นใหม่อยู่ดี ทำให้ตัวแปรที่เป็น Local ทั้งหมดใช้งานไม่ได้ ทางออกที่อ้อแนะนำคือ เอาค่า local ของเราไปฝากไว้เป็นตัวแปร session เหมือนกับที่ทำกับพวก Active Server Page (ASP) แต่คุณบอก Web Service ให้รองรับ session ด้วยโดยการเพิ่ม attribute ผลลัพธ์ของ sguess.asmx จึงเป็นดังนี้ครับ
|
sguess.asmx |
<%@ WebService Language="C#" class="SGuess" %> using System; using System.Web.Services; public class SGuess : WebService
{
public enum GUESS_TYPE {
TooLow,
TooHigh,
Correct
};
[WebMethod(Description="Reset Everything to Zero", EnableSession=true)]
public void Reset(int max)
{
Random rand = new Random();
Session["ptimes"] = 0;
Session["pnumber"] = rand.Next() % max + 1;
}
[WebMethod(EnableSession=true)]
public GUESS_TYPE Play(int num)
{
Session["ptimes"] = (int)Session["ptimes"] + 1;
if ((int)Session["pnumber"] == num) {
return GUESS_TYPE.Correct;
} else if ((int)Session["pnumber"] < num) {
return GUESS_TYPE.TooHigh;
} else {
return GUESS_TYPE.TooLow;
}
}
[WebMethod(EnableSession=true)]
public int Times()
{
return (int)Session["ptimes"];
}
}
|
จะเห็นได้ว่าจาก attribute [WebMethod] กลายเป็น [WebMethod(EnableSession=true)] ยิ่งเป็น WebMethod ของ Reset ยังใส่ Description ด้วยจะได้แสดงเมื่อเราเรียก browser ไปดู sguess.asmx
เมื่อเราบอกให้แต่ละ method รองรับ Session แล้วเราก็ใช้ตังแปร Session เหมือนใน ASP เพื่อเก็บค่าต่างๆ ตัวแปร Session นั้นไม่ได้เก็บข้อมูลได้เฉพาะตัวแปรธรรมดาเท่านั้นนะครับ object ก็เก็บได้ หรือข้อมูลทั้ง database เช่น dataset ของ ADO.Net ก็เก็บได้เช่นกัน
คราวนี้คุณลองทดสอบโปรแกรมผ่าน browser ดูว่ามันทำงานถูกต้องแล้วหรือยัง ถ้าถูกแล้วเรามาลองทำ client ใหม่ดังนี้
|
DOS Prompt |
| C:\cs>webserviceutil /pa:http://localhost/sguess.asmx?sdl /protocol:soap
/out:sgu ess.cs /command:proxy Microsoft (R) Web Services Utility [Microsoft .NET Framework Version 1.0.2204.21] Copyright (C) Microsoft Corp 2000. All rights reserved. sguess.cs C:\cs>csc /out:guess.exe cguess.cs sguess.cs /r:system.xml.serialization.dll;sy tem.web.services.dll Microsoft (R) Visual C# Compiler Version 7.00.9030 [CLR version 1.00.2204.21] Copyright (C) Microsoft Corp 2000. All rights reserved. C:\cs>guess Guess a number between 1 to 1000? 500 Tow High Guess a number between 1 to 1000? 250 Too Low Guess a number between 1 to 1000? 375 Tow High Guess a number between 1 to 1000? 315 Too Low Guess a number between 1 to 1000? 340 Too Low Guess a number between 1 to 1000? 355 Tow High Guess a number between 1 to 1000? 350 Too Low Guess a number between 1 to 1000? 353 Tow High Guess a number between 1 to 1000? 352 Tow High Guess a number between 1 to 1000? 351 Correct You guess in 10 times Try Again (y/n)? n Good bye! C:\cs> |
มันทำงานถูกแล้ว แต่คุณอาจจะไม่ค่อยชอบมาทำไมต้องเป็นแบบนี้ ผมเองก็ไม่ชอบเหมือนกัน แต่ในเมื่อมันอิงเข้ากับ Web Server มันก็ต้องจำกัดด้วยความสามารถของ Web Server รอดูใน version ต่อไปว่าจะมีอะไรพัฒนากว่านี้อีก
สรุปข้อจำกัดของ Web Service
ไม่สามารถใช้ตัวแปร local ได้ ถ้าจำเป็นต้องใช้ให้หลีกเลี่ยงไปใช้ตัวแปร session แทน
ไม่รองรับ constructor ที่มี parameter
ไม่รองรับ การสร้างตัวแปรลักษณะ property ต้องใช้เป็น Method เท่านั้น