עד עכשיו כתבתי על SOAP והגיע הזמן להכיר את השיטה המתחרה REST - representational state transfer , ההבדל העיקרי הוא ש SOAP הוא פרוטוקול, יש בו חוקים ברורים שבעזרתם עוטפים את המידע שברצוננו לשלוח, ב REST העסק שונה, REST היא ארכיטקטורה שמתבססת על HTTP Protocol וכל אחד יכול לממש אותה אחרת.
ב SOAP אנחנו עובדים מול קובץ WSDL שמכיל בתוכו את המעטפת (אלמנטים וקריאות לפונקציות) שמאוד קשה לתחזק וכשמדובר על Service מורכב העסק עלול להפוך למסורבל, REST נותן פתרון פשוט שמתבסס על HTTP ,בעזרת פונקציות שקיימות בפרוטוקול (Get,Put,Post,Delete) ננתב את הבקשות.
שימו לב! המאמר מציג שיטה למימוש Rest ע"ג IIS 7.
שלב א - צד השרת:
תחילה נבנה פרויקט חדש של Web Site ב Visual Studio.
ניצור Class חדש שיורש את ה Interface של IHttpHandler , עלינו לממש את המאפיין IsReusable ופונקציית ProcessRequest כפי שניתן לראות בדוגמה הבאה:
public class Service: IHttpHandler
{
bool IHttpHandler.IsReusable
{
get { return false; }
}
void IHttpHandler.ProcessRequest(HttpContext context)
{
//implementation of request by method...
}
}
CURD (create , update, read, delete) Method
פרוטוקל HTTP מכיל בתוכו פונקציות מובנות, מי שבונה אתרי אינטרנט מכיר בעיקר את ה GET ו POST המפורסמים שמאפשרים לנו להעביר מידע בין טפסים, אבל קיימות פונקציות נוספות כמו PUT ו DELETE שבעזרתם ניתן לנתב את הבקשות ל Service שלנו.
GET
בקשות למידע, לדוגמה קריאות מה Database.
POST
הוספת אובייקט ל Database בעזרת המידע שנשלח ב Body.
PUT
דומה ל POST , עדכון אובייקט קיים בעזרת המידע שנשלח ב Body.
DELETE
מחיקת אובייקט
כשנשלחת בקשת GET אנחנו פונים ל Database ומחזירים את הנתונים ללקוח על בסיס פרמטרים שעוברים ב Url, כאשר הלקוח שולח בקשת Post יוצרים אובייקט חדש ב Database שאת המידע אנחנו אוספים מה Body של החבילה.
כאשר מתבצעת קריאה ל Service הפונקציה Process Request היא בעצם ה Entry Point וכל בקשה תפעיל את הפונקציה הזאת ודרכה ננתב את הבקשות של המשתמש למשאבים המתאימים כפי שניתן לראות בדוגמה הבאה:
{
try
{
string url = Convert.ToString(_context.Request.Url);
//handling CURD (CREATE UPDATE READ DELETE)
switch (_context.Request.HttpMethod)
{
case "GET":
//return data from database
READ(_context);
break;
case "POST":
//create new element in database
CREATE(_context);
break;
case "PUT":
//update element in database
UPDATE(_context);
break;
case "DELETE":
//delete element from the database
DELETE(_context);
break;
default:
break;
}
}
catch (Exception ex)
{ _context.Response.Write(ex.Message);}
}
אחרי שלכדנו את השם של הפונקציה שנשלחה מהמשתמש , עלינו לנתב את הבקשה לפונקציה המתאימה, נבנה 4 פונקציות נוספות ב Class:
//getting object from request using GET method
private void READ(HttpContext _context)
{
string serialized = "";
//getting guides parameters from request
if (_context.Request["type"].ToString() == "person")
{
//properly the point to call database
//get data and parse to xml back to client
person _person = new person();
_person.FirstName = "HELLO";
_person.LastName = "WORLD";
serialized = Serialize(_person);
}
//depend what language you want to use
_context.Response.ContentType = "text/xml";
HttpContext.Current.Response.Write(serialized);
}
//insert new object using the POST method
private void CREATE(HttpContext _context)
{
//reading the body of the message
byte[] bodybytes = _context.Request.BinaryRead(_context.Request.ContentLength);
//convert the bytes array to string as utf8
string str = Encoding.UTF8.GetString(bodybytes);
// deserialize xml into person
person _person = person.deSerialize(bodybytes);
//insert database...
}
//update object using the POST method
private void UPDATE(HttpContext _context)
{
//reading the body of the message
byte[] bodybytes = _context.Request.BinaryRead(_context.Request.ContentLength);
//convert the bytes array to string as utf8
string str = Encoding.UTF8.GetString(bodybytes);
// deserialize xml into person
person _person = person.deSerialize(bodybytes);
//update database...
}
//delete element using the DELETE method
private void DELETE(HttpContext _context)
{
if (_context.Request["type"].ToString() == "person")
{
if (_context.Request["id"] != null)
{
int i = 0;
if (int.TryParse(_context.Request["id"].ToString(), out i))
{
//continue to database...
}
}
}
}
אחד היתרונות ב REST שניתן לעבוד עם סוגי מבנים שונים, בניגוד ל SOAP שמבוסס על XML ב REST אפשר לבחור מה שבא לנו XML , JSON או להמציא בעצמנו את המבנה, במאמר זה נעבוד עם XML , לכן חשוב לייצר 2 פונקציות נוספות שימירו לנו את המידע מ XML לאובייקט .Net ומ .Net ל XML.
קוד מלא:
using System.Collections.Generic;
using System.Web;
using System.Xml.Serialization;
using System.IO;
using System.Text;
using System.Xml;
namespace restLand
{
public class rest : IHttpHandler
{
bool IHttpHandler.IsReusable
{
get { return false; }
}
void IHttpHandler.ProcessRequest(HttpContext _context)
{
try
{
string url = Convert.ToString(_context.Request.Url);
//handling CURD (CREATE UPDATE READ DELETE)
switch (_context.Request.HttpMethod)
{
case "GET":
//return data from database
READ(_context);
break;
case "POST":
//create new element in database
CREATE(_context);
break;
case "PUT":
//update element in database
UPDATE(_context);
break;
case "DELETE":
//delete element from the database
DELETE(_context);
break;
default:
break;
}
}
catch (Exception ex)
{ _context.Response.Write(ex.Message);}
}
//getting object from request using GET method
private void READ(HttpContext _context)
{
string serialized = "";
//getting guides parameters from request
if (_context.Request["type"].ToString() == "person")
{
//properly the point to call database
//get data and parse to xml back to client
person _person = new person();
_person.FirstName = "HELLO";
_person.LastName = "WORLD";
serialized = Serialize(_person);
}
//depend what language you want to use
_context.Response.ContentType = "text/xml";
HttpContext.Current.Response.Write(serialized );
}
//insert new object using the POST method
private void CREATE(HttpContext _context)
{
//reading the body of the message
byte[] bodybytes = _context.Request.BinaryRead(_context.Request.ContentLength);
//convert the bytes array to string as utf8
string str = Encoding.UTF8.GetString(bodybytes);
// deserialize xml into person
person _person = person.deSerialize(bodybytes);
//insert database...
}
//update object using the POST method
private void UPDATE(HttpContext _context)
{
//reading the body of the message
byte[] bodybytes = _context.Request.BinaryRead(_context.Request.ContentLength);
//convert the bytes array to string as utf8
string str = Encoding.UTF8.GetString(bodybytes);
//deserialize xml into person
person _person = person.deSerialize(bodybytes);
//update database...
}
//delete element using the DELETE method
private void DELETE(HttpContext _context)
{
if (_context.Request["type"].ToString() == "person")
{
if (_context.Request["id"] != null)
{
int i = 0;
if (int.TryParse(_context.Request["id"].ToString(), out i))
{
//continue to database...
}
}
}
}
private String Serialize(object _class)
{
try
{
//output string
String xmlstring = null;
//parser to xml from object using polymorphism
XmlSerializer seriailizor = null;
if (_class.GetType().ToString() == "restLand.person")
seriailizor = new XmlSerializer(typeof(person));
//memory buffer
MemoryStream stream = new MemoryStream();
//xml writer be sure using utf8 encoding
XmlTextWriter textwriter = new XmlTextWriter(stream, Encoding.UTF8);
//the moment of true, parsing object
seriailizor.Serialize(textwriter, _class);
//fill the buffer
stream = (MemoryStream)textwriter.BaseStream;
//Convert to array to string
xmlstring = UTFbytesToString(stream.ToArray());
return xmlstring;
}
catch (Exception ex)
{
return "";
}
}
//convert utf8 bytes array to string
private string UTFbytesToString(Byte[] array)
{
UTF8Encoding utf8encoder = new UTF8Encoding();
string convertedString = utf8encoder.GetString(array);
return convertedString;
}
}
public class person
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
//create deserialize function for each class
public static person deSerialize(byte[] xmlbyte)
{
try
{
XmlSerializer seriailizor = new XmlSerializer(typeof(person));
MemoryStream stream = new MemoryStream(xmlbyte);
person _person = new person();
//parsing the bytes to person
_person = (person)seriailizor.Deserialize(stream);
return _person;
}
catch (Exception ex)
{throw;}
}
}
}
חשוב מאוד! להגדיר את ה Handler ב Web.config:
<httpHandlers>
<add type="restLand.rest" verb="*" path="service" />
</httpHandlers>
</system.web>
<system.webServer>
<handlers accessPolicy="Read, Script">
<add name="service" path="service" verb="*" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv2.0,bitness32" />
</handlers>
</system.webServer>
שלב ב - צד הלקוח
החלק הזה מאוד פשוט, ניצור פרוייקט חדש מסוג Console Application, ונבצע בקשות ל Url של ה Service בעזרת אובייקט HttpRequest, ונקבל את התשובה בעזרת HttpResponse.
החלק הזה מאוד פשוט, ניצור פרוייקט חדש מסוג Console Application, ונבצע בקשות ל Url של ה Service בעזרת אובייקט HttpRequest, ונקבל את התשובה בעזרת HttpResponse.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
namespace resttest
{
class Program
{
//service url
static string url = "http://192.168.1.8:443/service?type=person";
static void Main(string[] args)
{
GET();
POST();
}
//GET method
private static void GET()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
//selected method
request.Method = "GET";
Console.WriteLine("send GET request");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
Console.WriteLine("get response");
Console.WriteLine(reader.ReadToEnd());
Console.WriteLine("end GET request");
}
//create xml stream of person
private static byte[] create_person(string firstname, string lastname)
{
//create person xml
MemoryStream stream = new MemoryStream();
XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.Formatting = Formatting.Indented;
writer.WriteStartDocument();
writer.WriteStartElement("person");
writer.WriteStartElement("FirstName");
writer.WriteString(firstname);
writer.WriteEndElement();
writer.WriteStartElement("LastName");
writer.WriteString(lastname);
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
writer.Close();
return stream.ToArray();
}
//POST method, PUT and DELETE like this method
private static void POST()
{
byte[] _arr = create_person("WORLD", "HELLO");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
Console.WriteLine("send POST request");
//selected method
request.Method = "POST";
request.ContentType = "text/xml";
request.KeepAlive = false;
request.Timeout = 5000;
request.ContentLength = _arr.Length;
Stream stream = request.GetRequestStream();
// write xml to stream
stream.Write(_arr, 0, _arr.Length);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
//write the response from server
Console.WriteLine(reader.ReadToEnd().ToString());
Console.WriteLine("end POST request");
}
}
}
סיכום:
קשה מאוד להגיד איזו שיטה טובה יותר, אבל האמת היא ש REST מבוססת על תקשורת בסיסית של HTTP בניגוד ל SOAP שלא תמיד נתמך ולפעמים יש להשתמש בספריות צד שלישי בשביל לעבוד איתו, אבל חשוב מאוד לזכור שאחריות רבה מונחת על המפתח בזמן ההתעסקות עם REST, בגלל הגמישות שלו אנחנו חסרי הגנה וחשוב לבצע בדיקות רבות לפני שאנחנו משחררים אותו לעולם.
כולל שירות...
אין תגובות:
הוסף רשומת תגובה