HttpWebRequest详解
There is no class in the .NET Compact Framework that raises events to inform an application when connection to the network is lost and regained. Instead, you must code with this eventuality in mind and make sure your application is tolerant of network outages. You might need to write code that polls for a network resource to determine when the device has network connectivity.
Understanding WebRequest and WebResponse Basics
The most important classes in the System.Net namespace are the WebRequest and WebResponse classes, which are abstract classes.
using System.IO;
WebRequest req = WebRequest.Create(uri);
WebResponse res = req.GetResponse(); // GetResponse blocks until the response arrives
Stream ReceiveStream = res.GetResponseStream(); // Read the stream into a string
StreamReader sr = new StreamReader( ReceiveStream );
string resultstring = sr.ReadToEnd();
Fetching a Resource Using HTTP
If the scheme of the URI is http:// or https://, the Create method returns an instance of the HttpWebRequest class. The GetResponse method of this instance makes a request to the resource and returns an HttpWebResponse instance that contains the response.
using System;
using System.IO;
using System.Net;
namespace NETCFDevelopersReference
{
class HttpGetSample
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{
HttpGetSample thisclass = new HttpGetSample();
thisclass.getPage("target URL here"); // <-- EDIT THIS!
}
public void getPage(String url)
{
WebResponse result = null;
try
{
WebRequest req = WebRequest.Create(url);
result = req.GetResponse();
Stream ReceiveStream = result.GetResponseStream();
//read the stream into a string
StreamReader sr = new StreamReader( ReceiveStream );
string resultstring = sr.ReadToEnd();
Console.WriteLine("\r\nResponse stream received");
Console.WriteLine(resultstring);
}
catch(Exception exp)
{
Console.Write("\r\nRequest failed. Reason:");
Console.WriteLine(exp.Message);
}
finally
{
if ( result != null )
{
result.Close();
}
}
Console.WriteLine("\r\nPress Enter to exit.");
Console.ReadLine();
}
}
}
The Finally block ensures that the WebResponse object is closed, regardless of the success or failure of the program. This is important to ensure that network resources are not wasted.
Handling Errors
You get an exception of type UriFormatException when the URI cannot be parsed or an exception of type WebException for other errors. The Status property of WebException returns a value from the WebExceptionStatus enumeration. If Status is WebExceptionStatus.ProtocolError, the exception is a result of an HTTP protocol error. You can then access the WebException.Response property, which returns an HttpWebResponse object containing the response from the server. Use the HttpWebResponse.StatusCode property to find out the HTTP status code the server sent back, and use HttpWebResponse.StatusDescription to obtain the description string that corresponds to that status code.
The HttpWebRequest class allows you to control the HTTP headers sent with your request, which you do by setting properties or by executing methods of the class.
HTTP Headers Sent |
|
HTTP Header |
Set By |
Accept |
HttpWebRequest.Accept property |
Connection |
HttpWebRequest.Connection and HttpWebRequest.KeepAlive properties |
Content-Length |
HttpWebRequest.ContentLength property |
Expect |
HttpWebRequest.Expect property |
Date |
Set to the current system date |
Host |
Not set |
If-Modified-Since |
HttpWebRequest.IfModifiedSince property |
Range |
HttpWebRequest.AddRange method |
Referer |
HttpWebRequest.Referer property |
Transfer-Encoding |
HttpWebRequest.TransferEncoding property (the HttpWebRequest.SendChunked property must be true) |
User-Agent |
HttpWebRequest.UserAgent property |
The HttpWebResponse instance exposes HTTP headers as properties in a similar way.
HTTP Headers Received |
|
HTTP Header |
Read By |
Content-Encoding |
HttpWebResponse.ContentEncoding property |
Content-Length |
HttpWebResponse.ContentLength property |
Content-Type |
HttpWebResponse.ContentType property |
Length |
HttpWebResponse.Length property |
Server |
HttpWebResponse.Server property |
Both classes also have a Headers property that exposes HTTP headers as a System.Net.WebHeaderCollection instance. For example, instead of using the UserAgent property, you can add the User-Agent header using the following code:
req.Headers.Add("User-Agent", "dotNETCF Test Program");
HttpWebResponse res = (HttpWebResponse) req.GetResponse();
String server = res.Headers["Server"];
Network Transfer Encoding
All data transferred over the network is encoded. The Windows CE operating system uses Unicode to represent characters, but this requires at least 2 bytes to represent any character, which is not as efficient as the U.S. ASCII character set, which requires only one byte per character. But ASCII represents only commonly used characters in Western alphabets, so it is unusable in languages requiring other characters, such as European languages, Chinese, Japanese, or Arabic.
The compromise solution is to use character encodings. The most common of these is UTF-8, which is a variable-length multibyte encoding requiring a single byte for 7-bit characters that are in the U.S. ASCII character set and 2 bytes for characters outside it. This results in efficient encodings for Western alphabets and the flexibility to handle non-Latin characters.
The default encoding assumed by the HttpWebRequest and HttpWebResponse classes is UTF-8
You can determine the character set encoding of a response by getting the HttpWebResponse.CharacterSet property. However, beware, there is a trick to this! You must set the MediaType property on the request; otherwise, the CharacterSet property on the HttpWebResponse will be blank. Even then, CharacterSet will only return the character set encoding used if the first part of the Content-Type header returned with the response (which is the media type) matches the value you set in the MediaType property of the request.
For example, if you set HttpWebRequest.MediaType to “text/html” before calling HttpWebRequest.GetResponse, and the Content-Type header returned with the response is “text/html; charset=utf-8”, the HttpWebResponse.CharacterSet property will contain the name of the character set encoding (“utf-8”) because the “text/html” media type sent in the first part of the Content-Type header matches the value set in HttpWebRequest.MediaType. If the media types differ, HttpWebResponse.CharacterSet is a null string.
HttpWebRequest req = (HttpWebRequest) WebRequest.Create(url);
// You MUST set the MediaType property on the request, otherwise
// the CharacterSet property on the HttpWebResponse object will be
// blank.
req.MediaType = "text/html";
HttpWebResponse result = (HttpWebResponse) req.GetResponse();
// Note that the HttpWebResponse.ContentType property always
// returns the Content-Type header, which will be something like
// "text/html; charset=utf-8"
string contenttype = result.ContentType;
// Character set encoding may be something like ISO-10646-UCS-2,
// or UTF-8, us-ascii etc
string charsetencoding = result.CharacterSet;
//read the stream using the decoder
Stream ReceiveStream = result.GetResponseStream();
Encoding encode = System.Text.Encoding.GetEncoding(charsetencoding);
StreamReader sr = new StreamReader( ReceiveStream, encode );
Using HTTP GET Query String
In the full .NET Framework, the System.Web.HttpUtility class provides methods to encode and decode URL-encoded data, but this class is not supported in the .NET Compact Framework. You must write your own code to handle URL encoding, such as the example code shown here:
using System;
using System.IO;
namespace NETCFDevelopersReference
{
/// <summary>
/// Replacement for HttpUtility.UrlEncode
/// </summary>
public class HttpUtility
{
public static string UrlEncode(string instring)
{
StringReader strRdr = new StringReader(instring);
StringWriter strWtr = new StringWriter();
int charValue = strRdr.Read();
while (charValue != -1)
{
if (((charValue >= 48) && (charValue <= 57)) // 0-9
││((charValue >= 65) && (charValue <= 90)) // A-Z
││((charValue >= 97) && (charValue <= 122))) // a-z
strWtr.Write((char) charValue);
else if (charValue == 32) // Space
strWtr.Write('+');
else
strWtr.Write("%{0:x2}", charValue);
charValue = strRdr.Read();
}
return strWtr.ToString();
}
}
}
Using HTTP POST
To use POST, just set the HttpWebRequest.Method property to “POST” and then proceed as for GET. A typical HTML page shown in a Web browser posts data as key=value pairs, with the data URL-encoded, so your application must format the data in that way if that is what the Web server application expects. If you are simply posting application content or XML, just post it as is.
public void doPost(String url, String payload)
{
WebRequest req = WebRequest.Create(url);
req.Method = "POST";
req.ContentType = "text/plain; charset=utf-8";
// Encode the data
byte[] encodedBytes = Encoding.UTF8.GetBytes(payload);
req.ContentLength = encodedBytes.Length;
// Write encoded data into request stream
Stream requestStream = req.GetRequestStream();
requestStream.Write(encodedBytes, 0, encodedBytes.Length);
requestStream.Close();
WebResponse result = req.GetResponse();
}
Alternatives to Cookies for Session State Support
The .NET Compact Framework implementation of the HttpWebResponse class does not support the Cookies property found in the full .NET Framework implementation.
If you are using an ASP.NET application on the server, you can utilize the ASP.NET support for cookieless clients to track your smart device applications session. You enable this by setting cookieless to “true” in the sessionState configuration in the ASP.NET application’s Web.Config
ASP.NET uses a munged URL to track sessions for cookieless clients, meaning it embeds a session ID into the response URI. For example, if your application makes a request to http://myserver/myserverapp.aspx, the response URI that returns to the client is actually something like http://myserver/(1en4l345qq203lr2f0h4xt45)/myserverapp.aspx. To the client, it looks like a server-side redirect. In the client application, this response URI is in the HttpWebResponse.ResponseUri property, so the client application need only use this URI for subsequent requests for the server application to be able to identify those requests as coming from that particular client.
Authenticating Requests with the NetworkCredential Class
NetworkCredential creds =
new NetworkCredential("andy", "pA55w0rd", "");
req.Credentials = creds;
WebResponse result = req.GetResponse();
Making Network Requests via a Proxy Server
WebProxy myProxy = new WebProxy(
"http://myproxyserver.mine.com:8080", true);
// If this proxy requires authentication
myProxy.Credentials =
new NetworkCredential("loginname","password");
Request.Proxy = myProxy;
Asynchronous Web Requests
using System;
using System.IO;
using System.Threading;
namespace NETCFDevelopersReference
{
class HttpGetAsyncSample
{
public static void Main(string[] args)
{
HttpGetAsyncSample thisclass = new HttpGetAsyncSample();
thisclass.Run();
}
private bool isComplete;
public void Run()
{
Console.WriteLine();
Console.WriteLine("Test Program makes HTTP AsyncGet"
+ " to http://www.gotdotnet.com/team/netcf");
Console.WriteLine("Press Enter to continue");
Console.ReadLine();
GetHttpAsync callit = new GetHttpAsync();
// Wire up to handle the complete event
callit.GetComplete += new
GetHttpAsync.GetCompleteHandler(this.GetCompleteHandler);
// Set the flag we will check for completion
isComplete = false;
Console.WriteLine("Making async call");
callit.getPage("http://www.gotdotnet.com/team/netcf");
Console.WriteLine("Main thread writes these dots, "
+ "while async fetch proceeds");
Console.WriteLine("in secondary thread");
int count = 0;
while (!isComplete)
{
Console.Write(".");
if (++count == 80) Console.WriteLine();
Thread.Sleep(500); // Sleep for half a second
}
Console.WriteLine("\r\nResponse Received");
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
return;
}
protected void GetCompleteHandler()
{
// Event handler for get complete
isComplete = true;
}
}
}
using System;
using System.Net;
using System.IO;
using System.Text;
namespace NETCFDevelopersReference
{
// The RequestState class is used to pass data
// across async calls
public class RequestState
{
public HttpWebRequest Request;
public RequestState()
{
Request = null;
}
}
/// <summary>
/// Class makes asynchronous HTTP Get to remote URL
/// </summary>
public class GetHttpAsync
{
public delegate void GetCompleteHandler();
public event GetCompleteHandler GetComplete;
public void getPage(String url)
{
try
{
HttpWebRequest req =
(HttpWebRequest) WebRequest.Create(url);
// Create the state object
RequestState rs = new RequestState();
// Add the request into the state
// so it can be passed around
rs.Request = req;
// Issue the async request
req.BeginGetResponse(
new AsyncCallback(this.ResponseCallback), rs);
}
catch(Exception exp)
{
Console.WriteLine("\r\nRequest failed. Reason:");
while (exp != null)
{
Console.WriteLine(exp.Message);
exp = exp.InnerException;
}
}
}
private void ResponseCallback(IAsyncResult ar)
{
// Get the RequestState object from the async result
RequestState rs = (RequestState) ar.AsyncState;
// Get the HttpWebRequest from RequestState
HttpWebRequest req = rs.Request;
// Get the HttpWebResponse object
HttpWebResponse resp =
(HttpWebResponse) req.EndGetResponse(ar);
// Read data from the response stream
Stream responseStream = resp.GetResponseStream();
StreamReader sr =
new StreamReader( responseStream, Encoding.UTF8);
string strContent = sr.ReadToEnd();
// Write out the first 512 characters
Console.WriteLine("Length: {0}", strContent.Length);
Console.WriteLine(strContent.Substring(0,
strContent.Length < 512 ? strContent.Length : 512));
// Raise the completion event
GetComplete();
// Close down the response stream
responseStream.Close();
}
}
}