由於辭職的原因,最近正在忙於找工作。在這段期間收到了一家公司的上機測試題,一共兩道題,其中一道題是關於REST API的應用。雖然在面試時,我已經說過,不懂REST,但那面試PM還是給了一道這題讓我做。面試的PM還是比較友好,沒有限定時間,結果自己不爭氣,一邊查資料一邊做,一個多小時過了還是沒做出來,所以最後我放棄了,當然面試也就失敗了。於是歸納了一下失敗的原因,主要還是對REST不瞭解,把REST當做Web Service的另一種形式,先入為主的理解錯誤,必然會導致了失敗。

迴歸正傳,什麼是REST? 在此不詳說。我說一下自己的理解吧。簡單地說,REST就是一種基本HTTP請求服務從而達到操作資源的技術,其支援多種資料格式,比如xml、Json、Csv等等。Web Service是基本XML並利用SOAP協議實現遠端資源訪問操作的技術。因此,兩者本質是是不同的。也許我的理解不完全正確,歡迎指出錯誤。

下面來看看這道題,如下:

Userstory:
As a user I want to be able to see maximum, minimum and average predicted temperature for the following 3 days,
based on the city that needs to be entered.
Acceptance criteria:
Windows form application written in C#
One input field to define the city
A combobox to select Fahrenheit or Celcius degrees
Use free web service provided by http://www.worldweatheronline.com
The following key can be used to retrieve data : c6bbde7c9c021451123107
Show 'Retrieving weather data...' in status bar when request of data is submitted. Once data is retrieved
update status accordingly.
Use of linq statements to calculate the minimum, maximum and average temperature.
Main form needs to be responsive at all times
Use of delegates
Retrieval of data and UI logic needs to be seperated in code
Error handling when connection

以下是我寫的程式碼:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Threading; namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Init();
} private void Init()
{
//初始化單位資料
List<object> list = new List<object>();
list.Add(new{key="Celsius", value="C"});
list.Add(new { key = "Fahrenheit", value = "F" });
cbUnit.DataSource = list;
cbUnit.DisplayMember = "key";
cbUnit.ValueMember = "value";
cbUnit.SelectedIndex = 0;
} private void btCalculate_Click(object sender, EventArgs e)
{
try
{
slStatus.Text = "Retrieving weather data.....";
string city = tbCity.Text;
string unit = cbUnit.SelectedValue.ToString();
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("city", city);
dic.Add("unit", unit);
Thread thread = new Thread(new ParameterizedThreadStart(ThreadProcess));
thread.IsBackground = true;
thread.Start(dic);
}
catch (Exception ex)
{
slStatus.Text = "獲取資料失敗....";
} }
/// <summary>
/// 後臺處理執行緒
/// </summary>
/// <param name="obj"></param>
private void ThreadProcess(object obj)
{
Dictionary<string, string> dic = obj as Dictionary<string, string>;
XDocument xmlDoc = GetData(dic["city"]);
SetData(xmlDoc, dic["unit"]);
slStatus.Text = "";
}
/// <summary>
/// 獲取資料
/// </summary>
/// <param name="city"></param>
/// <returns></returns>
private XDocument GetData(string city)
{
XDocument doc;
Uri uri = new Uri(string.Format(@"http://free.worldweatheronline.com/feed/weather.ashx?q={0},china&format=xml&num_of_days=3&key=c6bbde7c9c021451123107",city));
HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
string result=reader.ReadToEnd();
StringReader str = new StringReader(result);
doc=XDocument.Load(str);
}
return doc; } /// <summary>
/// 設定資料
/// </summary>
/// <param name="doc"></param>
/// <param name="unit"></param>
private void SetData(XDocument doc,string unit)
{ var query = from p in doc.Root.Elements("weather")
select new {
Date = p.Element("date") == null ? null : p.Element("date").Value,
MaxC = p.Element("tempMaxC") == null ? null : p.Element("tempMaxC").Value,
MinC = p.Element("tempMinC") == null ? null : p.Element("tempMinC").Value,
MaxF = p.Element("tempMaxF") == null ? null : p.Element("tempMaxF").Value,
MinF = p.Element("tempMinF") == null ? null : p.Element("tempMinF").Value
}; if (unit == "C")
{
CrossThreadAccess(tbMaximum, query.Max(p => p.MaxC));
CrossThreadAccess(tbMinimum, query.Min(p => p.MinC)); }
else
{
CrossThreadAccess(tbMaximum, query.Max(p => p.MaxF));
CrossThreadAccess(tbMinimum, query.Min(p => p.MinF)); }
CrossThreadAccess(tbAverage, Convert.ToString((Convert.ToInt32(tbMaximum.Text) + Convert.ToInt32(tbMinimum.Text)) / 2)); } /// <summary>
/// UI執行緒間訪問
/// </summary>
/// <param name="control"></param>
/// <param name="value"></param>
private void CrossThreadAccess(Control control,string value)
{
if (control.InvokeRequired)
{
control.Invoke(new Action<Control, string>(CrossThreadAccess), control, value);
}
else
{
TextBox tb = control as TextBox;
if (tb != null) tb.Text = value; }
} }
}

效果如下: