בגרות במדעי המחשב תכנות באינטרנט 899282/899372 - בחינה לדוגמה
נבחנים אקסטרניים · שאלון 899282/899372 · 9 שאלות · 2 שעות · פתרון מלא לכל סעיף
בחינת הבגרות במדעי המחשב, תכנות באינטרנט, בחינה לדוגמה, כל 9 השאלות כמו בשאלון. לכל סעיף יש תשובה ופתרון מלא: הרעיון, הקוד או המעקב, ובדיקה קצרה. כל קוד מופיע ב-Java וב-C#. נסו לפתור לבד, ואז פתחו את הפתרון.
הוראות הבחינה (כמו בשאלון)
הוראות לנבחנים
פרק ראשון - (3 × 10) - 30 נקודות
פרק שני - (2 × 20) + (10 × 1) - 50 נקודות
פרק שלישי - (1 × 20) - 20 נקודות
סך הכול - 100 נקודות
כתבו במחברת הבחינה בלבד, רשמו "טיוטה" בראש כל עמוד המשמש טיוטה.
כתיבת טיוטה בדפים שאינם במחברת הבחינה עלולה לגרום לפסילת הבחינה.
ההנחיות בשאלון זה מנוסחות בלשון רבים ומכוונות לנבחנות ולנבחנים כאחד.
בהצלחה!
פרק ראשון
פרק ראשון (30 נקודות)
ענו על שלוש השאלות 1-3 (לכל שאלה – 10 נקודות)
שאלה 1 · ההבדל בין get ל-post
מה ההבדל בין השיטות get ו - post?
(1) בשיטת get הערכים שנשלחים מוצגים בשורת הכתובת, לעומת שיטת post בה הערכים שנשלחים אינם מוצגים. בנוסף כמות המידע שניתן להעביר בשיטת post גדולה יותר.
(2) בשיטת post הערכים שנשלחים מוצגים בשורת הכתובת, לעומת שיטת get בה הערכים שנשלחים אינם מוצגים. בנוסף כמות המידע שניתן להעביר בשיטת get גדולה יותר.
(3) בשיטת get הערכים שנשלחים מוצגים בשורת הכתובת, לעומת שיטת post בה הערכים שנשלחים אינם מוצגים. בנוסף המידע המועבר בשיטת get מאובטח יותר.
(4) בשיטת post הערכים שנשלחים מוצגים בשורת הכתובת, לעומת שיטת get בה הערכים שנשלחים אינם מוצגים. בנוסף המידע המועבר בשיטת post מאובטח יותר.
מה ההבדל בין השיטות get ו - post?
◂ פתרון מלא לשאלה 1
פתרון
(1)
מה נדרש – לבחור את התשובה הנכונה על ההבדל בין שתי שיטות שליחת הטופס (method) – get ו-post.
רעיון – לזכור איפה כל שיטה "שמה" את הנתונים בבקשת ה-HTTP.
פתרון
?, למשל page.aspx?name=dana&age=17. לכן רואים אותם בשורת הכתובת, הם נשמרים בהיסטוריה של הדפדפן, ואורך הכתובת מוגבל – אפשר להעביר מעט מידע.בדיקת התשובות:
| תשובה | נכון/לא נכון | למה |
|---|---|---|
| (1) | ✔ נכון | get – בכתובת, post – לא; ב-post אפשר להעביר יותר מידע. |
| (2) | ✘ | הפוך: דווקא ב-get הערכים מופיעים בכתובת, ו-get מוגבל בכמות. |
| (3) | ✘ | החלק הראשון נכון, אבל get אינו מאובטח יותר – הנתונים גלויים בכתובת. |
| (4) | ✘ | הפוך: ב-post הערכים לא מוצגים בשורת הכתובת. |
התשובה: (1).
בדיקה – בטופס <form method="get"> עם שדה name שבו הקלדנו dana, אחרי השליחה שורת הכתובת תסתיים ב-?name=dana; עם method="post" הכתובת נשארת בלי הנתונים.
טעות נפוצה: לחשוב ש-post "מצפין" את הנתונים. הוא רק לא מציג אותם בכתובת; הצפנה של התעבורה נעשית ב-HTTPS, בשתי השיטות.
שאלה 2 · מהו Session
מהו Session?
(1) אובייקט שנוצר בצד שרת עבור משתמש מסוים ויכול לשמש לאחסון מידע אישי עבורו.
(2) אובייקט שנוצר בשרת ומשותף לכל המשתמשים באתר
(3) פעולה שכתובה בצד הלקוח.
(4) שירות גישה לבסיס הנתונים.
מהו Session?
◂ פתרון מלא לשאלה 2
פתרון
(1)
מה נדרש – לבחור את ההגדרה הנכונה של Session.
רעיון – להבחין בין Session (לכל משתמש בנפרד) ובין Application (אחד, משותף לכל המשתמשים).
פתרון
Session הוא אובייקט שהשרת יוצר לכל משתמש (לכל דפדפן) בנפרד, כשהמשתמש נכנס לאתר. הוא נשמר בשרת לאורך כל הביקור באתר ועובר בין הדפים, ולכן משתמשים בו לשמור מידע אישי של המשתמש: האם הוא מחובר (Session["isLogin"]), שם המשתמש, סל קניות וכו'.
| תשובה | נכון/לא נכון | למה |
|---|---|---|
| (1) | ✔ נכון | זו בדיוק ההגדרה של Session. |
| (2) | ✘ | זה תיאור של Application – משותף לכל המשתמשים באתר. |
| (3) | ✘ | Session הוא אובייקט בצד שרת, לא פעולה בצד הלקוח. |
| (4) | ✘ | גישה לבסיס הנתונים נעשית עם SqlConnection, SqlCommand וכו', לא עם Session. |
התשובה: (1).
בדיקה – אם שני תלמידים נכנסים לאותו אתר ובכל אחד מהם מתבצע Session["name"] = ... עם שם אחר, כל אחד יראה את השם שלו; לעומת זאת Application["count"] הוא ערך אחד שכולם רואים.
שאלה 3 · Response.Write של טבלת HTML
לפניכם הפונקציה func(), רשמו מה יוצג על הדפדפן לאחר הרצת פונקציה
public void func()
{
string str = "<table border = '1'>";
int num;
for (int i=1; i<=10; i++)
{
str += "<tr>";
for (int j = 1; j <= 10; j++)
{
num = i * j;
str += "<td>" + num + "</td>";
}
str += "</tr>";
}
str += "</table>";
Response.Write(str);
}רשמו מה יוצג על הדפדפן לאחר הרצת פונקציה func().
◂ פתרון מלא לשאלה 3
פתרון
טבלה עם מסגרת (border=1) של 10 שורות ו-10 עמודות – לוח הכפל: בשורה i ובעמודה j מופיע i·j (מ-1 עד 100).
מה נדרש – לכתוב מה יופיע בדפדפן אחרי ש-func() בונה מחרוזת HTML ושולחת אותה עם Response.Write.
רעיון – המחרוזת str היא קוד HTML של טבלה. הלולאה החיצונית (i) בונה שורות <tr>, והפנימית (j) בונה בכל שורה 10 תאים <td> שבכל אחד הערך i*j. הדפדפן מציג את הטבלה – כלומר לוח הכפל.
פתרון
<table border = '1'> – טבלה עם מסגרת (קווי גבול סביב הטבלה ובין התאים).המחרוזת שנשלחת לדפדפן מתחילה כך:
<table border = '1'><tr><td>1</td><td>2</td><td>3</td> ... <td>10</td></tr><tr><td>2</td><td>4</td> ... <td>100</td></tr></table>ובדפדפן תוצג טבלה עם מסגרת, 10 שורות על 10 עמודות (בטבלה עצמה אין שורת כותרת – השורה הראשונה היא שורה רגילה 1..10):
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|
| 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 |
| 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 |
| 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | 40 |
| 5 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 |
| 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 54 | 60 |
| 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 |
| 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 |
| 9 | 18 | 27 | 36 | 45 | 54 | 63 | 72 | 81 | 90 |
| 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100 |
בדיקה – נוצרות 10 שורות ו-100 תאים; השורה הראשונה 1 2 … 10, השורה החמישית 5 10 15 … 50, והתא האחרון 100.
טעות נפוצה: לכתוב את תגיות ה-HTML עצמן כ"פלט". הדפדפן אינו מציג את התגיות <tr>/<td> – הוא מפרש אותן ומציג טבלה.
פרק שני
פרק שני (50 נקודות)
ענו על שלוש שאלות
על שאלה 4 (שאלת חובה - 10 נקודות)
ועל שתיים מתוך השאלות 5-7 (לכל שאלה - 20 נקודות)
ענו על שאלה 4 – חובה
(לפני שאלה 5:) ענו על שתיים מן השאלות 5-7:
שאלה 4 · בדיקת תעודת זהות ב-JavaScript
בטופס הרשמה לאתר ישנו שדה למילוי עבור תעודת הזהות, הרשום בצד השרת באופן הבא:
<input type="text" maxlength="9" name="idNumber" id="idNumber"/>לפניכם פונקציה ב-javascript שמבצעת בדיקה האם מס תעודת הזהות שהוקלד תקין.
תעודת זהות תקינה חייבת להכיל מחרוזת בעלת 9 תווים, וכל התווים הם ספרות.
בפונקציה חסרים חמישה ביטויים המסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (5), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
function isValidId()
{
var x = document.getElementById(____(1)____).value;
if (____(2)________)
return false;
if (x.length < 9)
____(3)______;
for (var i=0; i<x.length; i++)
{
if (____(4)_____ ))
return ____(5)____;
}
return true;
}בפונקציה חסרים חמישה ביטויים המסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (5), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
◂ פתרון מלא לשאלה 4
פתרון
(1) "idNumber"
(2) x == ""
(3) return false
(4) !(x.charAt(i) >= '0' && x.charAt(i) <= '9' (השורה המלאה: if (!(x.charAt(i) >= '0' && x.charAt(i) <= '9')))
(5) false
מה נדרש – להשלים 5 ביטויים בפונקציית JavaScript שמחזירה true רק אם בשדה idNumber יש בדיוק 9 תווים וכולם ספרות.
רעיון – לקרוא את ערך השדה לפי ה-id שלו, לפסול שדה ריק ואורך קטן מ-9, ואז לעבור תו-תו ולהחזיר false ברגע שמוצאים תו שאינו ספרה. אם הלולאה הסתיימה – כל התווים ספרות ⇒ true.
פתרון
| מספר | הביטוי החסר | הסבר |
|---|---|---|
| (1) | "idNumber" | getElementById מקבל את ה-id של הרכיב כמחרוזת (במירכאות). |
| (2) | x == "" | שדה ריק (המשתמש לא הקליד כלום) – אין תעודת זהות. קלט ארוך מ-9 לא צריך בדיקה כאן, כי בשדה מוגדר maxlength="9" ואי אפשר להקליד בו יותר מ-9 תווים. |
| (3) | return false | קלט קצר מ-9 תווים אינו תקין. |
| (4) | !(x.charAt(i) >= '0' && x.charAt(i) <= '9' | התנאי "התו במקום i אינו ספרה"; הסוגר ) הנוסף כבר מודפס אחרי הקו. |
| (5) | false | נמצא תו שאינו ספרה ⇒ ת"ז לא תקינה. |
הפונקציה המלאה:
function isValidId()
{
var x = document.getElementById("idNumber").value;
if (x == "")
return false;
if (x.length < 9)
return false;
for (var i=0;i<x.length;i++)
{
if (!(x.charAt(i) >= '0' && x.charAt(i) <= '9'))
return false;
}
return true;
}הערות על תשובות אפשריות נוספות:
x.length == 0, x.length > 9 או x.length != 9. שתי האחרונות מגינות גם על קלט ארוך מ-9 שעקף את maxlength (למשל אם שינו את הטופס בדפדפן).x[i] < '0' || x[i] > '9' (ואז בלי הסוגר הנוסף), או isNaN(x.charAt(i) (ואז השורה היא if (isNaN(x.charAt(i)))). שימו לב: isNaN(" ") מחזיר false, כי רווח מומר למספר 0, ולכן בגרסה עם isNaN הקלט "1234 5678" מתקבל בטעות כתקין.בדיקה:
| קלט | תוצאה |
|---|---|
123456789 | true |
012345678 | true |
12345678 (8 תווים) | false |
"" (ריק) | false |
1234567890 (10 תווים – אי אפשר להקליד בשדה בגלל maxlength) | true – הפונקציה לא בודקת זאת; עם x.length != 9 ב-(2) – false |
12345678a | false |
1234 5678 | false (בגרסת isNaN – true!) |
12345-678 | false |
טעות נפוצה: לכתוב ב-(1) idNumber בלי מירכאות (זה שם של משתנה שלא קיים), או להשתמש ב-name במקום ב-id.
שאלה 5 · הצגת טבלת תלמידים מבסיס הנתונים
במסד הנתונים קיימת טבלה - StudentsTable השומרת נתונים על האיחורים והחיסורים של התלמידים.
לפניכם פונקציה שכותרתה: protected void printTable(string connectionString)
הפונקציה מקבלת מחרוזת connectionString שמפנה לבסיס הנתונים

דוגמא לצורה בה הטבלה תודפס עם הנתונים הקיימים:
בפונקציה חסרים שמונה ביטויים מסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (8), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
protected void printTable(string connectionString)
{
string resultStr = "";
DataSet ds = new DataSet();
SqlConnection connection = new SqlConnection(______(1)_______);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = " ______(2)_______ FROM [dbo].[TableOfStudents]";
cmd.Connection = connection;
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(_____(3)___);
adapter._____(4)___ (_____(5)___);
connection.Close();
DataTable statTable = _____(6)_______;
resultStr += "<table dir=rtl><tr><th> שם פרטי </th><th> שם משפחה </th><th> מספר זהות </th>";
resultStr += "<th> כיתה </th><th> איחורים </th><th>חיסורים</th></tr>";
foreach (______(7)_______)
{
resultStr += "<tr>";
resultStr += "<td>" + dr["שם_פרטי"] + "</td>";
resultStr += "<td>" + dr["שם_משפחה"] + "</td>";
resultStr += "<td>" + dr["מספר_זהות"] + "</td>";
resultStr += "<td>" + dr["כיתה"] + "</td>";
resultStr += "<td>" + dr["איחורים"] + "</td>";
resultStr += "<td>" + dr["חיסורים"] + "</td>";
resultStr += "</tr>";
}
resultStr += "</table>";
______(8)_____ (resultStr);
}בפונקציה חסרים שמונה ביטויים מסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (8), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.
◂ פתרון מלא לשאלה 5
פתרון
(1) connectionString
(2) SELECT *
(3) cmd
(4) Fill
(5) ds
(6) ds.Tables[0]
(7) DataRow dr in statTable.Rows
(8) Response.Write
מה נדרש – להשלים 8 ביטויים בפונקציה שקוראת את כל טבלת התלמידים מבסיס הנתונים ומציגה אותה כטבלת HTML.
רעיון – הדרך הרגילה לקרוא טבלה:
חיבור (SqlConnection) ← פקודת SQL (SqlCommand) ← מתאם (SqlDataAdapter) שממלא DataSet ← לוקחים מה-DataSet את ה-DataTable ← עוברים על השורות (DataRow) ובונים מחרוזת HTML ← שולחים לדפדפן עם Response.Write.
פתרון
| מספר | הביטוי החסר | הסבר |
|---|---|---|
| (1) | connectionString | מחרוזת החיבור שהפונקציה קיבלה כפרמטר. |
| (2) | SELECT * | צריך את כל העמודות (שם פרטי, שם משפחה, מספר זהות, כיתה, איחורים, חיסורים). אפשר גם לפרט את שש העמודות במפורש. |
| (3) | cmd | המתאם מקבל את הפקודה, וממנה הוא יודע גם את השאילתה וגם את החיבור. |
| (4) | Fill | הפעולה שמריצה את השאילתה וממלאת את התוצאה. |
| (5) | ds | ה-DataSet שהוגדר בתחילת הפונקציה. |
| (6) | ds.Tables[0] | הטבלה הראשונה (והיחידה) שנוספה ל-DataSet. |
| (7) | DataRow dr in statTable.Rows | בגוף הלולאה משתמשים ב-dr["..."], לכן המשתנה הוא dr מטיפוס DataRow. |
| (8) | Response.Write | שולח את מחרוזת ה-HTML לדפדפן. |
הפונקציה המלאה:
protected void printTable(string connectionString)
{
string resultStr = "";
DataSet ds = new DataSet();
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT * FROM [dbo].[TableOfStudents]";
cmd.Connection = connection;
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(ds);
connection.Close();
DataTable statTable = ds.Tables[0];
resultStr += "<table dir=rtl><tr><th> שם פרטי </th><th> שם משפחה </th><th> מספר זהות </th>";
resultStr += "<th> כיתה </th><th> איחורים </th><th>חיסורים</th></tr>";
foreach (DataRow dr in statTable.Rows)
{
resultStr += "<tr>";
resultStr += "<td>" + dr["שם_פרטי"] + "</td>";
resultStr += "<td>" + dr["שם_משפחה"] + "</td>";
resultStr += "<td>" + dr["מספר_זהות"] + "</td>";
resultStr += "<td>" + dr["כיתה"] + "</td>";
resultStr += "<td>" + dr["איחורים"] + "</td>";
resultStr += "<td>" + dr["חיסורים"] + "</td>";
resultStr += "</tr>";
}
resultStr += "</table>";
Response.Write(resultStr);
}השאילתה (2) במלואה:
SELECT * FROM [dbo].[TableOfStudents];בדיקה – על עשר השורות מהדוגמה השאילתה מחזירה 10 שורות, למשל גל | דנון | 012333333 | יא | 0 | 3. הפונקציה המלאה בונה טבלת HTML עם שורת כותרות ועוד 10 שורות, למשל <tr><td>אילנה</td><td>בר</td><td>012999993</td><td>יב</td><td>9</td><td>15</td></tr> – בדיוק כמו בדוגמה.
הערה: בשאלה כתוב שהטבלה נקראת StudentsTable, אבל בקוד מודפס [dbo].[TableOfStudents]. ההשלמה (2) היא רק החלק שלפני FROM, ולכן שם הטבלה נשאר כמו בקוד.
טעות נפוצה: לכתוב ב-(7) DataRow dr in ds או dr in statTable – עוברים על אוסף השורות statTable.Rows; ולכתוב ב-(6) ds – ds הוא DataSet (אוסף טבלאות), לא DataTable.
protected void printTable(string connectionString)
{
string resultStr = "";
DataSet ds = new DataSet();
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT * FROM [dbo].[TableOfStudents]";
cmd.Connection = connection;
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(ds);
connection.Close();
DataTable statTable = ds.Tables[0];
resultStr += "<table dir=rtl><tr><th> שם פרטי </th><th> שם משפחה </th><th> מספר זהות </th>";
resultStr += "<th> כיתה </th><th> איחורים </th><th>חיסורים</th></tr>";
foreach (DataRow dr in statTable.Rows)
{
resultStr += "<tr>";
resultStr += "<td>" + dr["שם_פרטי"] + "</td>";
resultStr += "<td>" + dr["שם_משפחה"] + "</td>";
resultStr += "<td>" + dr["מספר_זהות"] + "</td>";
resultStr += "<td>" + dr["כיתה"] + "</td>";
resultStr += "<td>" + dr["איחורים"] + "</td>";
resultStr += "<td>" + dr["חיסורים"] + "</td>";
resultStr += "</tr>";
}
resultStr += "</table>";
Response.Write(resultStr);
}שאלה 6 · הפניה לפי סוג משתמש (Session)
במערכת הוגדרו שני משתני Session מטיפוס בוליאני, Session["isLogin"] , Session["admin"]
כתוב פונקציה בצד השרת שכותרתה protected void checkLogin()
פונקציה זו בודקת איזה סוג משתמש מחובר ומפנה לדף המתאים:
כתוב פונקציה בצד השרת שכותרתה protected void checkLogin() שבודקת איזה סוג משתמש מחובר ומפנה לדף המתאים.
◂ פתרון מלא לשאלה 6
פתרון
if (Session["isLogin"] != null && (bool)Session["isLogin"]) { if (Session["admin"] != null && (bool)Session["admin"]) Response.Redirect("admin.aspx"); else Response.Redirect("user.aspx"); } else Response.Redirect("home.aspx");
מה נדרש – פעולה בצד השרת שבודקת את שני משתני ה-Session ומפנה (Response.Redirect) לאחד משלושה דפים.
רעיון – קודם בודקים אם המשתמש מחובר. אם לא – home.aspx. אם כן – בודקים אם הוא מנהל: admin.aspx, אחרת user.aspx.
ב-Session הערכים נשמרים מטיפוס object, ולכן צריך המרה (bool). אם המשתנה עדיין לא הוגדר (למשל משתמש שנכנס לאתר בפעם הראשונה), Session["isLogin"] הוא null – לכן בודקים != null לפני ההמרה.
פתרון
protected void checkLogin()
{
if (Session["isLogin"] != null && (bool)Session["isLogin"] == true)
{
if (Session["admin"] != null && (bool)Session["admin"] == true)
Response.Redirect("admin.aspx");
else
Response.Redirect("user.aspx");
}
else
{
Response.Redirect("home.aspx");
}
}הנחנו שמנהל הוא משתמש מחובר שגם Session["admin"] שלו true (מנהל שאינו מחובר מופנה ל-home.aspx).
בדיקה:
| isLogin | admin | הפניה |
|---|---|---|
| true | true | admin.aspx |
| true | false | user.aspx |
| true | לא הוגדר | user.aspx |
| false | false | home.aspx |
| false | true | home.aspx |
| לא הוגדר | לא הוגדר | home.aspx |
טעות נפוצה: לכתוב if (Session["admin"] == true) – זו שגיאת הידור, כי Session מחזיר object. צריך (bool)Session["admin"]. וטעות נוספת: לבדוק קודם את admin בלי לבדוק שהמשתמש מחובר.
protected void checkLogin()
{
if (Session["isLogin"] != null && (bool)Session["isLogin"] == true)
{
if (Session["admin"] != null && (bool)Session["admin"] == true)
Response.Redirect("admin.aspx");
else
Response.Redirect("user.aspx");
}
else
{
Response.Redirect("home.aspx");
}
}שאלה 7 · מונה לחיצות – Page_Load ו-IsPostBack
לפניכם דף האינטרנט הבא (צד שרת):
תוכנית זו מטרתה להגדיל את הניקוד באחד עם כל לחיצה על הכפתור button, אולם התוכנית אינה מבצעת את הנדרש.
רשמו והסברו היכן התקלה וכיצד ניתן לתקנה.
<%@ Page Language="C#" %>
<script runat="server">
public int myScore;
public void Page_Load(object obj ,EventArgs e){
myScore = 1;
Session["theScore"] = myScore.ToString()
}
void Button1_Click(object sender, EventArgs e)
{
myScore = Convert.ToInt32(Session["theScore"]);
myScore = myScore + 1;
Session["theScore"] = myScore.ToString();
}
</script>
<html>
<head>
</head>
<body>
<form id="form1" runat="server">
<input type="button" runat="server" id="btn1" value="press here" onserverclick="Button1_Click" />
<div runat="server" id="div1">
Score is <% =myScore %> aaa
</div>
</form>
</body>
</html>רשמו והסברו היכן התקלה וכיצד ניתן לתקנה.
◂ פתרון מלא לשאלה 7
פתרון
התקלה ב-Page_Load: הפעולה רצה בכל בקשה – גם בכל לחיצה (postback), ולפני Button1_Click – ומאפסת את Session["theScore"] ל-1, ולכן אחרי כל לחיצה מוצג 2. התיקון: לבצע את האתחול רק בכניסה הראשונה – if (!IsPostBack) { myScore = 1; Session["theScore"] = myScore.ToString(); } (ובנוסף חסר ; בסוף השורה Session["theScore"] = myScore.ToString()).
מה נדרש – למצוא למה הניקוד לא עולה באחד בכל לחיצה, ולתקן.
רעיון – מחזור החיים של דף ASP.NET: כל לחיצה על הכפתור (runat="server" עם onserverclick) שולחת את הטופס לשרת (postback). בכל בקשה השרת יוצר אובייקט דף חדש (לכן השדה myScore מתחיל מחדש), מריץ קודם את Page_Load, אחר כך את פעולת האירוע Button1_Click, ורק אז מייצר את ה-HTML (<% =myScore %>). רק Session שומר ערך בין הבקשות.
פתרון
myScore = 1; Session["theScore"] = "1";. לכן בכל לחיצה: Page_Load מאפס את ה-Session ל-1 ⇐ Button1_Click קורא 1, מוסיף 1 ושומר 2 ⇐ מוצג "Score is 2". הניקוד נתקע על 2.Session["theScore"] = myScore.ToString() שב-Page_Load חסר ; בסוף (שגיאת הידור).התיקון – לאתחל את הניקוד רק בכניסה הראשונה לדף, כלומר כשהבקשה אינה postback, ולהוסיף את ה-;. קטע הסקריפט המתוקן:
public int myScore;
public void Page_Load(object obj, EventArgs e)
{
if (!IsPostBack) // only on the first visit to the page
{
myScore = 1;
Session["theScore"] = myScore.ToString();
}
else // postback: keep the value saved in Session
{
myScore = Convert.ToInt32(Session["theScore"]);
}
}
void Button1_Click(object sender, EventArgs e)
{
myScore = Convert.ToInt32(Session["theScore"]);
myScore = myScore + 1;
Session["theScore"] = myScore.ToString();
}ה-else אינו חובה (כאן כל postback נובע מלחיצה, ו-Button1_Click קורא את הערך מה-Session בעצמו), אבל הוא מבטיח שגם ב-postback אחר יוצג הניקוד הנוכחי ולא 0. שאר הדף נשאר כמו שהוא.
בדיקה – הדמינו את מחזור החיים (אובייקט דף חדש בכל בקשה, אותו Session, Page_Load ואחריו Button1_Click):
| בקשה | הדף המקורי (עם ה-; שנוסף) | הדף המתוקן |
|---|---|---|
| כניסה ראשונה | Score is 1 aaa | Score is 1 aaa |
| לחיצה 1 | Score is 2 aaa | Score is 2 aaa |
| לחיצה 2 | Score is 2 aaa | Score is 3 aaa |
| לחיצה 3 | Score is 2 aaa | Score is 4 aaa |
| לחיצה 4 | Score is 2 aaa | Score is 5 aaa |
והקוד כפי שהודפס (בלי ה-;) אכן אינו עובר הידור: error CS1002: ; expected.
טעות נפוצה: לחשוב שהשדה myScore "זוכר" את ערכו בין לחיצות. כל בקשה יוצרת אובייקט דף חדש, ולכן ערך שצריך להישמר בין לחיצות חייב להיות ב-Session (או ב-ViewState וכד').
public int myScore;
public void Page_Load(object obj, EventArgs e)
{
if (!IsPostBack) // only on the first visit to the page
{
myScore = 1;
Session["theScore"] = myScore.ToString();
}
else // postback: keep the value saved in Session
{
myScore = Convert.ToInt32(Session["theScore"]);
}
}
void Button1_Click(object sender, EventArgs e)
{
myScore = Convert.ToInt32(Session["theScore"]);
myScore = myScore + 1;
Session["theScore"] = myScore.ToString();
}פרק שלישי
פרק שלישי (20 נקודות)
ענה על שאלה אחת מן השאלות 8-9
שאלה 8 · XML Schema – טבלה מתאימה
לפניכם קובץ schema (הנח שהסימון xs שייך למרחב השמות החוקי).
<xs:element name="Student">
<xs:complexType>
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
<xs:element name="StudentType">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="A" />
<xs:enumeration value="B" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Age">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs: minExclusive="10"/>
<xs:maxExclusive="30"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Subject" type="xs:string" maxOccurs="10" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>רשמו דוגמה לטבלה המתאימה לפירוט הסכמה והכוללת שני מופעים.
רשמו דוגמה לטבלה המתאימה לפירוט הסכמה והכוללת שני מופעים.
◂ פתרון מלא לשאלה 8
פתרון
טבלה Student עם העמודות Name (מחרוזת), StudentType (רק A או B), Age (מספר שלם, 10 < Age < 30, כלומר 11–29) ו-Subject (0 עד 10 מקצועות). למשל: Dana Levi | A | 17 | Computer Science, Math, Physics ; Yossi Cohen | B | 25 | (אין מקצועות).
מה נדרש – לקרוא את הסכמה (XSD) ולתת דוגמה לטבלה עם שני מופעים (שתי רשומות) שמתאימים לה.
רעיון – כל רכיב בתוך xs:sequence הוא עמודה (בסדר הזה), וההגבלות (restriction) קובעות אילו ערכים מותרים בה.
פתרון
פירוט הסכמה – רכיב Student שבו, בסדר הזה:
| רכיב | טיפוס | הגבלה | כמה פעמים |
|---|---|---|---|
| Name | xs:string | – | בדיוק פעם אחת |
| StudentType | xs:string | enumeration – רק A או B | בדיוק פעם אחת |
| Age | xs:integer | minExclusive 10, maxExclusive 30 – גדול מ-10 וקטן מ-30 (גבולות לא כלולים), כלומר 11 עד 29 | בדיוק פעם אחת |
| Subject | xs:string | – | 0 עד 10 פעמים (minOccurs="0", maxOccurs="10") |
דוגמה לטבלה עם שני מופעים:
| Name | StudentType | Age | Subject |
|---|---|---|---|
| Dana Levi | A | 17 | Computer Science, Math, Physics |
| Yossi Cohen | B | 25 | (אין – 0 מקצועות) |
ושני המופעים כמסמכי XML:
<Student>
<Name>Dana Levi</Name>
<StudentType>A</StudentType>
<Age>17</Age>
<Subject>Computer Science</Subject>
<Subject>Math</Subject>
<Subject>Physics</Subject>
</Student><Student>
<Name>Yossi Cohen</Name>
<StudentType>B</StudentType>
<Age>25</Age>
</Student>הערה על הסכמה המודפסת: שתי שורות ההגבלה של Age כתובות לא נכון – <xs: minExclusive="10"/> ו-<xs:maxExclusive="30"/>. הצורה התקינה היא <xs:minExclusive value="10"/> ו-<xs:maxExclusive value="30"/>. הבנו אותן לפי הכוונה הברורה (10 < Age < 30).
בדיקה – בדקנו את שני המסמכים מול הסכמה (עם התיקון הנ"ל) – שניהם תקינים. עוד בדקנו: Age=11 ו-Age=29 תקינים; Age=10, Age=30, StudentType=C, 11 מקצועות, וסדר רכיבים שונה – נפסלים.
טעות נפוצה: לבחור גיל 10 או 30 – ב-Exclusive הגבול עצמו אינו מותר. ועוד: לשכוח ש-Subject יכול להופיע כמה פעמים (עד 10) או לא להופיע בכלל.
(XML אינו נכלל בחומר לבחינה לפי המיקוד העדכני; השאלה מופיעה בשאלון לדוגמה ולכן פתרנו אותה.)
שאלה 9 · טופס, Request.Form והפניה לדף
לפניכם התוכנית הבאה בדף Default.aspx (אין טעויות תחביר)
01. <%@ Page Language="C#" %>
02. <%@ Import Namespace="System.Data" %>
03. <%@ Import Namespace="System.Data.SqlClient" %>
04.
05. <script runat="server">
06. protected void Page_Load(object sender, EventArgs e)
07. {
08. string connectionString = @"Data Source=.\SQLEXPRESS;AttachDbFilename=localhost\dbName.mdf;Integrated Security=True";
09. SqlConnection connection = new SqlConnection(connectionString);
10. SqlCommand command = new SqlCommand("", connection);
11. SqlDataAdapter adapter = new SqlDataAdapter(command);
12.
13. if (Request.Form["tbxName"] != null)
14. {
15. MySQLDataAccess DataAccsess = new MySQLDataAccess();
16. DataTable dt = new DataTable();
17.
18. command.CommandText = string.Format("Select * From Names Where Name = '{0}'", Request.Form["tbxName"]);
19.
20. adapter.Fill(dt);
21.
22. if (dt.Rows.Count == 1)
23. {
24. Session["Key"] = "OK";
25. Response.Redirect("Page2.aspx");
26. }
27. }
28. }
29.
30. </script>
31. <html xmlns="http://www.w3.org/1999/xhtml">
32. <head runat="server">
33. <title>Exam</title>
34. </head>
35. <body>
36. <form id="form1" action="Exam.aspx" method="post">
37. <input type="text" name="tbxName" />
38. <input type="submit" value="Send" />
39. </form>
40. </body>
41. </html>לפניכם חמישה הגדים, רשמו עבור כל הגד נכון / לא נכון.
לפניכם חמישה הגדים, רשמו עבור כל הגד נכון / לא נכון.
◂ פתרון מלא לשאלה 9
פתרון
1 – נכון; 2 – נכון; 3 – לא נכון; 4 – לא נכון; 5 – נכון
מה נדרש – לקבוע לכל אחד מחמשת ההגדים אם הוא נכון.
רעיון – לעקוב מתי רץ Page_Load של Default.aspx ומה הוא עושה, ולשים לב במיוחד לשורה 36: <form id="form1" action="Exam.aspx" method="post"> – הטופס נשלח לדף Exam.aspx, לא לדף Default.aspx עצמו (וגם אין לו runat="server").
פתרון
מה הקוד עושה: Page_Load רץ בכל בקשה לדף Default.aspx. רק אם בבקשה יש שדה טופס בשם tbxName (שורה 13), נבנית שאילתה שמחפשת בטבלה Names את השם שהוקלד (שורה 18), התוצאה נטענת ל-dt (שורה 20), ואם התקבלה בדיוק שורה אחת (שורה 22) נשמר Session["Key"] = "OK" ומתבצע מעבר ל-Page2.aspx.
| הגד | תשובה | הסבר |
|---|---|---|
| 1 | נכון | Default.aspx רק "מסמן" ב-Session שהמשתמש עבר את הבדיקה. כל אחד יכול להקליד בדפדפן ישירות את הכתובת Page2.aspx, ולכן Page2.aspx חייב לבדוק ש-Session["Key"] שווה "OK" (ואם לא – להפנות חזרה), אחרת הבדיקה חסרת ערך. |
| 2 | נכון | Request.Form["tbxName"] שונה מ-null רק כשהבקשה כוללת נתוני טופס (post) שיש בהם שדה tbxName. בבקשה רגילה (get – הקלדת הכתובת, רענון) הוא null והשורות 15–25 לא מתבצעות. |
| 3 | לא נכון | (א) הטופס בדף נשלח ל-Exam.aspx, כך שהנתונים שמוקלדים בו כלל לא מגיעים ל-Page_Load של Default.aspx – גם שם שקיים בטבלה לא יגרום למעבר. (ב) וגם אילו הטופס היה נשלח ל-Default.aspx, התנאי אינו "השם שווה": המעבר קורה רק אם יש בדיוק שורה אחת מתאימה (שם שמופיע פעמיים – אין מעבר), והשאילתה נבנית ישירות מהקלט (string.Format), כך שקלט כמו x' OR '1'='1 מחזיר את כל הטבלה – ואם יש בה שורה אחת יהיה מעבר בלי שהשם קיים (SQL Injection). |
| 4 | לא נכון | שליחת הטופס מובילה ל-Exam.aspx, לא ל-Page2.aspx; וגם בבקשה עם נתוני טופס ל-Default.aspx המעבר קורה רק כשהשאילתה מחזירה שורה אחת. |
| 5 | נכון | כשנכנסים ל-Default.aspx (get) – אין נתוני טופס, ולכן שורה 13 לא מתקיימת. כששולחים את הטופס – הבקשה הולכת ל-Exam.aspx. כך שמהדף הזה, כפי שהוא כתוב, לא מגיעים לעולם ל-Response.Redirect("Page2.aspx"). |
כדי שהדף יעבוד כמתוכנן צריך לשנות בשורה 36 את action="Exam.aspx" ל-action="Default.aspx" (או להשמיט את action), ולבנות את השאילתה עם פרמטר (@name) במקום string.Format.
בדיקה – שורות 13–26 על טבלת הדוגמה:
| בקשה | תוצאה |
|---|---|
| get ל-Default.aspx (בלי טופס) | אין מעבר |
| post עם tbxName=Dana, בטבלה Dana, Avi | מעבר ל-Page2.aspx |
| post עם tbxName=Moshe (לא בטבלה) | אין מעבר |
| post עם tbxName=Dana, בטבלה Dana פעמיים | אין מעבר (2 שורות) |
post עם x' OR '1'='1, בטבלה רק Dana | מעבר ל-Page2.aspx (SQL Injection) |
| שליחת הטופס שבדף (action=Exam.aspx) | הבקשה הולכת ל-Exam.aspx – Default.aspx לא מבצע מעבר |
טעות נפוצה: לענות על הגד 4 "נכון" בלי לשים לב ל-action של הטופס, או לחשוב ש-dt.Rows.Count == 1 זהה ל"השם קיים בטבלה".
טבלת התשובות
| שאלה | סעיף | תשובה |
|---|---|---|
| 1 | - | (1) |
| 2 | - | (1) |
| 3 | - | טבלה עם מסגרת (border=1) של 10 שורות ו-10 עמודות – לוח הכפל: בשורה i ובעמודה j מופיע i·j (מ-1 עד 100). |
| 4 | - | (1) "idNumber" |
| 5 | - | (1) connectionString |
| 6 | - | if (Session["isLogin"] != null && (bool)Session["isLogin"]) { if (Session["admin"] != null && (bool)Session["admin"]) Response.Redirect("admin.aspx"); else Response.Redirect("user.aspx"); } else Response.Redirect("home.aspx"); |
| 7 | - | התקלה ב-Page_Load: הפעולה רצה בכל בקשה – גם בכל לחיצה (postback), ולפני Button1_Click – ומאפסת את Session["theScore"] ל-1, ולכן אחרי כל לחיצה מוצג 2. התיקון: לבצע את האתחול רק בכניסה הראשונה – if (!IsPostBack) { myScore = 1; Session["theScore"] = myScore.ToString(); } (ובנוסף חסר ; בסוף השורה Session["theScore"] = myScore.ToString()). |
| 8 | - | טבלה Student עם העמודות Name (מחרוזת), StudentType (רק A או B), Age (מספר שלם, 10 < Age < 30, כלומר 11–29) ו-Subject (0 עד 10 מקצועות). למשל: Dana Levi | A | 17 | Computer Science, Math, Physics ; Yossi Cohen | B | 25 | (אין מקצועות). |
| 9 | - | 1 – נכון; 2 – נכון; 3 – לא נכון; 4 – לא נכון; 5 – נכון |