الغرض الوحيد من واجهة برمجة تطبيقات Ajax الأساسية (المعروفة باسم XMLHttpRequest) هو إرسال طلبات HTTP لتبادل البيانات بين متصفح الويب والخادم. يمكن أن تستخدم تعليمات JavaScript البرمجية التي يتم تشغيلها في صفحة ويب XMLHttpRequest لإرسال معلمات الطلب إلى برنامج نصي من جانب الخادم، مثل صفحة Servlet أو JSP. سوف يرسل Servlet/JSP المستدعي استجابة تحتوي على بيانات تستخدم عادةً لتحديث عرض المستخدم دون تحديث الصفحة بأكملها. يوفر هذا الأسلوب مزايا فريدة من حيث الأداء وسهولة الاستخدام نظرًا لتقليل حركة مرور الشبكة وتكون واجهة مستخدم الويب قابلة للاستخدام تقريبًا مثل واجهة المستخدم الرسومية لسطح المكتب.
ومع ذلك، فإن تطوير واجهة المستخدم هذه ليس بالأمر السهل، حيث يجب عليك تنفيذ تبادل البيانات والتحقق من صحتها ومعالجتها باستخدام JavaScript من جانب العميل وJava (أو لغة معادلة) من جانب الخادم. ومع ذلك، في كثير من الحالات، فإن الجهد الإضافي لبناء واجهة تعتمد على Ajax يستحق كل هذا العناء، مع الأخذ في الاعتبار الفوائد التي سيتم الحصول عليها.
في هذه المقالة، سأقدم إحدى الطرق الأساسية لنقل البيانات بين عملاء وخوادم Ajax، ومقارنة الاختلافات بين نموذج تطبيق الويب التقليدي ونموذج Ajax. بالإضافة إلى ذلك، ستستكشف المقالة أيضًا تقنيات معالجة البيانات على جانب الخادم وجانب العميل.
أولاً، ستتعلم كيفية استخدام JavaScript لتشفير معلمات كائن الطلب على جانب العميل. يمكنك استخدام ما يسمى بتشفير URL (التشفير الافتراضي الذي تستخدمه متصفحات الويب)، أو يمكنك تضمين معلمات الطلب في مستند XML. سيقوم الخادم بمعالجة الطلب وإرجاع الاستجابة التي يجب أيضًا تشفير بياناتها. تستكشف هذه المقالة JavaScript Object Notation (JSON) وXML، وهما خيارات تنسيق بيانات الاستجابة الرئيسية.
ستركز معظم هذه المقالة على واجهات برمجة التطبيقات المرتبطة بـ XML والمستخدمة بشكل شائع في تطبيقات Ajax. من ناحية العميل، واجهة برمجة تطبيقات XML محدودة للغاية ولكنها كافية. في معظم الحالات، يمكن إنجاز كافة العمليات الضرورية باستخدام XMLHttpRequest. بالإضافة إلى ذلك، يمكن استخدام JavaScript لتحليل مستندات XML وإجراء تسلسل لأشجار DOM في متصفح الويب. على جانب الخادم، هناك العديد من واجهات برمجة التطبيقات (API) والأطر المتاحة لمعالجة مستندات XML. توضح هذه المقالة كيفية تنفيذ المهام الأساسية باستخدام Java API القياسي لـ XML، الذي يدعم مخطط XML وXPath وDOM والعديد من المعايير الأخرى.
من خلال هذه المقالة يمكنك التعرف على أفضل التقنيات وأحدث واجهات برمجة التطبيقات (APIs) لتبادل البيانات في تطبيقات Ajax. نموذج التعليمات البرمجية المتضمن موجود في ثلاث حزم: util، وmodel، وfeed. توفر الفئات الموجودة في حزمة الأداة المساعدة طرقًا لتحليل XML، والتحقق من صحة المخطط، والاستعلام المستند إلى XPath، وتسلسل DOM، وترميز JSON. تحتوي حزمة النموذج على نماذج بيانات نموذجية يمكن تهيئتها من مستندات XML ثم تحويلها إلى تنسيق JSON. يوجد أيضًا مثال مخطط في دليل النموذج الذي يمكن استخدامه للتحقق من صحة XML. يمكن استخدام الفئات الموجودة في حزمة الخلاصة لمحاكاة خلاصة البيانات، والتي تسترد المعلومات عبر Ajax كل 5 ثوانٍ لتحديث صفحة الويب. يشرح هذا المقال كيفية تجنب تسرب الذاكرة في متصفح الويب الخاص بك عن طريق إنهاء طلبات Ajax المعلقة وحذف كائن XMLHttpRequest عند الانتهاء من استخدامه.
يتم تضمين عينات JSP وJavaScript في دليل الويب. يحتوي ajaxUtil.js على وظائف مساعدة لإرسال طلبات Ajax وإنهاء الطلبات ومعالجة أخطاء HTTP. يوفر هذا الملف أيضًا أدوات JavaScript مساعدة لتشفير XML وURL، وتحليل XML، وتسلسل DOM. يعمل ملف ajaxCtrl.jsp كوحدة تحكم Ajax، حيث يتلقى كل طلب Ajax، أو يعيد توجيه المعلمات إلى نموذج البيانات، أو يوفر المعالجة، ثم يعيد استجابة Ajax. أما باقي ملفات الويب فهي أمثلة توضح كيفية استخدام هذه الطريقة العملية.
إن أبسط طريقةلإنشاء طلب من جانب العميل
لإرسال البيانات إلى خادم ويب هي تشفير الطلب كسلسلة استعلام، والتي يمكن إما إلحاقها بعنوان URL أو تضمينها في نص الطلب، اعتمادًا على طريقة HTTP المستخدمة. إذا كنت بحاجة إلى إرسال هياكل بيانات معقدة، فالحل الأفضل هو تشفير المعلومات في مستند XML. سأغطي كلتا الطريقتين في هذا القسم.
تشفير معلمات الطلب. عند تطوير تطبيقات الويب التقليدية، لا داعي للقلق بشأن تشفير بيانات النموذج لأن متصفح الويب يقوم بذلك تلقائيًا عندما يرسل المستخدم البيانات. ومع ذلك، في تطبيق Ajax، يجب عليك تشفير معلمات الطلب بنفسك. توفر JavaScript وظيفة escape() مفيدة جدًا، والتي تستبدل أي أحرف لا يمكن أن تكون جزءًا من عنوان URL بـ %HH (حيث HH هو الرمز السداسي العشري). على سبيل المثال، يتم استبدال أية أحرف مسافات بيضاء بـ %20.
يوفر تنزيل نموذج التعليمات البرمجية وظيفة مساعدة، buildQueryString()، والتي تقوم بتسلسل المعلمات المستردة من مصفوفة، وفصل اسم وقيمة كل معلمة بواسطة = ووضع الحرف & بين كل زوج من الاسم والقيمة:
function buildQueryString(params) {
استعلام فار = "";
لـ (var i = 0; i < params.length; i++) {
الاستعلام += (i > 0 ? "&" : "")
+ الهروب (المعلمات [i].name) + "="
+ escape(params[i].value);
}
استعلام الإرجاع؛
}
لنفترض أنك تريد تشفير المعلمات التالية:
var someParams = [
{ الاسم: "الاسم"، القيمة: "جون سميث" }،
{ الاسم: "البريد الإلكتروني"، القيمة: " [email protected] " }،
{ الاسم:"الهاتف"، القيمة: "7890 456 (123)" }
];
سيؤدي استدعاء buildQueryString(someParams) إلى ظهور نتائج تحتوي على:
name=John%20Smith&[email protected]&phone=%28123%29%20456%207890
إذا كنت ترغب في استخدام أسلوب GET، فيجب عليك إلحاق الاستعلام بعنوان URL بعد الحرف ? عند استخدام POST، يجب تعيين رأس نوع المحتوى على application/x-www-form-urlencoded عبر setRequestHeader()، ويجب تمرير سلسلة الاستعلام إلى طريقة send() الخاصة بـ XMLHttpRequest، والتي سترسل طلب HTTP إلى الخادم.
قم بإنشاء مستند XML. يعد استخدام السلاسل لإنشاء عناصر من سماتها وبياناتها هو أبسط طريقة لإنشاء مستندات XML باستخدام JavaScript. إذا اعتمدت هذا الحل، فستحتاج إلى طريقة مساعدة للهروب من &، <، >، "، والأحرف:
function escapeXML(content) {
إذا (المحتوى == غير محدد)
يعود ""؛
إذا (!content.length || !content.charAt)
المحتوى = سلسلة جديدة (المحتوى)؛
نتيجة فار = "";
فار الطول = المحتوى. الطول؛
لـ (var i = 0; i < length; i++) {
var ch = content.charAt(i);
التبديل (الفصل) {
قضية &:
النتيجة += "&";
استراحة؛
حالة < :
النتيجة += "< "؛
استراحة؛
حالة >:
النتيجة += ">";
استراحة؛
قضية ":
النتيجة += """;
استراحة؛
قضية \:
النتيجة += "'";
استراحة؛
تقصير:
النتيجة += الفصل؛
}
}
نتيجة الإرجاع؛
}
لتسهيل المهمة، يلزم وجود بعض الأساليب المساعدة الإضافية، مثل:
function attribute(name, value) {
return " " + name + "="" + escapeXML(value) + """;
}
ينشئ المثال التالي مستند XML من مصفوفة من الكائنات ذات ثلاث خصائص: الرمز، والمشاركة، والسعر المدفوع:
function buildPortfolioDoc(stocks) {
var xml = "<portfolio>";
لـ (var i = 0; i < Stocks.length; i++) {
مخزون فار = الأسهم[i];
xml += "< مخزون ";
xml += attribute("symbol", Stock.symbol);
xml += attribute("shares", Stock.shares);
xml += attribute("PaidPrice"، Stock.payPrice);
xml += "";
}
xml += "< /portfolio>";
إرجاع أكس أم أل؛
}
إذا كنت تفضل العمل مع DOM، فيمكنك استخدام واجهة برمجة تطبيقات متصفح الويب الخاص بك لتحليل XML وإجراء تسلسل لشجرة DOM. باستخدام IE، يمكنك إنشاء مستند فارغ باستخدام ActiveXObject("Microsoft.XMLDOM" جديد). يمكن بعد ذلك تحليل XML من سلسلة أو عنوان URL باستخدام أساليب التحميل ()loadXML أو التحميل () على التوالي. في حالة IE، تحتوي كل عقدة على سمة تسمى xml والتي تسمح لك بالحصول على تمثيل XML للعقدة وجميع العقد التابعة لها. لذلك، يمكنك تحليل سلسلة XML، وتعديل شجرة DOM، ثم إجراء تسلسل لـ DOM مرة أخرى إلى XML.
يتيح لك متصفحا Firefox وNetscape إنشاء مستند فارغ باستخدام document.implementation.createDocument(...). يمكن بعد ذلك إنشاء عقد DOM باستخدام createElement() و createTextNode() و createCDATASection() وما إلى ذلك. يوفر متصفح Mozilla أيضًا واجهتي برمجة تطبيقات باسم DOMParser وXMLSerializer. تحتوي واجهة برمجة تطبيقات DOMParser على أساليب parseFromStream() وparseFromString(). تحتوي فئة XMLSerializer على طرق مقابلة لإجراء تسلسل لشجرة DOM: serializeToStream() وserializeToString().
تقوم الوظيفة التالية بتوزيع سلسلة XML وإرجاع مستند DOM:
function parse(xml) {
فار دوم؛
يحاول{
dom = new ActiveXObject("Microsoft.XMLDOM");
dom.async = false;
dom.loadXML(xml);
} التقاط (خطأ) {
يحاول{
var parser = new DOMParser();
dom = parser.parseFromString(xml, "text/xml");
حذف المحلل اللغوي؛
} التقاط (خطأ2) {
إذا (التصحيح)
تنبيه ("تحليل XML غير مدعوم.")؛
}
}
عودة دوم؛
}
تقوم الوظيفة الثانية بإجراء تسلسل لعقدة DOM وجميع العقد التابعة لها، وإرجاع XML كسلسلة:
function serialize(dom) {
var xml = dom.xml;
إذا (xml == غير محدد) {
يحاول{
var serializer = new XMLSerializer();
xml = serializer.serializeToString(dom);
حذف التسلسل؛
} التقاط (خطأ) {
إذا (التصحيح)
تنبيه ("تسلسل DOM غير مدعوم.")؛
}
}
إرجاع أكس أم أل؛
}
يمكنك أيضًا استخدام XMLHttpRequest كمحلل أو مُسلسل. بعد تلقي استجابة لطلب Ajax من الخادم، يتم تحليل الاستجابة تلقائيًا. يمكن الوصول إلى الإصدار النصي وشجرة DOM من خلال خصائص ResponseText وresponseXML الخاصة بـ XMLHttpRequest على التوالي. بالإضافة إلى ذلك، يتم إجراء تسلسل لشجرة DOM تلقائيًا عند تمريرها إلى الأسلوب send().
أرسل طلبا. في مقال سابق، قمت بتقديم XMLHttpRequest API ووظيفة الأداة المساعدة، sendHttpRequest()، والتي يمكنك العثور عليها في ملف ajaxUtil.js في النموذج المقدم للتنزيل. تأخذ هذه الوظيفة أربع معلمات (أسلوب HTTP، وURL، ومصفوفة معلمات، ورد اتصال)، وتقوم بإنشاء كائن XMLHttpRequest، وتعيين خصائصه، واستدعاء أسلوب الإرسال (). إذا تم توفير معلمة رد الاتصال، فسيتم إرسال الطلب بشكل غير متزامن ويتم استدعاء وظيفة رد الاتصال بعد تلقي الاستجابة. بخلاف ذلك، يتم إرسال الطلب بشكل متزامن ويمكنك التعامل مع الاستجابة بمجرد إرجاع sendHttpRequest().
كما ترى، يتعين عليك اتخاذ بعض الاختيارات المهمة عند استخدام XMLHttpRequest
. ما هي طريقة HTTP التي يجب استخدامها (GET أو POST)؟
التنسيق المستخدم لترميز معلمات الطلب (تمت مناقشة ترميز XML وURL سابقًا في هذه المقالة)
ما إذا كان سيتم إجراء المكالمة بشكل متزامن (في انتظار استجابة) أو بشكل غير متزامن (باستخدام رد اتصال) تنسيق الاستجابة، مثل XML أو XHTML أو HTML أو JavaScript Object Notation (JSON) (ستتم مناقشته لاحقًا في هذه المقالة). لنفترض أنك تريد الحصول على بعض معلومات أسعار الأسهم من موجز البيانات وتحديث المعلومات بشكل دوري دون تدخل المستخدم. في هذه الحالة، يجب إرسال طلب HTTP بشكل غير متزامن حتى لا يتم حظر واجهة المستخدم أثناء استرداد المعلومات. معلمة الطلب عبارة عن مجموعة من الرموز التي يمكن ترميزها في عنوان URL. نظرًا لاحتمال زيادة التحميل على الخادم، فإنك لا ترغب في إرسال مستندات XML عند إجراء طلبات متكررة. نظرًا لأنك مهتم فقط بأحدث أسعار الأسهم، فيجب إنهاء أي طلبات سابقة معلقة:
var ctrlURL = "ajaxCtrl.jsp";
var FeedRequest = null
function sendInfoRequest(symbols, callback) {
إذا (طلب التغذية)
abortRequest(feedRequest);
var params = new Array();
لـ (var i = 0; i < الرموز. الطول; i++)
المعلمات[i] = {
الاسم: "رمز"،
القيمة:الرموز[i]
};
FeedRequest = sendHttpRequest(
"GET"، ctrlURL، params، callback)؛
}
قبل استدعاء الأسلوب abort() الخاص بكائن الطلب، تقوم الدالة abortRequest() (الموجودة في الملف ajaxUtil.js) بتعيين الخاصية onreadystatechange إلى رد اتصال لا يفعل شيئًا. بالإضافة إلى ذلك، من الضروري حذف كائن الطلب لتجنب تسرب الذاكرة:
function abortRequest(request) {
وظيفة لا تفعل شيئا () {
}
request.onreadystatechange = doNothing;
request.abort();
حذف طلب التغذية؛
}
لنفكر في حالة أخرى: عند نقل بيانات المستخدم بأكملها ليتم حفظها في قاعدة البيانات، يجب إرسال الطلب بشكل متزامن لأنك ربما لا تريد أن يقوم المستخدم بتعديله أثناء حفظ هذه البيانات قيد التقدم. في هذه الحالة، يُفضل تنسيق XML لأنه غالبًا ما يكون تشفير نموذج الكائن في المستند أسهل من استخدام العديد من معلمات السلسلة. بالإضافة إلى ذلك، طلبات حفظ البيانات نادرة ويتعامل الخادم مع التحميل دون أي مشاكل. يمكن ترميز مستند XML كمعلمة بحيث يمكنك الوصول إليه في صفحة JSP باستخدام بناء جملة EL (${param.xml}). هذه هي الوظيفة التي ترسل بيانات النموذج المشفرة في مستند XML:
function sendSaveRequest(xml) {
var params = [ { name:"xml"، value:xml } ];
var saveRequest = sendHttpRequest("POST", ctrlURL, params);
إذا (حفظ الطلب)
حذف saveRequest؛
}
إذا كنت بحاجة إلى استعادة نموذج الكائن، فيمكنك أيضًا إرسال طلب بشكل متزامن لاسترداد البيانات من الخادم. في هذه الحالة، يجب أن يعرض الخادم استجابة JSON بحيث يمكنك تحويلها بسهولة إلى شجرة كائنات JavaScript باستخدام eval(loadRequest.responseText):
function sendLoadRequest() {
نموذج فار = فارغ؛
varloadRequest = sendHttpRequest("GET", ctrlURL);
إذا (طلب التحميل) {
model = eval(loadRequest.responseText);
حذف طلب التحميل؛
}
نموذج العودة
}
يصف القسمان التاليان العمليات التي يتم إجراؤها عادةً على مستندات XML على الخادم وكيفية الاستجابة لطلبات Ajax.
معالجة الطلبات على جانب الخادم
تقوم حاوية Servlet/JSP بتحليل كل طلب HTTP وإنشاء مثيل ServletRequest، والذي يسمح لك بالحصول على معلمات الطلب من خلال getParameter() / getParameterValues() أو نص الطلب من خلال getInputStream(). في صفحات JSP، يمكن أيضًا الحصول على هذه المعلمات باستخدام صيغة EL (${param...} و${paramValues...}). لاحظ أن تمرير getParameter يكون ممكنًا فقط إذا كان عميل Ajax يستخدم وظيفة مساعدة مثل buildQueryString() لتشفير البيانات بتنسيق application/x-www-form-urlencoded (الموصوف في القسم السابق من هذه المقالة () أو $ {param...} للحصول على معلمات الطلب. إذا قمت بتمرير مستند XML أو شجرة DOM إلى طريقة الإرسال () الخاصة بـ XMLHttpRequest من جانب العميل، فيجب عليك استخدام طريقة getInputStream () من ServletRequest من جانب الخادم.
التحقق من صحة البيانات. يقوم تطبيق الويب النموذجي بتنفيذ العديد من عمليات التحقق من صحة البيانات. معظم الأخطاء المحتملة بسيطة إلى حد ما، مثل معلمات الطلب المفقودة، وتنسيقات الأرقام غير الصحيحة، وما إلى ذلك. عادةً ما تنتج هذه الأخطاء عن نسيان المستخدم إدخال قيمة لعنصر نموذج أو تقديم قيمة غير صالحة. تعتبر أطر عمل الويب مثل JSF وOracle ADF Faces جيدة جدًا في التعامل مع أخطاء المستخدم هذه. في تطبيقات Ajax، يمكن اكتشاف هذه الأخطاء ومعالجتها من جانب العميل باستخدام JavaScript. على سبيل المثال، يمكنك استخدام isNaN(new Number(value)) للتحقق من أن القيمة الرقمية غير صالحة.
لأسباب تتعلق بالأمان والموثوقية، يجب إعادة التحقق من صحة البيانات من جانب الخادم، ولا ينبغي افتراض أن طلبات XML منسقة بشكل جيد. تعد مخططات XML أداة مفيدة للتحقق من صحة الطلبات المعقدة من جانب الخادم. يشتمل تنزيل نموذج التعليمات البرمجية على فئة تسمى XMLUtil والتي توفر طرقًا لتحميل مستندات المخطط واستخدامها. يوضح مقتطف التعليمات البرمجية التالي كيفية تهيئة SchemaFactory:
import javax.xml.*;
import javax.xml.validation.*;
...
schemaFactory ثابت محمي؛
ثابت {
schemaFactory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setErrorHandler(newErrorHandler());
}
يقوم الأسلوب newErrorHandler() بإرجاع معالج أخطاء SAX:
import org.xml.sax.*;
...
ErrorHandler ثابت عام newErrorHandler() {
إرجاع ErrorHandler جديد () {
تحذير الفراغ العام (SAXParseException e)
يرمي SAXException {
Logger.global.warning(e.getMessage());
}
خطأ باطل عام (SAXParseException e)
يرمي SAXException {
رمي ه.
}
الخطأ القاتل العام (SAXParseException e)
يرمي SAXException {
رمي ه.
}
};
}
يمكنك استخدام getResourceAsStream() للعثور على ملف XSD وتحميله في دليل أو JAR محدد في CLASSPATH:
InputStream العام الثابت getResourceAsStream(String name)
يلقي IOException {
InputStream in = XMLUtil.class.getResourceAsStream(name);
إذا (في == فارغة)
رمي FileNotFoundException(name);
العودة في؛
}
بعد ذلك، استخدم مثيل SchemaFactory للحصول على كائن المخطط من خلال طريقة newSchema():
import javax.xml.validation.*;
...
المخطط الثابت العام newSchema (اسم السلسلة)
يلقي IOException، SAXException {
مخطط المخطط؛
InputStream in = getResourceAsStream(name);
يحاول{
schema = schemaFactory.newSchema(new StreamSource(in));
}أخيراً{
in. Close();
}
مخطط العودة؛
}
يمكنك أيضًا إنشاء كائن Oracle XMLSchema باستخدام:
import oracle.xml.parser.schema.XMLSchema;
import oracle.xml.parser.schema.XSDBuilder;
...
XMLSchema العام الثابت newOracleSchema (اسم السلسلة)
يلقي IOException، SAXException {
مخطط XMLSchema؛
InputStream in = getResourceAsStream(name);
يحاول{
XSDBuilder builder = new XSDBuilder();
المخطط = builder.build(new InputSource(in));
} قبض (الاستثناء ه){
رمي SAXException (e) جديد ؛
}أخيراً{
in. Close();
}
مخطط العودة؛
}
بعد ذلك، تحتاج إلى إنشاء DocumentBuilderFactory. إذا تم العثور على تطبيق JAXP 1.1 في CLASSPATH، فقد تؤدي طريقة setSchema() المعرفة بواسطة JAXP 1.2 إلى UnsupportedOperationException، وفي هذه الحالة يجب استبدال تطبيق JAXP 1.1 بتطبيق JAXP 1.2 لـ Java SE 5.0. في هذه الحالة، لا يزال بإمكانك إنشاء كائن مخطط باستخدام newOracleSchema() وتعيينه عبر طريقة setAttribute():
import javax.xml.parsers.*;
import oracle.xml.jaxp.JXDocumentBuilderFactory;
...
DocumentBuilderFactory الثابت العام newParserFactory(
اسم مخطط السلسلة) يطرح IOException، SAXException {
DocumentBuilderFactory parserFactory
= DocumentBuilderFactory.newInstance();
يحاول{
parserFactory.setSchema(newSchema(schemaName));
} التقاط (UnsupportedOperationException e) {
إذا (مثيل parserFactory لـ JXDocumentBuilderFactory) {
parserFactory.setAttribute(
JXDocumentBuilderFactory.SCHEMA_OBJECT،
newOracleSchema(schemaName));
}
}
إرجاع parserFactory;
}
بعد ذلك، قم بإنشاء كائن DocumentBuilder واستخدمه للتحقق من صحة مستند XML وتحليله:
import javax.xml.parsers.*;
...
DocumentBuilder العام الثابت newParser (
DocumentBuilderFactory (parserFactory)
يرمي ParserConfigurationException {
DocumentBuilder parser = parserFactory.newDocumentBuilder();
parser.setErrorHandler(newErrorHandler());
محلل العودة.
};
افترض أنك تريد التحقق من صحة مستند XML مقابل مثال مخطط المحفظة.xsd:
< xsd:schema xmlns:xsd=" http://www.w3.org/2001/XMLSchema ">
< xsd:element name="portfolio" type ="portfolioType"
< xsd:complexType name="portfolioType">
<xsd:التسلسل>
<xsd:اسم العنصر = "المخزون"
minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:اسم السمة = "الرمز"
اكتب = "xsd: سلسلة" استخدام = "مطلوب"/>
<xsd:اسم السمة = "المشاركات"
type = "xsd:positiveInteger" use = "required"/>
<xsd:اسم السمة = "السعر المدفوع"
اكتب = "xsd: عشري" استخدام = "مطلوب"/>
< /xsd:complexType>
< /xsd:العنصر>
< /xsd:التسلسل>
< /xsd:complexType>
< /xsd:schema>
تستخدم طريقة parsePortfolioDoc() لفئة DataModel XMLUtil للتحقق من صحة معلمات xml وتحليلها وإرجاع مستند DOM:
سلسلة نهائية ثابتة خاصة SCHEMA_NAME
= "/ajaxapp/model/portfolio.xsd";
DocumentBuilderFactory ثابت خاص؛
parsePortfolioDoc للمستند الثابت الخاص (سلسلة xml)
يلقي IOException، SAXException،
ParserConfigurationException {
متزامن (DataModel.class) {
إذا (parserFactory == فارغ)
parserFactory = XMLUtil.newParserFactory(SCHEMA_NAME);
}
DocumentBuilder parser = XMLUtil.newParser(parserFactory);
InputSource in = new InputSource(new StringReader(xml));
إرجاع parser.parse(in);
}
الآن بعد أن أصبحت لديك شجرة DOM، فأنت بحاجة إلى الحصول على البيانات اللازمة لتكوين عقد DOM.
استخراج المعلومات المطلوبة. يمكنك استخدام DOM API أو لغة استعلام (مثل XQuery أو XPath) لتصفح شجرة DOM. توفر Java واجهة برمجة تطبيقات قياسية لـ XPath، والتي سيتم استخدامها لاحقًا. تقوم فئة XMLUtil بإنشاء XPathFactory باستخدام أسلوب newXPath():
import javax.xml.xpath.*;
...
XPathFactory ثابت محمي xpathFactory؛
ثابت {
xpathFactory = XPathFactory.newInstance();
}
XPath العام الثابت newXPath() {
إرجاع xpathFactory.newXPath();
}
تقوم الطرق التالية بتقييم تعبير XPath في سياق معين وإرجاع القيمة الناتجة:
import javax.xml.xpath.*;
import org.w3c.dom.*;
...
سلسلة ثابتة عامة evalToString (تعبير سلسلة،
سياق الكائن) يطرح XPathExpressionException {
إرجاع (سلسلة) newXPath().evaluate(التعبير، السياق،
XPathConstants.STRING);
}
evalToBoolean المنطقية العامة الثابتة (تعبير السلسلة،
سياق الكائن) يطرح XPathExpressionException {
إرجاع ((منطقي) newXPath().evaluate(expression, context,
XPathConstants.BOOLEAN)).booleanValue();
}
evalToNumber مزدوج ثابت عام (تعبير سلسلة،
سياق الكائن) يطرح XPathExpressionException {
إرجاع ((مزدوج) newXPath().evaluate(expression, context,
XPathConstants.NUMBER)).doubleValue();
}
العقدة الثابتة العامة evalToNode (تعبير السلسلة،
سياق الكائن) يطرح XPathExpressionException {
إرجاع (عقدة) newXPath (). تقييم (التعبير، السياق،
XPathConstants.NODE);
}
NodeList العامة الثابتة evalToNodeList (تعبير السلسلة،
سياق الكائن) يطرح XPathExpressionException {
إرجاع (NodeList) newXPath().evaluate(expression, context,
XPathConstants.NODESET);
}
تستخدم طريقة setData() الخاصة بـ DataModel طريقة حل XPath لاستخراج المعلومات من مستند XML المدمج:
public syncronized void setData(String xml)
يلقي IOException، SAXException،
ParserConfigurationException,
XPathExpressionException {
يحاول{
قائمة مخزون ArrayList
= قائمة صفيف جديدة () ؛
Document doc = parsePortfolioDoc(xml);
NodeListNodeList = XMLUtil.evalToNodeList(
"/portfolio/stock"، doc);
for (int i = 0; i <nodeList.getLength(); i++) {
العقدة العقدة = العقدةList.item(i);
StockBean stock = new StockBean();
الأسهم.setSymbol(
XMLUtil.evalToString("@symbol"، العقدة));
الأسهم.setShares(
(int) XMLUtil.evalToNumber("@shares"، العقدة));
سعر السهم المحدد(
XMLUtil.evalToNumber("@profitPrice"، العقدة));
StockList.add(stock);
}
this.stockList = StockList;
} قبض (الاستثناء ه){
Logger.global.logp(Level.SEVERE، "DataModel"، "setData"،
e.getMessage(), e);
}
}
بمجرد توفر البيانات في نموذج البيانات من جانب الخادم، يمكن معالجتها وفقًا لمتطلبات التطبيق. بعد ذلك، يجب عليك الرد على طلب Ajax.
إنشاء الاستجابة على جانب الخادم
يعد إرجاع HTML كرد على طلب Ajax هو الحل الأبسط لأنه يمكنك إنشاء العلامات باستخدام بناء جملة JSP ويستخدم عميل Ajax ببساطة عنصر <div> أو <span> وتقوم الخاصية الداخلية HTML بإدراج HTML في مكان ما على الصفحة. ومع ذلك، فمن الأفضل إرجاع البيانات إلى عميل Ajax دون أي ترميز للعرض التقديمي. يمكنك استخدام تنسيق XML أو JSON.
توليد استجابة XML. يوفر Java EE العديد من الخيارات لإنشاء مستندات XML: التي تم إنشاؤها عبر JSP، أو تم إنشاؤها من شجرة كائنات عبر JAXB، أو تم إنشاؤها باستخدام javax.xml.transform. سيقوم المحول في المثال التالي بإجراء تسلسل لشجرة DOM:
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
...
public static TransformerFactory serializerFctory;
ثابت {
serializerFctory = TransformerFactory.newInstance();
}
تسلسل الفراغ الثابت العام (عقدة العقدة، مخرج OutputStream)
يرمي TransformerException {
محول تسلسلي = serializerFctory.newTransformer();
Properties serializerProps = new Properties();
serializerProps.put(OutputKeys.METHOD, "xml");
serializer.setOutputProperties(serializerProps);
مصدر المصدر = DOMSource الجديد (عقدة)؛
نتيجة النتيجة = StreamResult الجديد (out)؛
serializer.transform(source, result);
}
هناك العديد من الخيارات القياسية وأطر عمل مصادر التطوير لإنشاء XML على جانب الخادم، والشيء الوحيد الذي عليك فعله هو اختيار الخيار الذي يناسبك. ومع ذلك، يختلف الوضع تمامًا بالنسبة للعميل، حيث لا يمكن تحليل XML إلا باستخدام DOM. تدعم بعض المتصفحات أيضًا XPath وXSLT.
في مقالات Ajax السابقة، تعلمت كيفية إنشاء XML عبر JSP ثم تحليله على العميل باستخدام JavaScript وDOM. الحل الآخر هو استخدام JSON بدلاً من XML كتنسيق بيانات للاستجابة لطلبات Ajax. كما ذكرنا سابقًا، يمكن تحويل سلسلة JSON إلى شجرة كائنات JavaScript باستخدام الدالة eval(). يعد هذا أسهل من استخدام JavaScript لاستخراج المعلومات من شجرة DOM. كل ما تحتاجه هو فئة فائدة جيدة تنشئ JSON على جانب الخادم.
ترميز JSON. توفر فئة JSONEncoder طرقًا لترميز القيم الحرفية والكائنات والمصفوفات. يتم تخزين النتائج في java.lang.StringBuilder:
package ajaxapp.util
public class JSONEncoder {;
خاص StringBuilder buf؛
JSONEncoder العامة () {
buf = new StringBuilder();
}
...
}
يقوم أسلوب الحرف () بتشفير حرف واحد:
public void Character(char ch) {
التبديل (الفصل) {
قضية \:
قضية \":
قضية \ :
buf.append( \ );
buf.append(ch);
استراحة؛
قضية :
buf.append( \ );
buf.append() ;
استراحة؛
قضية
:
buf.append( \ );
buf.append(
);
استراحة؛
قضية
:
buf.append( \ );
buf.append(
);
استراحة؛
تقصير:
إذا (الفصل >= 32 && الفصل <128)
buf.append(ch);
آخر{
buf.append( \ );
buf.append(u);
لـ (int j = 12; j >= 0; j-=4) {
int k = (((int) ch) >> j) & 0x0f;
كثافة العمليات ج = ك < 10 ? + ك :أ + ك - 10;
buf.append((شار) ج);
}
}
}
}
تقوم طريقة string() بتشفير السلسلة بأكملها:
public void string(String str) {
int length = str. length();
ل(int i = 0; i < length; i++)
حرف(str.charAt(i));
}
الطريقة الحرفية () تقوم بتشفير JavaScript literal:
public void literal(Object value) {
إذا (قيمة مثيل السلسلة) {
buf.append(");
قيمة السلسلة ((سلسلة))؛
buf.append(");
} else if (قيمة مثيل الحرف) {
buf.append(\);
الحرف(((الحرف) القيمة).charValue());
buf.append(\);
} آخر
buf.append(value.toString());
}
تقوم طريقة comma() بإلحاق حرف فاصلة:
public void comma() {
buf.append(,);
}
ستقوم طريقة الحذفLastComma() بإزالة حرف الفاصلة الأخير في نهاية المخزن المؤقت (إن وجد):
public voiddeleteLastComma() {
إذا (buf. length() > 0)
إذا (buf.charAt(buf.length()-1) == ,)
buf.deleteCharAt(buf. length()-1);
}
تُلحق طريقة startObject() الحرف { للإشارة إلى بداية كائن JavaScript:
public void startObject() {
buf.append({);
}
تقوم طريقة property() بتشفير خصائص JavaScript:
خاصية الفراغ العام (اسم السلسلة، قيمة الكائن) {
buf.append(name);
buf.append(:);
حرفي(قيمة);
فاصلة()؛
}
تقوم طريقة endObject() بإلحاق حرف } للإشارة إلى نهاية كائن JavaScript:
public void endObject() {
حذفLastComma();
buf.append(});
فاصلة()؛
}
تُلحق طريقة startArray() حرف [ للإشارة إلى بداية مصفوفة JavaScript:
public void startArray() {
buf.append([);
}
يقوم أسلوب element() بتشفير عناصر مصفوفة JavaScript:
public void element(Object value) {
حرفي(قيمة);
فاصلة()؛
}
يُلحق أسلوب endArray() حرف ] للإشارة إلى نهاية مصفوفة JavaScript:
public void endArray() {
حذفLastComma();
buf.append(]);
فاصلة()؛
}
تُرجع طريقة toString () سلسلة JSON:
public String toString() {
حذفLastComma();
إرجاع buf.toString();
}
طريقة Clear () تمسح المخزن المؤقت:
public void Clear() {
buf.setLength(0);
}
يستخدم DataModel فئة JSONEncoder لتشفير البيانات التي يحتفظ بها:
public Synchronized String getData() {
JSONEncoder json = new JSONEncoder();
json.startArray();
لـ (int i = 0; i < StockList.size(); i++) {
مخزون StockBean = StockList.get(i);
json.startObject();
json.property("symbol"، Stock.getSymbol());
json.property("shares"، Stock.getShares());
json.property("PaidPrice"، Stock.getPaidPrice());
json.endObject();
}
json.endArray();
إرجاع json.toString();
}
إذا تم توفير معلمات طلب xml، فستقوم صفحة ajaxCtrl.jsp بتعيين بيانات النموذج. بخلاف ذلك، ستستخدم الصفحة تعبير EL ${dataModel.data} لإخراج سلسلة JSON التي يتم إرجاعها بواسطة getData():
< %@ taglib prefix="c" uri=" http://java.sun.com/jsp/ جستل / الأساسية " %>
...
< jsp:useBean id = "dataModel" نطاق = "جلسة"
class="ajaxapp.model.DataModel" />
< c:choose>
...
< c:when test="${!empty param.xml}">
< c:set target="${dataModel}"
خاصية = "البيانات"
value="${param.xml}" />
< /ج:متى>
<ج:خلاف ذلك>
${dataModel.data}
< /ج:خلاف ذلك>
< /ج: اختر>
هذه المهمة لم تكتمل لأنه يجب على عميل Ajax معالجة بيانات JSON.
معالجة الاستجابات من جانب العميل
في تطبيق ويب نموذجي، يمكنك إنشاء محتوى على جانب الخادم باستخدام JSPs وأطر عمل الويب ومكتبات العلامات. تعتبر تطبيقات Ajax مثالية لهذا الموقف لأن أطر عمل الويب مثل JavaServer Faces وOracle ADF Faces مفيدة جدًا في إنشاء تطبيقات Ajax. ومع ذلك، لا تزال هناك اختلافات كبيرة بين تطبيقات Ajax والتطبيقات غير التابعة لـ Ajax. عند استخدام Ajax، يجب عليك معالجة البيانات من جانب العميل واستخدام JavaScript لإنشاء محتوى ديناميكيًا لتوفير البيانات للمستخدم.
إذا كنت تستخدم تنسيق JSON لتحويل البيانات، فمن السهل جدًا تحويل النص إلى شجرة من الكائنات باستخدام وظيفة eval() التي توفرها JavaScript. إذا كنت تفضل استخدام XML، فهناك الكثير من الأشياء الأخرى التي يتعين عليك القيام بها، ولكن هذا التنسيق له أيضًا مزاياه الخاصة. على سبيل المثال، يمكن استخدام XML من قبل العديد من أنواع العملاء، بينما من السهل تحليل JSON في بيئة JavaScript فقط. بالإضافة إلى ذلك، عند استخدام XML، يمكن العثور على الأخطاء وإصلاحها بشكل أسرع، وبالتالي تقليل وقت تصحيح الأخطاء.
استخدم JavaScript للوصول إلى شجرة DOM. تشبه واجهة DOM API الخاصة بجافا سكريبت إلى حد كبير حزمة org.w3c.dom الخاصة بجافا. والفرق الرئيسي هو الوصول إلى الخصائص. في JavaScript، يمكنك الوصول إلى الخصائص مباشرةً، بينما تتعامل Java مع الخصائص على أنها خاصة وتحتاج إلى الوصول إليها من خلال أساليب الحصول والتعيين. على سبيل المثال، يمكنك الحصول على العنصر الجذر للمستند من خلال dom.documentElement.
DOM عبارة عن واجهة برمجة تطبيقات منخفضة المستوى توفر الوصول إلى بنية المستند الذي تم تحليله. على سبيل المثال، تريد تجاهل التعليقات في معظم الحالات، وقد لا ترغب في الحصول على عقد نصية مجاورة. خذ بعين الاعتبار المثال البسيط التالي:
var xml = "< element>da< !--comment-->ta&"
+ "< ![CDATA[cdata< /element>";
يمكنك تحليل سلسلة XML أعلاه باستخدام وظيفة الأداة المساعدة التي تم تقديمها مسبقًا:
var dom = parse(xml);
يمكنك العثور على التعليمات البرمجية الخاصة بوظيفة parse() في ajaxUtil.js؛ وفي هذه الحالة، تُرجع الدالة شجرة DOM يحتوي عنصرها الجذري على عقدة نصية، متبوعة بتعليق، وعقدة نصية أخرى، وعقدة بيانات الأحرف. إذا كنت تريد تضمين نص بدون تعليقات، فيجب عليك التكرار على العناصر الفرعية للعنصر، وربط قيم عقد بيانات النص والأحرف (التي لها النوعان 3 و4 على التوالي):
var element = dom.documentElement;
var ChildNodes = element.childNodes;
نص فار = "";
لـ (var i = 0; i < ChildNodes.length; i++)
إذا (childNodes[i].nodeValue) {
var type = ChildNodes[i].nodeType;
إذا (اكتب == 3 || اكتب == 4)
text += ChildNodes[i].nodeValue;
}
عند العمل مع DOM، يجب عليك إنشاء مجموعة صغيرة من الوظائف المساعدة لتجنب التعامل مع هذه التفاصيل ذات المستوى المنخفض.
استخدم JavaScript لإنشاء محتوى ديناميكي. تسمح لك متصفحات الويب بالوصول إلى بنية DOM لصفحة الويب من خلال كائنات المستند. على سبيل المثال، يمكنك استخدام document.getElementById(...) للعثور على عنصر بسهولة شديدة. يمكنك أيضًا إنشاء عناصر وعقد نصية جديدة يمكن إدراجها في المستندات الموجودة. ومع ذلك، من الأسهل إنشاء HTML عن طريق وصل السلاسل كما هو موضح أدناه:
function updateInfo(request) {
أسهم فار = eval(request.responseText);
var table = "< table border=1 cellpadding=5>";
الجدول += "<tr>";
table += "< th>Symbol< /th>";
table += "< th>الاتجاه< /th>";
table += "<th>السعر الأخير< /th>";
table += "< /tr>";
لـ (var i = 0; i < share.length; i++) {
var share = share[i];
رمز فار = escapeXML(share.symbol)
فار الاتجاه = share.trend > 0 "+" : "-";
var lastPrice = new Number(share.lastPrice).toFixed(2);
الجدول += "<tr>";
table += "<td>" + الرمز + "< /td>";
table += "<td>" + Trend + "< /td>";
table += "< td>" + lastPrice + "< /td>";
table += "< /tr>";
}
table += "< /table>";
document.getElementById("table").innerHTML = table;
}
يمكن إدراج HTML الذي تم إنشاؤه
في عنصر فارغ عن طريق تعيين خاصية InnerHTML للكائن الذي تم إرجاعه بواسطة getElementByid () ، على سبيل المثال:
<div id = "table">
< /ديف>
يستخدم المثال في هذه المقالة وظيفة updateInfo () كإجراء رد اتصال للتعامل مع الاستجابات على طلبات AJAX المرسلة إلى الخادم من خلال SendInforeQuest في ملف ajaxlogic.js. إذا كنت ترغب في تحديث المعلومات كل 5 ثوانٍ ، فيمكنك استخدام وظيفة SetInterVal () JavaScript:
VAR Symods = [...] ؛
setInterval ("sendInforeQuest (الرموز ، updateInfo)" ، 5000) ؛
فئة تسمى DataFeed تحاكي خلاصة من جانب الخادم. تستدعي صفحة Ajaxctrl.jsp طريقة GetData () الخاصة بتغذية ، وإعادة الاستجابة كسلسلة JSON. على جانب العميل ، تقوم وظيفة updateInfo () بتوزيع سلسلة JSON باستخدام eval (request.Responsetext) ، كما هو موضح في مثال الكود السابق.