站在世界的浪尖

有你就有奇迹
 
 

Powered by: 博客园
模板提供:沪江博客
博客园 | 首页 | 发新随笔 | 发新文章 | 联系 | 订阅订阅 | 管理

2011年4月11日

Objective-读Xml

有两种方式:

一:NSXMLParser

 

Utility.h:

#import <Foundation/Foundation.h>
@interface Utility : NSObject {

}

//获取文件在设备上的绝对路径

+ (NSString *) pathForResource:(NSString*)resourcepath;
@end

 

Utility.m :

 #import "Utility.h"


@implementation Utility


+ (NSString*) pathForResource:(NSString*)resourcepath
{
    NSBundle * mainBundle = [NSBundle mainBundle];
    NSMutableArray *directoryParts = [NSMutableArray arrayWithArray:[resourcepath componentsSeparatedByString:@"/"]];
    NSString       *filename       = [directoryParts lastObject];
    [directoryParts removeLastObject];
    
    NSString *directoryStr = @"Xml";
    NSString *path= [mainBundle pathForResource:filename
                                         ofType:@""
                                    inDirectory:directoryStr];
    
    return path;
}

@end

 

 - (void)viewDidLoad {

  NSURL *xmlUrl = [NSURL fileURLWithPath:[Utility pathForResource:@"SearchLog.xml"]];
     NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:xmlUrl];
        
     [parser setDelegate:self];
     [parser setShouldProcessNamespaces:NO];
     [parser setShouldReportNamespacePrefixes:NO];
     [parser setShouldResolveExternalEntities:NO];
    

 /*启动Xml解析,会触发

   parser:
        didStartElement:
        namespaceURI:
        qualifiedName:
        attributes:*/

  [parser parse];
    

  //刷新Table
    // [searchTableView reloadData];
        
     NSError *parseError = [parser parserError];
     if (parseError) {
            
     }
        
     [parser release];

 }

 
- (void)parser:(NSXMLParser *)parser
        didStartElement:(NSString *)elementName
        namespaceURI:(NSString *)namespaceURI
        qualifiedName:(NSString *)qName
        attributes:(NSDictionary *)attributeDict{
    
    if (qName) {
        elementName = qName;
    }
    
    if (xmlSearchParsedCounter >= 5) {
        [parser abortParsing];
    }
    
    if ([elementName isEqualToString:@"item"]) {
        xmlSearchParsedCounter++;
        NSString *value = [attributeDict valueForKey:@"value"];
        [self.searchList addObject:value];
    }
}

 二:libxml

参考:http://blog.prosight.me/index.php/2010/02/586

  1. 展开Targets
  2. 双击项目名
  3. 选择所有配置
  4. 搜索Header Search Path
  5. 加入一行并选中recursive选项: ${SDKROOT}/usr/include/libxml2
  6. 搜索Other Linker Flag
  7. 加入一行:-lxml2

Utility.h:


#import <Foundation/Foundation.h>
#import "TFHpple.h"

@interface Utility : NSObject {

}

+ (NSString *) pathForResource:(NSString*)resourcepath;
+ (NSMutableArray *)getXmlWithFileName:(NSString  *)fileName XPath: (NSString *)xpath;

@end

 

Utility.m:

 
#import "Utility.h"


@implementation Utility


+ (NSString*) pathForResource:(NSString*)resourcepath
{
    NSBundle * mainBundle = [NSBundle mainBundle];
    NSMutableArray *directoryParts = [NSMutableArray arrayWithArray:[resourcepath componentsSeparatedByString:@"/"]];
    NSString       *filename       = [directoryParts lastObject];
    [directoryParts removeLastObject];
    
    NSString *directoryStr = @"Xml";
    NSString *path= [mainBundle pathForResource:filename
                                         ofType:@""
                                    inDirectory:directoryStr];
    
    return path;
}


+ (NSMutableArray *)getXmlWithFileName:(NSString  *)fileName XPath: (NSString *)xpath{
    NSURL *xmlUrl = [NSURL fileURLWithPath:[Utility pathForResource:fileName]];
    
    NSData *data = [[NSData alloc] initWithContentsOfURL:xmlUrl];
    
    TFHpple *xpathParser = [[TFHpple alloc] initWithXMLData:data];        
    NSArray *elements  = [xpathParser search:xpath];
    NSMutableArray *array = [[NSMutableArray alloc] init];
    for(TFHppleElement *element in elements) {
        [array addObject:[element content]];
    }
    
    [xpathParser release];
    [data release];
    
    return array;
}

@end

 

 调用例子:

 searchList = [Utility getXmlWithFileName:@"SearchLog.xml" XPath:@"//SearchLog/Item/text()"];

 

posted @ 2011-04-11 22:18 Edison.Feng 阅读(102) 评论(0) 编辑
 

2011年4月10日

[转]iPhone Mapkit 之在地图加入坐标点 使用MKAnnotation和MKAnnotationView

iPhone Mapkit 之在地图加入坐标点

iPhone Developme 2010-01-22 00:01:24 阅读948 评论0   字号:大中小 订阅

 

☉目標:在前一個範例中建立的Google Map上,加上座標點(POIs),當點選座標點會觸發對應的event。
☉步驟說明:
在 地圖上每一個座標點,都是一個MKAnnotationView,也就是UI。而每一個MKAnnotationView都需要有對應的資料 MKAnnotation,這是Protocal,也就是儲存每個座標點所需要用到的資料的地方。因此,我們要先建立一個使用MKAnnotation的 類別。

依照iPhone開發者文件的說明。這個Protocal需要宣告三個屬性和一個初始化方法。三個屬性分別是coordinate、title、subtitle,和一個方法initWithCoords。

下面是MKAnnotation類別的程式碼 POI.h

 
  1. #import <FOUNDATION foundation.h>   
  2. #import <MAPKIT mapkit.h>   
  3. #import <CORELOCATION corelocation.h>   
  4.   
  5. @interface POI : NSObject <MKANNOTATION> {   
  6.   
  7.     CLLocationCoordinate2D coordinate;   
  8.     NSString *subtitle;   
  9.     NSString *title;   
  10. }   
  11.   
  12. @property (nonatomic,readonly) CLLocationCoordinate2D coordinate;   
  13. @property (nonatomic,retain) NSString *subtitle;   
  14. @property (nonatomic,retain) NSString *title;   
  15.   
  16. -(id) initWithCoords:(CLLocationCoordinate2D) coords;   
  17.   
  18. @end  

下面是MKAnnotation類別的程式碼 POI.m

 
  1. #import "POI.h"   
  2.   
  3. @implementation POI   
  4.   
  5. @synthesize coordinate,subtitle,title;   
  6.   
  7. - (id) initWithCoords:(CLLocationCoordinate2D) coords{   
  8.        
  9.     self = [super init];   
  10.        
  11.     if (self != nil) {   
  12.            
  13.         coordinate = coords;    
  14.            
  15.     }   
  16.        
  17.     return self;   
  18.        
  19. }   
  20.   
  21. - (void) dealloc   
  22.   
  23. {   
  24.     [title release];   
  25.     [subtitle release];   
  26.     [super dealloc];   
  27. }   
  28.   
  29. @end  

宣告了符合MKAnnotation Protocal的類別後,我們就要在Google Map上建立座標點。在iPhone上顯示Google Map的程式可以參考使用MKMapView實作Google Map。

接下來,
Step(1): 我宣告了一個函式createMapPoint用來建立座標點。在這裡用到了我們在前面宣告的類別POI(這是一個符合MKAnnotation Protocal的類別),我們Create一個POI,接著將座標點所需的經緯度、標題、子標題等訊息都放進去。接著呼叫

 
  1. [mapView addAnnotation:poi];  

把我們所建立的POI加入地圖(MKMapView)的Annotation集合中。放入集合的只是座標點的資料,這個時候還沒有真正建立座標點。

以下是函式createMapPoint的程式碼:

 
  1. #import "POI.h"   
  2.   
  3. -(void*) createMapPoint:(MKMapView *)mapView coordinateX:(double)coorX coordinateY:(double)coorY   
  4.                   Title:(NSString*)title Subtitle:(NSString*)subtitle{   
  5.        
  6.        
  7.        
  8.     if(mapView!=nil){   
  9.            
  10.         //set POI lat and lng   
  11.         CLLocationCoordinate2D p1;   
  12.         POI *poi;   
  13.            
  14.         if(coorX && coorY){   
  15.                
  16.             p1.latitude=coorX;   
  17.             p1.longitude = coorY;   
  18.             poi = [[POI alloc] initWithCoords:p1];    
  19.                
  20.             if(title!=NULL)   
  21.                 poi.title=title;   
  22.                
  23.             if(subtitle!=NULL)   
  24.                 poi.subtitle=subtitle;   
  25.              mapView.delegate = self;         
  26.             [mapView addAnnotation:poi];   
  27.             [poi release];   
  28.                
  29.         }   
  30.            
  31.     }   
  32.     return NULL;   
  33. }     

Step(2):參考MKMapView的說明文件可以看到viewForAnnotation這個方法,這是MKMapView實際建立座標點的地方。MKMapView類別在render地圖的時候會依照Annotation集合的資料建立座標點。

 
  1. - (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{   
  2.        
  3.     //方法一:using default pin as a PlaceMarker to display on map   
  4.     MKPinAnnotationView *newAnnotation = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"annotation1"];   
  5.     newAnnotation.pinColor = MKPinAnnotationColorGreen;   
  6.     newAnnotation.animatesDrop = YES;    
  7.     //canShowCallout: to display the callout view by touch the pin   
  8.     newAnnotation.canShowCallout=YES;   
  9.        
  10.     UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];   
  11.     [button addTarget:self action:@selector(checkButtonTapped:event:) forControlEvents:UIControlEventTouchUpInside];   
  12.     newAnnotation.rightCalloutAccessoryView=button;    
  13.   
  14.     return newAnnotation;   
  15.        
  16.        
  17.     //方法二:using the image as a PlaceMarker to display on map   
  18.     /*  
  19.      MKAnnotationView *newAnnotation=[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"annotation1"];  
  20.      newAnnotation.image = [UIImage imageNamed:@"icon.png"];  
  21.      newAnnotation.canShowCallout=YES;  
  22.      return newAnnotation;  
  23.      */  
  24. }   
  25.    

Annotation集合中有幾筆資料vieForAnnotation方法就會被執行幾次。 因此每次viewForAnnotation被執行,我們都要建立一個MKAnnotationView物件的實體,並且return這個實體。 MKMapView接收到MKAnnotationView的實體就會將它顯示在地圖上,這就是我們想要顯示在地圖上的座標點。在上面程式碼中使用了 MKPinAnnotationView這個物件是繼承自MKAnnotationView,作用就是在地圖上顯示一個大頭釘。你可以用

 
  1. annotationView.pinColor = MKPinAnnotationColorGreen;   

設定大頭釘的顏色,不過只有紅色、紫色、綠色,三種顏色(似乎有點稀少XD)。

 
  1. newAnnotation.canShowCallout=YES;   

設定在點選大頭釘的時候氣泡視窗是否會談出來。 第10行到第12行動態建立了一個DetailDisclousue類型的按鈕,替這個按鈕設置了一個 UIControlEventTouchUpInside事件,並將它放入氣泡視窗的AccessoryView中。最後,將建立好的座標點回傳給 MapView。 被註解的方法二是直接使用MKAnnotationView建立座標點,並且設置她的image屬性,因此座標點不一定是大頭釘,可以有更多的變化與花樣。

 
  1. - (void)checkButtonTapped:(id)sender event:(id)event{   
  2.   
  3.     UIAlertView *tmp= [[UIAlertView alloc] initWithTitle:@"訊息!" message:@"Callout測試" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];   
  4.     [tmp show];   
  5.     [tmp release];   
  6. }   

上面是UIControlEventTouchUpInside事件所執行的事件處理常式。當點選DetailDisclousue按鈕後,就會跳出一個alert,表示這個event有正確無誤的被處理。

從這個範例,我們可以看到MKAnnotation和MKAnnotationView之間的關係。

 

copy from http://blog.finalevil.com/

posted @ 2011-04-10 17:29 Edison.Feng 阅读(1684) 评论(0) 编辑
 

2011年2月25日

[转]Android:使用URL和URLConnection(多线程下载)
使用URL和URLConnection(多线程下载)
2010-02-22 13:47

17.2.3 使用URL和URLConnection

URL(Uniform Resource Locator)对象代表统一资源定位器,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象引用,例如对数据库或搜 索引擎的查询。通常情况而言,URL可以由协议名、主机、端口和资源组成。即满足如下格式:

protocol://host:port/resourceName

例如如下的URL地址:

http://www.oneedu.cn/Index.htm

JDK中还提供了一个URI(Uniform Resource Identifiers)类,其实例代表一个统一资源标识符,Java的URI不能用于定位任何资源,它的唯一作用就是解析。与此对应的是,URL则包含 一个可打开到达该资源的输入流,因此我们可以将URL理解成URI的特例。

URL类提供了多个构造器用于创建URL对象,一旦获得了URL对象之后,可以调用如下方法来访问该URL对应的资源:

String getFile():获取此URL的资源名。
String getHost():获取此URL的主机名。
String getPath():获取此URL的路径部分。
int getPort():获取此 URL 的端口号。
String getProtocol():获取此 URL 的协议名称。
String getQuery():获取此 URL 的查询字符串部分。
URLConnection openConnection():返回一个URLConnection对象,它表示到URL所引用的远程对象的连接。
InputStream openStream():打开与此URL的连接,并返回一个用于读取该URL资源的InputStream。

URL对象中前面几个方法都非常容易理解,而该对象提供的openStream()可以读取该URL资源的InputStream,通过该方法可以非常方便地读取远程资源——甚至实现多线程下载。如下程序所示:

程序清单:codes/17/17-2/MutilDown.java

//定义下载从start到end的内容的线程
class DownThread extends Thread
{
//定义字节数组(取水的竹筒)的长度
private final int BUFF_LEN = 32;
//定义下载的起始点
private long start;
//定义下载的结束点
private long end;
//下载资源对应的输入流
private InputStream is;
//将下载到的字节输出到raf中
private RandomAccessFile raf ;
//构造器,传入输入流,输出流和下载起始点、结束点
public DownThread(long start , long end
, InputStream is , RandomAccessFile raf)
{
//输出该线程负责下载的字节位置
System.out.println(start + "---->"  + end);
this.start = start;
this.end = end;
this.is = is;
this.raf = raf;
}
public void run()
{
try
{
is.skip(start);
raf.seek(start);
//定义读取输入流内容的缓存数组(竹筒)
byte[] buff = new byte[BUFF_LEN];
//本线程负责下载资源的大小
long contentLen = end - start;
//定义最多需要读取几次就可以完成本线程的下载
long times = contentLen / BUFF_LEN + 4;
//实际读取的字节数
int hasRead = 0;
for (int i = 0; i < times ; i++)
{
hasRead = is.read(buff);
//如果读取的字节数小于0,则退出循环!
if (hasRead < 0)
{
break;
}
raf.write(buff , 0 , hasRead);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
//使用finally块来关闭当前线程的输入流、输出流
finally
{
try
{
if (is != null)
{
is.close();
}
if (raf != null)
{
raf.close();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
}
public class MutilDown
{
public static void main(String[] args)
{
final int DOWN_THREAD_NUM = 4;
final String OUT_FILE_NAME = "down.jpg";
InputStream[] isArr = new InputStream[DOWN_THREAD_NUM];
RandomAccessFile[] outArr = new RandomAccessFile[DOWN_THREAD_NUM];
try
{
//创建一个URL对象
URL url = new URL("http://images.china-pub.com/
+ "ebook35001-40000/35850/shupi.jpg");
//以此URL对象打开第一个输入流
isArr[0] = url.openStream();
long fileLen = getFileLength(url);
System.out.println("网络资源的大小" + fileLen);
//以输出文件名创建第一个RandomAccessFile输出流
outArr[0] = new RandomAccessFile(OUT_FILE_NAME , "rw");
//创建一个与下载资源相同大小的空文件
for (int i = 0 ; i < fileLen ; i++ )
{
outArr[0].write(0);
}
//每线程应该下载的字节数
long numPerThred = fileLen / DOWN_THREAD_NUM;
//整个下载资源整除后剩下的余数
long left = fileLen % DOWN_THREAD_NUM;
for (int i = 0 ; i < DOWN_THREAD_NUM; i++)
{
//为每个线程打开一个输入流、一个RandomAccessFile对象,
//让每个线程分别负责下载资源的不同部分。
if (i != 0)
{
//以URL打开多个输入流
isArr[i] = url.openStream();
//以指定输出文件创建多个RandomAccessFile对象
outArr[i] = new RandomAccessFile(OUT_FILE_NAME , "rw");
}
//分别启动多个线程来下载网络资源
if (i == DOWN_THREAD_NUM - 1 )
{
//最后一个线程下载指定numPerThred+left个字节
new DownThread(i * numPerThred , (i + 1) * numPerThred + left
, isArr[i] , outArr[i]).start();
}
else
{
//每个线程负责下载一定的numPerThred个字节
new DownThread(i * numPerThred , (i + 1) * numPerThred,
isArr[i] , outArr[i]).start();
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
//定义获取指定网络资源的长度的方法
//定义获取指定网络资源的长度的方法
public static long getFileLength(URL url) throws Exception
{
long length = 0;
//打开该URL对应的URLConnection。
URLConnection con = url.openConnection();
//获取连接URL资源的长度
long size = con.getContentLength();
length = size;
return length;
}
}

上面程序中定义了DownThread线程类,该线程从InputStream中读取从start开始,到end结束的所有字节数据,并写入RandomAccessFile对象。这个DownThread线程类的run()就是一个简单的输入、输出实现。

程序中MutilDown类中的main方法负责按如下步骤来实现多线程下载:

创建URL对象。

获取指定URL对象所指向资源的大小(由getFileLength方法实现),此处用到了URLConnection类,该类代表Java应用程序和URL之间的通信链接。下面还有关于URLConnection更详细的介绍。

在本地磁盘上创建一个与网络资源相同大小的空文件。

计算每条线程应该下载网络资源的哪个部分(从哪个字节开始,到哪个字节结束)。

依次创建、启动多条线程来下载网络资源的指定部分。

上面程序已经实现了多线程下载的核心代码,如果要实现断点下载,则还需要额外增加一个配置文件(读者可以发现所有断点下载工具都会在下载开始生成两 个文件:一个是与网络资源相同大小的空文件,一个是配置文件),该配置文件分别记录每个线程已经下载到了哪个字节,当网络断开后再次开始下载时,每个线程 根据配置文件里记录的位置向后下载即可。

URL的openConnection()方法将返回一个URLConnection对象,该对象表示应用程序和 URL 之间的通信链接。程序可以通过URLConnection实例向该URL发送请求、读取URL引用的资源。

通常创建一个和 URL的连接,并发送请求、读取此URL引用的资源需要如下几个步骤:

通过调用URL对象openConnection()方法来创建URLConnection对象。

设置URLConnection的参数和普通请求属性。

如果只是发送GET方式请求,使用connect方法建立和远程资源之间的实际连接即可;如果需要发送POST方式的请求,需要获取URLConnection实例对应的输出流来发送请求参数。

远程资源变为可用,程序可以访问远程资源的头字段或通过输入流读取远程资源的数据。

在建立和远程资源的实际连接之前,程序可以通过如下方法来设置请求头字段:

setAllowUserInteraction:设置该URLConnection的allowUserInteraction请求头字段的值。
setDoInput:设置该URLConnection的doInput请求头字段的值。
setDoOutput:设置该URLConnection的doOutput请求头字段的值。
setIfModifiedSince:设置该URLConnection的ifModifiedSince请求头字段的值。
setUseCaches:设置该URLConnection的useCaches请求头字段的值。

除此之外,还可以使用如下方法来设置或增加通用头字段:

setRequestProperty(String key, String value):设置该URLConnection的key请求头字段的值为value。如下代码所示:

conn.setRequestProperty("accept" , "*/*")

addRequestProperty(String key, String value):为该URLConnection的key请求头字段的增加value值,该方法并不会覆盖原请求头字段的值,而是将新值追加到原请求头字段中。

当远程资源可用之后,程序可以使用以下方法用于访问头字段和内容:

Object getContent():获取该URLConnection的内容。
String getHeaderField(String name):获取指定响应头字段的值。
getInputStream():返回该URLConnection对应的输入流,用于获取URLConnection响应的内容。
getOutputStream():返回该URLConnection对应的输出流,用于向URLConnection发送请求参数。

如果既要使用输入流读取URLConnection响应的内容,也要使用输出流发送请求参数,一定要先使用输出流,再使用输入流。

getHeaderField方法用于根据响应头字段来返回对应的值。而某些头字段由于经常需要访问,所以Java提供以下方法来访问特定响应头字段的值:

getContentEncoding:获取content-encoding响应头字段的值。
getContentLength:获取content-length响应头字段的值。
getContentType:获取content-type响应头字段的值。
getDate():获取date响应头字段的值。
getExpiration():获取expires响应头字段的值。
getLastModified():获取last-modified响应头字段的值。

下面程序示范了如何向Web站点发送GET请求、POST请求,并从Web站点取得响应的示例。

程序清单:codes/17/17-2/TestGetPost.java

public class TestGetPost
{
/**
* 向指定URL发送GET方法的请求
* @param url 发送请求的URL
* @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。
* @return URL所代表远程资源的响应
*/
public static String sendGet(String url , String param)
{
String result = "";
BufferedReader in = null;
try
{
String urlName = url + "?" + param;
URL realUrl = new URL(urlName);
//打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
//设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
//建立实际的连接
conn.connect();
//获取所有响应头字段
Map<String, List<String>> map = conn.getHeaderFields();
//遍历所有的响应头字段
for (String key : map.keySet())
{
System.out.println(key + "--->" + map.get(key));
}
//定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine())!= null)
{
esult += "\n" + line;
}
}
catch(Exception e)
{
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
//使用finally块来关闭输入流
finally
{
try
{
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
return result;
}
/**
* 向指定URL发送POST方法的请求
* @param url 发送请求的URL
* @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。
* @return URL所代表远程资源的响应
*/ 
public static String sendPost(String url,String param)
{
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try
{
URL realUrl = new URL(url);
//打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
//设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
//发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
//获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
//发送请求参数
out.print(param);
//flush输出流的缓冲
out.flush();
//定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine())!= null)
{
result += "\n" + line;
}
}
catch(Exception e)
{
System.out.println("发送POST请求出现异常!" + e);
e.printStackTrace();
}
//使用finally块来关闭输出流、输入流
finally
{
try
{
if (out != null)
{
out.close();
}
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
return result;
}
//提供主方法,测试发送GET请求和POST请求
public static void main(String args[])
{
//发送GET请求
String s = TestGetPost.sendGet("http://localhost:8888/abc/
login.jsp",null);
System.out.println(s);
//发送POST请求
String s1 = TestGetPost.sendPost("http://localhost:8888/abc/a.jsp",
"user=李刚&pass=abc");
System.out.println(s1);
}
}

上面程序中发送GET请求时只需将请求参数放在URL字符串之后,以?隔开,程序直接调用URLConnection对象的connect方法即 可,如程序中sendGet方法中粗体字代码所示;如果程序需要发送POST请求,则需要先设置doIn和doOut两个请求头字段的值,再使用 URLConnection对应的输出流来发送请求参数即可,如程序中sendPost()方法中粗体字代码所示。

不管是发送GET请求,还是发送POST请求,程序获取URLConnection响应的方式完全一样:如果程序可以确定远程响应是字符流,则可以使用字符流来读取;如果程序无法确定远程响应是字符流,则使用字节流读取即可。

上面程序中发送请求的两个URL是笔者在本机部署的Web应用,关于如何创建Web应用,编写JSP页面请参考笔者所著的《轻量级J2EE企业应用 实战》。由于程序可以使用这种方式直接向服务器发送请求——相当于提交Web应用中的登录表单页,这样就可以让程序不断地变换用户名、密码来提交登录请 求,直到返回登录成功,这就是所谓的暴力破解。


源:http://hi.baidu.com/lfcaolibin/blog/item/25bf243205ce1248ad4b5fe5.html
posted @ 2011-02-25 19:18 Edison.Feng 阅读(400) 评论(0) 编辑
 

2010年11月23日

【转】httpModules 与 httpHandlers

HttpModule&Handler 2010-07-28 16:31:25 阅读68 评论0   字号:大中小 订阅

ASP.NET对请求处理的过程:
当请求一个*.aspx文件的时候,这个请求会被inetinfo.exe进程截获,它判断文件的后缀(aspx)之后,将这个请求转交给ASPNET_ISAPI.dll,ASPNET_ISAPI.dll会通过http管道(Http PipeLine)将请求发送给ASPNET_WP.exe进程,在ASPNET_WP.exe进程中通过HttpRuntime来处理这个请求,处理完毕将结果返回客户端。
    inetinfo.exe进程:是www服务的进程,IIS服务和ASPNET_ISAPI.DLL都寄存在此进程中。
    ASPNET_ISAPI.DLL:是处理.aspx文件的win32组件。其实IIS服务器是只能识别.html文件的,当IIS服务器发现被请求的文件是.aspx文件时,IIS服务器将其交给aspnet_isapi.dll来处理。
    aspnet_wp.exe进程:ASP.NET框架进程,提供.net运行的托管环境,.net的CLR(公共语言运行时)就是寄存在此进程中。

ASP.NET Framework处理一个Http Request的流程:
    HttpRequest-->inetinfo.exe-->ASPNET_ISAPI.dll-->ASPNET_WP.exe-->HttpRuntime-->HttpApplication Factory-->HttpApplication-->HttpModule-->HttpHandler Factory-->HttpHandler-->HttpHandler.ProcessRequest()

ASP.NET请求处理过程是基于管道模型的,这个管道模型是由多个HttpModule和HttpHandler组成,ASP.NET把http请求依次传递给管道中各个HttpModule,最终被HttpHandler处理,处理完成后,再次经过管道中的HTTP模块,把结果返回给客户端。我们可以在每个HttpModule中都可以干预请求的处理过程。

 

 

注意:在http请求的处理过程中,只能调用一个HttpHandler,但可以调用多个HttpModule。
当请求到达HttpModule的时候,系统还没有对这个请求真正处理,但是我们可以在这个请求传递到处理中心(HttpHandler)之前附加一些其它信息,或者截获的这个请求并作一些额外的工作,也或者终止请求等。在HttpHandler处理完请求之后,我们可以再在相应的HttpModule中把请求处理的结果进行再次加工返回客户端。

HttpModule
    HTTP模块是实现了System.Web.IhttpModule接口的类。
    IHttpModule接口的声明:
        public interface IHttpModule
        {
            void Init (HttpApplication context);
            void Dispose ();
        }
        Init 方法:系统初始化的时候自动调用,这个方法允许HTTP模块向HttpApplication 对象中的事件注册自己的事件处理程序。
        Dispose方法: 这个方法给予HTTP模块在对象被垃圾收集之前执行清理的机会。此方法一般无需编写代码。
   
    HTTP模块可以向System.Web.HttpApplication对象注册下面一系列事件:
        AcquireRequestState 当ASP.NET运行时准备好接收当前HTTP请求的对话状态的时候引发这个事件。
        AuthenticateRequest 当ASP.NET 运行时准备验证用户身份的时候引发这个事件。
        AuthorizeRequest 当ASP.NET运行时准备授权用户访问资源的时候引发这个事件。
        BeginRequest 当ASP.NET运行时接收到新的HTTP请求的时候引发这个事件。
        Disposed 当ASP.NET完成HTTP请求的处理过程时引发这个事件。
        EndRequest 把响应内容发送到客户端之前引发这个事件。
        Error 在处理HTTP请求的过程中出现未处理异常的时候引发这个事件。
        PostRequestHandlerExecute 在HTTP处理程序结束执行的时候引发这个事件。
        PreRequestHandlerExecute 在ASP.NET开始执行HTTP请求的处理程序之前引发这个事件。在这个事件之后,ASP.NET 把该请求转发给适当的HTTP处理程序。
        PreSendRequestContent 在ASP.NET把响应内容发送到客户端之前引发这个事件。这个事件允许我们在内容到达客户端之前改变响应内容。我们可以使用这个事件给页面输出添加用于所有页面的内容。例如通用菜单、头信息或脚信息。
        PreSendRequestHeaders 在ASP.NET把HTTP响应头信息发送给客户端之前引发这个事件。在头信息到达客户端之前,这个事件允许我们改变它的内容。我们可以使用这个事件在头信息中添加cookie和自定义数据。
        ReleaseRequestState 当ASP.NET结束所搜有的请求处理程序执行的时候引发这个事件。
        ResolveRequestCache 我们引发这个事件来决定是否可以使用从输出缓冲返回的内容来结束请求。这依赖于Web应用程序的输出缓冲时怎样设置的。
        UpdateRequestCache 当ASP.NET完成了当前的HTTP请求的处理,并且输出内容已经准备好添加给输出缓冲的时候,引发这个事件。这依赖于Web应用程序的输出缓冲是如何设置的。

    上面这么多的事件,我们看起来可能会有些眼晕,但没关系,下面一步一步地看。
    HttpModule生命周期示意图

 

下面是事件的触发顺序:

 

BeginRequest和PreRequestHandlerExecute之间的事件是在服务器执行HttpHandler处理之前触发。
    PostRequestHandlerExecute和PreSendRequestContent之间的事件是在服务器执行Handler处理之后触发。

 

 

 

 下面我们看一下如何使用HttpModule来实现我们日常的应用:
        HttpModule通过在某些事件中注册,把自己插入ASP.NET请求处理管道。当这些事件发生的时候,ASP.NET调用对相应的HTTP模块,这样该模块就能处理请求了。
       1、向每个页面动态添加一些备注或说明性的文字:
            有的网站每一个页面都会弹出一个广告或在每个页面都以注释形式(<!-- -->)加入网站的版权信息。如果在每个页面教编写这样的JS代码的话,对于大一点的网站,这种JS代码的编写与维护可是一个很繁琐枯燥的工作。
            有了HttpModule我们就可以很简单地解决这个问题了。HttpModule是客户端发出请求到客户端接收到服务器响应之间的一段必经之路。我们完全可以在服务器处理完请求之后,并在向客户端发送响应文本之前这段时机,把这段注释文字添加到页面文本之后。这样,每一个页面请求都会被附加上这段注释文字。
            这段代码究竟该在哪个事件里实现呢? PostRequestHandlerExecute和PreSendRequestContent之间的任何一个事件都可以,但我比较喜欢在EndRequest事件里编写代码。
            第一步:创建一个类库ClassLibrary831。
            第二步:编写一个类实现IHttpModule接口
                class TestModule:IHttpModule
                {
                    public void Dispose()
                    {
                    }
                    public void Init(HttpApplication context)
                    {
                    }
                }
            第三步:在Init事件中注册EndRequest事件,并实现事件处理方法
               class TestModule:IHttpModule
                {
                    public void Dispose(){}
                    public void Init(HttpApplication context)
                    {
                        context.EndRequest += new EventHandler(context_EndRequest);
                    }
                    void context_EndRequest(object sender, EventArgs e)
                    {
                        HttpApplication ha = (HttpApplication)sender;
                        ha.Response.Write("<!--这是每个页面都会动态生成的文字。--grayworm-->");
                    }
                }
            第四步:在Web.Conofig中注册一下这个HttpModule模块
          
<httpModules>
           <add name="TestModule" type="ClassLibrary831.TestModule,ClassLibrary831"></add>
          </httpModules>
          name:模块名称,一般是类名
          type:有两部分组成,前半部分是命名空间和类名组成的全名,后半部分是程序集名称,如果类是直接放在App_Code文件夹中,那程序名称是App_Code。
                这样在Web站点是添加该类库的引用后,运行每个页面,会发现其源文件中都会加入“<!--这是每个页面都会动态生成的文字。--grayworm-->”这句话。同样的方法你也可以在其中加入JS代码。
       2、身份检查
            大家在作登录时,登录成功后,一般要把用户名放在Session中保存,在其它每一个页面的Page_Load事件中都检查Session中是否存在用户名,如果不存在就说明用户未登录,就不让其访问其中的内容。
            在比较大的程序中,这种做法实在是太笨拙,因为你几乎要在每一个页面中都加入检测Session的代码,导致难以开发和维护。下面我们看看如何使用HttpModule来减少我们的工作量
            由于在这里我们要用到Session中的内容,我们只能在AcquireRequestState和PreRequestHandlerExecute事件中编写代码,因为在HttpModule中只有这两事件中可以访问Session。这里我们选择PreRequestHandlerExecute事件编写代码。
            第一步:创建一个类库ClassLibrary831。
            第二步:编写一个类实现IHttpModule接口
                class TestModule:IHttpModule
                {
                    public void Dispose()
                    {
                    }
                    public void Init(HttpApplication context)
                    {
                    }
                }
            第三步:在Init事件中注册PreRequestHandlerExecute事件,并实现事件处理方法
               class AuthenticModule:IHttpModule
                {
                    public void Dispose(){}
                    public void Init(HttpApplication context)
                    {
                        context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
                    }
                    void context_PreRequestHandlerExecute(object sender, EventArgs e)
                    {
                        HttpApplication ha = (HttpApplication)sender;
                        string path = ha.Context.Request.Url.ToString();
                        int n = path.ToLower().IndexOf("Login.aspx");
                        if (n == -1) //是否是登录页面,不是登录页面的话则进入{}
                        {
                            if (ha.Context.Session["user"] == null) //是否Session中有用户名,若是空的话,转向登录页。
                            {
                                ha.Context.Response.Redirect("Login.aspx?source=" + path);
                            }
                        }
                    }
                }
            第四步:在Login.aspx页面的“登录”按钮中加入下面代码
                protected void Button1_Click(object sender, EventArgs e)
                {
                    if(true)    //判断用户名密码是否正确
                    {
                        if (Request.QueryString["source"] != null)
                        {
                            string s = Request.QueryString["source"].ToLower().ToString();   //取出从哪个页面转来的
                            Session["user"] = txtUID.Text;
                            Response.Redirect(s); //转到用户想去的页面
                        }
                        else
                        {
                            Response.Redirect("main.aspx");    //默认转向main.aspx
                        }
                    }
                }
            第五步:在Web.Conofig中注册一下这个HttpModule模块
          
<httpModules>
           <add name="TestModule" type="ClassLibrary831.TestModule,ClassLibrary831"></add>
          </httpModules>
       3、多模块的操作
            如果定义了多个HttpModule,在web.config文件中引入自定义HttpModule的顺序就决定了多个自定义HttpModule在处理一个HTTP请求的接管顺序。

HttpHandler
    HttpHandler是HTTP请求的处理中心,真正地对客户端请求的服务器页面做出编译和执行,并将处理过后的信息附加在HTTP请求信息流中再次返回到HttpModule中。
    HttpHandler与HttpModule不同,一旦定义了自己的HttpHandler类,那么它对系统的HttpHandler的关系将是“覆盖”关系。
    IHttpHandler接口声明
    public interface IHttpHandler
    {
        bool IsReusable { get; }
        public void ProcessRequest(HttpContext context); //请求处理函数
    }
   
    示例:把硬盘上的图片以流的方式写在页面上
        class TestHandler : IHttpHandler
        {
            public void ProcessRequest(HttpContext context)
            {
                FileStream fs = new FileStream(context.Server.MapPath("worm.jpg"), FileMode.Open);
                byte[] b = new byte[fs.Length];
                fs.Read(b, 0, (int)fs.Length);
                fs.Close();
                context.Response.OutputStream.Write(b, 0, b.Length);
            }
            public bool IsReusable
            {
                get
                {
                    return true;
                }
            }
        }
        Web.Config配置文件
      <httpHandlers>
       <add verb="*" path="*" type="ClassLibrary831.TestHandler,ClassLibrary831"></add>
      </httpHandlers>
           Verb属性:指定了处理程序支持的HTTP动作。*-支持所有的HTTP动作;“GET”-支持Get操作;“POST”-支持Post操作;“GET, POST”-支持两种操作。
  Path属性:指定了需要调用处理程序的路径和文件名(可以包含通配符)。“*”、“*.aspx”、“showImage.aspx”、“test1.aspx,test2.aspx”
  Type属性:用名字空间、类名称和程序集名称的组合形式指定处理程序或处理程序工厂的实际类型。ASP.NET运行时首先搜索bin目录中的DLL,接着在GAC中搜索。
        这样程序运行的效果是该网站的任何一个页面都会显示worm.jpg图片。如何只让一个页面(default21.aspx)执行HttpHandler中的ProcessRequest方法呢?最简单的办法是在Web.Config文件中把path配置信息设为default21.aspx。
        根据这个例子大家可以考虑一下如何编写“验证码”了。

IHttpHandler工厂
    IHttpHandlerFactory的作用是对IHttpHandler进行管理。工厂的作用请见http://hi.baidu.com/grayworm/blog/item/4a832160f8c9de46eaf8f8c1.html"
    IHttpHandlerFactory接口的声明:
        public interface IHttpHandlerFactory
        {
            IHttpHandler GetHandler (HttpContext context,string requestType,string url,string pathTranslated);
            void ReleaseHandler (IHttpHandler handler);
        }
       GetHandler返回实现IHttpHandler接口的类的实例,ReleaseHandler使工厂可以重用现有的处理程序实例。
    示例:两个用IHttpHandlerFactory来实现对不同HttpHandler的调用。
    有两个HttpHandler:将图片显示在页面上的HttpHandler和生成验证码的Handler
        //将图片显示在页面上的Handler
        class TestHandler : IHttpHandler
        {
            public void ProcessRequest(HttpContext context)
            {
                FileStream fs = new FileStream(context.Server.MapPath("worm.jpg"), FileMode.Open);
                byte[] b = new byte[fs.Length];
                fs.Read(b, 0, (int)fs.Length);
                fs.Close();
                context.Response.OutputStream.Write(b, 0, b.Length);
            }
            public bool IsReusable
            {
                get
                {
                    return true;
                }
            }
        }
        //生成验证码的Handler
        class CodeHandler:IHttpHandler
        {
            public bool IsReusable
            {
                get
                {
                    return true;
                }
            }
            public void ProcessRequest(HttpContext context)
            {
                Image b = new Bitmap(50,20);
                Graphics g = Graphics.FromImage(b);
                SolidBrush sb = new SolidBrush(Color.White);
                Font f = new Font("宋体", 12);
                string str = "";
                Random r = new Random();
                for (int i = 0; i < 4; i++)
                {
                    str += r.Next(10);
                }
                g.DrawString(str,f,sb,0,0);
                b.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
            }
        }
         IHttpHandler工厂
         class TestHandlerFactory : IHttpHandlerFactory
         {
            public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
            {
               
                string fname = url.Substring(url.IndexOf('/') + 1);
                while (fname.IndexOf('/') != -1)
                    fname = fname.Substring(fname.IndexOf('/') + 1);
                string cname = fname.Substring(0, fname.IndexOf('.'));
                string className ="";

                className = "ClassLibrary831.CodeHandler";
                object h = null;
                try
                {
                    //h = new TestHandler();
                    h = Activator.CreateInstance(Type.GetType(className));
                }
                catch (Exception e)
                {
                    throw new HttpException("工厂不能为类型" + cname + "创建实例。", e);
                }
                return (IHttpHandler)h;
            }
            public void ReleaseHandler(IHttpHandler handler)
            {
            }
         }(车延禄)
        配置文件
    <httpHandlers>
    <add verb="*" path="default21.aspx,default22.aspx" type="ClassLibrary831.TestHandlerFactory,ClassLibrary831"></add>
   </httpHandlers>

   这样TestHandlerFactory就会根据请求的不同页面执行不同的HttpHandler处理程序了。

HttpHandler使用会话
    如果要在处理程序中使用Session,那必须把该HttpHandler实现IRequiresSessionState接口,,IRequiresSessionState接口是个空接口,它没有抽象方法,只是一个标记。此处就不作例子验证了

 

 

 

ASP.Net处理Http Request时,使用Pipeline(管道)方式,由各个HttpModule对请求进行处理,然后到达 HttpHandler,HttpHandler处理完之后,仍经过Pipeline中各个HttpModule的处理,最后将HTML发送到客户端浏览 器中。

生命周期中涉及到几个非常重要的对象:HttpHandler,HttpModule,IHttpHandlerFactory,他们的执行(顺序)大致的执行过程是这样的:client端发送页面请求,被IIS的某个进程截获,它根据申请的页 面后缀(.aspx)不同,调用不同的页面处理程序(.asp->asp.dll; .aspx->ISAPI.dll).而页面处理程序在处理过程中,则要经历HttpModule,HttpHandler的处理:前者HttpModule用于页面处理前和处理后的一些事件的处理,后者HttpHandler进行真正的页面的处理。
如前所说,HttpModule会在页面处理前和后对页面进行处理,所以它不会影响真正的页面请求。通常用在给每个页面的头部或者尾部添加一些信息(如版 权声明)等.曾经见过一些免费的空间,我们的页面上传上去后,浏览的时候发现,在每个页面的头部和尾部多了很多小广告....,如果理解了 HttpModule的原理,要做这个就不是很难了~


IHttpModule与IHttpHandler的区别整理
    1.先后次序.先IHttpModule,后IHttpHandler. 注:Module要看你响应了哪个事件,一些事件是在Handler之前运行的,一些是在Handler之后运行的
    2.对请求的处理上:
        IHttpModule是属于大小通吃类型,无论客户端请求的是什么文件,都会调用到它;例如aspx,rar,html的请求.
        IHttpHandler则属于挑食类型,只有ASP.net注册过的文件类型(例如aspx,asmx等等)才会轮到调用它.
   3.IHttpHandler按照你的请求 生成响应的内容,IHttpModule对请求进行预处理,如验证、修改、过滤等等,同时也可以对响应进行处理

 

 

ASP.Net系统本身配置有很多HttpHandler和HttpModule,以处理aspx等.Net标准的页面文件,以及这些页面文件中标 准的事件处理等。查看%System%/Microsoft.NET\Framework\v2.0.50727\CONFIG目录下的 web.config文件中的httpHandlers和httpModules节点,可以看到这些配置。如果有兴趣,可以使用Reflector查 看.Net系统中相关的类和方法,了解.Net如何处理以及做了什么处理。

.Net也提供了一套机制来开发自定义的HttpHandler和 HttpModule,均可以用于对HttpRequest的截取,完成自定义的处理。 HttpModule 继承System.Web.IHttpModule接口,实现自己的HttpModule类。必须要实现接口的两个方法:Init和Dispose。在 Init中,可以添加需要截取的事件;Dispose用于资源的释放,如果在Init中创建了自己的资源对象,请在Dispose中进行释放。

namespace MyModule
{
public class MyHttpModule : IHttpModule
{
    public MyHttpModule()
    {
    }

   
   //Init方法用来注册HttpApplication 事件。
   public void Init(HttpApplication r_objApplication)
    {
      r_objApplication.BeginRequest += new EventHandler(this.BeginRequest);
    }    

 

public void Dispose()
    {
    }    

private void BeginRequest(object r_objSender, EventArgs r_objEventArgs)
    {
      HttpApplication objApp = (HttpApplication)r_objSender;
      objApp.Response.Write("您请求的URL为" + objApp.Request.Path);
    }
}


}   

将编译的dll文件拷贝到web项目的bin目录下,在web项目的web.config文件system.web节点中配置:
    这样就将自定义的HttpModule类MyHttpModule插入到了当前web的HttpModule的Pipeline中。 HttpModule主要功能是对Application的各个事件进行截取,在这些事件中完成自己的处理。其实如果自己开发一些项目,直接在 Global.asax中处理已经足够了。如果是开发一个Framework或者是某些方面的组件,需要在事件中添加处理,开发自定义的 HttpModule,可以避免使用Framework或者组件时,还得手工在Global.asax中添加代码。     目前想到的开发自定义HttpModule的用途,有全局的身份/权限验证、自定义网站访问/操作日志的记录、处于管理/调试等目的对站点进行监控追踪 等。当然,如果是结合自定义的HttpHandler进行Framework的开发,HttpModule可以用于其它的一些特殊的处理。

      <httpModules>
         <add name="test" type="MyHttpModuleTest.MyHttpModule,MyHttpModule"/>
       </httpModules>
   注意要区分大小写,因为web.config作为一个XML文件是大小写敏感的。“type=MyHttpModuleTest.MyHttpModule,MyHttpModule”告诉我们
   系统将会将http request请求交给位于MyHttpModule.dll文件中的MyHttpModuleTest.MyHttpModule类去处理。

 

 

 

HttpHandler是完全的对Http Request的截取。
    首先,继承System.Web.IHttpHandler接口,实现自己的HttpHandler类。必须要实现接口的ProcessRequest方 法和IsReusable属性。ProcessRequest方法中完成对每个Http Request的处理,发送处理结果的HTML到输出缓存中。IsReusable属性被.Net Framework调用,用以确定这个HttpHandler的实例是否可以被重用于同类型其它的Request处理。
    如果你在自己的HttpHandler类中,需要读取或者是写Session值,需要再继承一个接口IRequiresSessionState。这个接 口没有任何方法,只是一个标记接口。继承这个接口之后,就可以在自己的HttpHandler中访问Session,可以在Session中写入值。
namespace MyHandler
{
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
    public MyHttpHandler() {}
    public bool IsReusable
    {
      get { return true; }
    }
    public void ProcessRequest(HttpContext context)
    {
      HttpResponse objResponse = context.Response ;
      objResponse.Write("
This request is handled by MyHttpHandler
");
    }
}
}
    把编译的dll文件拷贝到web项目的bin目录下。
    接下来,这样来测试一下MyHttpHandler。我们为IIS配置一个以.cc为后缀名的文件类型,用我们写的MyHttpHandler来处理。
    首先,在IIS站点的Configuration配置里面,添加一个对.cc后缀名处理的Application Extention Mapping项。  
    然后,在web项目的web.config节点节点中配置:

MyHttpHandler, MyHandler"/>

    verb属性配置这个HttpHandler处理那些HTTP方法,例如GET、POST等,如果是处理所有方法,就用*。path属性配置HttpHandler对哪些文件进行处理,例如可以是myfile.cc,如果是处理所有的.cc文件,就用*.cc。
    这样,这个站点上所有.cc类型文件的访问,都由MyHttpHandler处理。使用http://localhost/站点虚拟目录/a.cc访问测试站点,可以看到测试效果。当然,a.cc这个文件在Web服务器上是并不存在的。

    对HttpHandler的使用,比较典型的有.Net的Web MVC开源项目Maverick。Maverick使用一个Dispatcher类对所有的Http Request进行截取,他以.m作为后缀名向Web服务器提交请求,在Dispatcher中,将.m的后缀去掉,提取Command Name,然后以这个command name从配置文件中加载处理的flow,形成一个chain,依次对chain上的各个command和view进行处理,对各个command和 view的处理结果可能会在chain中选择不同的处理分支,每个处理的Step中将处理结果的HTML写入Response的缓存中进行输出。
    总体来说,Maverick的框架架构概念很不错,但也存在明显的缺陷,以后有时间再详细的写写它的架构和需要改进之处。

    总之,将HttpModule、HttpHandler,以及使用Ajax等将客户端进行封装结合起来,能够给web项目的开发带来非常大的改善空间。

Asp.Net HttpHandler实现URL重写的
我们经常看到很多网站访问文章的时候才用的是***.html 或***.shtml (如本blog的日志访问效果),其时这写文件在服务器上不存在的,那为什么会出现这样的效果呢,是因为Web服务器上对URL执行了重写,把访问的 URL根据特定的格式重写成内部访问页面来实现的,它的好处是便于用户理解,同时搜索引擎也能更好地收入你的网站,当然其它的好处也很多,这里不做一一介 绍了。
本文所讲的是使用Asp.Net中的HttpHandler实现URL重写的,它所实现的原理请看这里,本程序可以处理任何Url,因为我在程序中使用了URL过虑,只有访问文件名是数字的才进行处理,并指在内部执行一个新的页面,并输出数据,代码如下:
public void ProcessRequest(HttpContext Context)
{   
try {       
         //申明Request        
HttpRequest Request = Context.Request;
//取来路Url的绝对路径       
string Url = Request.Url.AbsolutePath;
//取访问的Web文件的开始字符间隔数
int RegStart = Url.LastIndexOf("/") + 1;
//申明一个确定Web文件名是否全是数字
Regex Reg = new Regex(@"\d+");
//用正则表达式进行匹配
if (Reg.IsMatch(Url, RegStart))
{
// 如果web文件名是数字,则判定是查询相关文章,执行指定页面             Context.Server.Execute("~/PermaLink.aspx?id=" + Reg.Match(Url, RegStart).Value);       
}   
}
catch
{
      Context.Response.Redirect(Context.Request.Url.ToString());
}
}
当然你首先要做的是先建一个类,并继承自IHttpHandler,然后把这段代码拷入,并编译。在Web项目中若要使用此功能,需要在web.config里面加上如下语句:
<httpHandlers>
    <add verb="*" path="*.shtml" type="HttpHandle.UrlRewrite" />
</httpHandlers>
同时,还要在IIS中对Web项目进行配置,在Web项目的属性中,在主目录选项卡里,把执行权限改为"脚本和可执行文件",然后打开配置,在应用程序扩展里加上需重写的文件格式的扩展,好了,成事具备,只欠运行了。

posted @ 2010-11-23 00:49 Edison.Feng 阅读(159) 评论(1) 编辑
 
【转】挣脱浏览器的束缚(2) - 别让脚本引入坏了事

挣脱浏览器的束缚(2) - 别让脚本引入坏了事

2007-01-20 01:25 by 老赵, 4546 visits

现在哪里还找得到不引入JavaScript脚本文件的Web应用?使用脚本文件的好处多多,其中最重要的可能就是提供缓存能力了。使用脚本文件之后再加上缓存,可以大大降低数据传输量,提高页面打开的速度。不过脚本文件的引入也不是简单得不值一提,我们完全有能力来优化它。

 

小心传统的脚本引入方式带来的性能问题

现在的Web应用所需的脚本越来越多,一张页面下载几百K的脚本也不再是难以想象的事情了,这就直接导致页面需要更长的时间来加载脚本。不过传统的脚本引入方式(使用<script />)会造成什么问题?再查看这点之前,我们先写一个HttpHandler来模拟一个需要较长时间才能加载的脚本。这很简单,我们只要创建一个Http Handler来做到这一点,如下:

public class Scripts : IHttpHandler {

    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "application/x-javascript";

        System.Threading.Thread.Sleep(1500);

        context.Response.Write("//");
    }

    public bool IsReusable {
        get {
            return false;
        }
    }
}

 

我使用Thread.Sleep函数使线程休眠1.5秒,然后输出一个注释符。这样就保证了页面加载该文件需要比较长的时间,也可以将脚本的执行时间降到最低。

然后我们就写个最简单的页面,来测试一下加载这些文件的结果:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server" id="aaa">
    <title>Untitled Page</title>
    <script type="text/javascript" language="javascript" src="Scripts.ashx?a"></script>
    <script type="text/javascript" language="javascript" src="Scripts.ashx?b"></script>
    <script type="text/javascript" language="javascript" src="Scripts.ashx?c"></script>
    <script type="text/javascript" language="javascript" src="Scripts.ashx?d"></script>
    <script type="text/javascript" language="javascript" src="Scripts.ashx?e"></script>
</head>
<body>
    ...
</body>
</html>

 

在IE里打开页面,看看这些脚本加载的情况。请注意,您可以使用IE Dev Toolbar来禁用Cache(图5)。


图5:IE中传统方式加载脚本的情况

真可谓是相当的整齐。不过整齐的背后是较低的性能:脚本文件一个一个被加载,所有脚本文件被加载完需要用8秒多时间。

那么FireFox的表现又如何?我们使用同样的页面来测试一下(图6)。


图6:FireFox中传统方式加载脚本的情况

嘿,情况差不多。

其实出现这个状况是By Design的。从上面这个简单的例子里可能还无法看出,事实上,当浏览器遇到<script />标签时,它会开始加载脚本文件,而此时页面的其它加载行为则会全部停止,包括HTML的呈现,页面或图片的下载等等。这是因为浏览器“怀疑”这些脚本文件中的一些行为可能会再页面中输出HTML。自然,我们可以使用document.write方法这么做。而很多可以放在网站中的第三方小部件,都是靠脚本文件里的document.write方法来生成HTML的。

这就让用户不太好受了。为什么我的浏览器只能建立一个连接?为什么不能一起下载?我们的带宽不是浪费了很多吗?这些都没错。还记得前一段时间台湾地震使一些Blog无法打开或者打开很慢吗?这很可能就是在页面中使用<script />引入脚本文件时造成的问题:文件下载特别慢,甚至会超时。而且当时我的blog也遇到这个问题。解决方案很简单,把<script />去掉便是。或者,您可以将<script />元素放置在“页尾”代码中,这样,页面就会打开地比较快了——不过当然,那个文件很可能还在继续加载脚本中。

这就是提高了所谓的“感知性能(Perceived Performance)”,简单的说,就是用户“感受”到的性能。用户会发现页面已经打开了,虽然还没有完全加载完,例如Snap Preview还无法工作。

 

尝试打破传统脚本引入的瓶颈

现在的脚本越做越大了,一个200K的文件,如果以20K每秒的速度下载也要10秒。如果这十秒结束之后又来个十秒……这样的网页加载速度太可怕了。我们必须尝试着打破这个瓶颈。

很有趣的是,如果您在页面中使用document.write来写一个<script />元素的话,这些脚本就可以并行下载了。我们就用下面的代码进行尝试吧:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server" id="aaa">
    <title>Untitled Page</title>
    <script type="text/javascript" language="javascript">
        document.write(
            '<script type="text/javascript" language="javascript"' + 
            ' src="Scripts.ashx?a"><' + '/script>');
        document.write(
            '<script type="text/javascript" language="javascript"' + 
            ' src="Scripts.ashx?b"><' + '/script>');
        document.write(
            '<script type="text/javascript" language="javascript"' + 
            ' src="Scripts.ashx?c"><' + '/script>');
        document.write(
            '<script type="text/javascript" language="javascript"' + 
            ' src="Scripts.ashx?d"><' + '/script>');
        document.write(
            '<script type="text/javascript" language="javascript"' + 
            ' src="Scripts.ashx?e"><' + '/script>');
    </script>
</head>
<body>
    ...
</body>
</html>

 

这样的做法似乎有些复杂,不过应该还算直观。上面代码的目的就是在页面中“写入”<script />元素,以达到引入脚本文件的目的。还是用事实说话,先来看一下IE中打开页面的效果吧(图6):


图7:IE中使用document.write加载脚本的情况

状况好多了。可以看出总是有两个脚本文件在同时下载,虽然还是受制于浏览器对于每个Domain只有2个连接的限制,但是页面加载时间已经从8秒多锐减到不到5秒了。这实在是一个绝好的消息。那么再公布一个好消息,使用这种方式引入脚本文件的话,脚本文件的执行顺序与脚本文件出现的顺序相同。我们只要安排好脚本文件的顺序,这样就可以保证脚本执行的正确性了。

嘿嘿,不管怎么说这个方法还是非常容易使用的,不是吗?那么让我们欢呼雀跃吧,因为优化就是这么简单!

很可惜事情的发展并不如我们想象的那么单纯。我们还没有试过FireFox下的状况呢。看了FireFox加载页面的数据统计图,可能就会知道,我们离目标还有很大的距离——因为它的状况和图6的显示状况完全相同,document.write这种做法在FireFox里没有起到任何作用。

为什么IE的表现和FireFox的表现不同呢?可能这就要问一下浏览器的开发者了,我们现在要做的,可能只是根据结果来为我们的应用想出更好的解决方案。

路漫漫其修远兮。

 

原文:http://blog.zhaojie.me/2007/01/break-the-browsers-restrictions-2.html

posted @ 2010-11-23 00:05 Edison.Feng 阅读(94) 评论(0) 编辑
 

2010年9月10日

[转]asp.net中利用ashx实现图片防盗链
盗链原理:看下面用httpwatch截获的http发送的数据
GET /Img.ashx?img=svn_work.gif HTTP/1.1
Accept: */*
Referer: http://www.svnhost.cn/
Accept-Language: zh-cn
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; CIBA)
Host: http://www.svnhost.cn/
Connection: Keep-Alive

该数据包表示请求http://www.svnhost.cn/Img.ashx?img=svn_work.gif文件。我们可以看到Referer表示上一页请求页面地址,也就是文件来源。Host表示当前请求的主机地址。
下面是一个盗链的数据包

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->GET /Img.ashx?img=svn_work.gif HTTP/1.1
Accept: */*
Referer: http://745.cc/
Accept-Language: zh-cn
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; CIBA)
Host: http://www.svnhost.cn/
Connection: Keep-Alive

我们可以看到,上面两个数据,表示对于同一个文件:http://www.svnhost.cn/Img.ashx?img=svn_work.gif的请求过程,这里的不同就是Referer,也就是都是请求同一个文件,但是请求的来源是不同的。因此我们可以在程序里判断是否是来源于当前服务器,来判断是否是盗链。明白原理以后,实现防盗链就非常简单了。下面以图片防盗链来实现一个演示。ASP.NET中添加一个img.ashx文件,然后后台代码如下:

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->using System;

using System.Collections;

using System.Data;

using System.Web;

using System.Web.Services;

using System.Web.Services.Protocols;

namespace GetImage

{

    ///

    /// $codebehindclassname$ 的摘要说明

    ///

    [WebService(Namespace = "http://tempuri.org/")]

    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

    public class Img : IHttpHandler

    {

        public void ProcessRequest(HttpContext context)

        {

            context.Response.ContentType = "image/jpg";

            if (context.Request.UrlReferrer != null && context.Request.UrlReferrer.Host.Equals(context.Request.Url.Host, StringComparison.InvariantCultureIgnoreCase))

                context.Response.WriteFile(context.Server.MapPath("~/" + context.Request.QueryString["img"]));

            else

                context.Response.WriteFile(context.Server.MapPath("~/logo.gif"));

        }

        public bool IsReusable

        {

            get

            {

                return false;

            }

        }

    }

}




表示如果来源不为空,并且来源的服务器和当前服务器一致,那就表示是正常访问,非盗链。正常访问文件内容。
否则就是盗链,返回网站LOGO。
你甚至可以做成随机返回正确的图片,随机返回错误图片,或者定时返回正确图片,定时返回错误图片。
然后就是图片的使用了,这时使用图片就不是直接了,而是,就是说通过img,ashx来读取图片。
posted @ 2010-09-10 23:28 Edison.Feng 阅读(212) 评论(0) 编辑
 

2010年5月13日

求任何一个正数的组合,组合的规则是这个数等于1或2的整数幂之和,请列出组合的情况。
摘要: 求任何一个正数的组合,组合的规则是这个数等于1或2的整数幂之和,请列出组合的情况。比如:11 = 1,2,826 = 2,8,1631 = 1,2,4,8,1696 = 64,321.拆分法分析:把10进制数转换2进制11 = 1,2,8 -> 1011 = 1,10,100 31 = 1,2,4,8,16 -> 11111 = 1,10,100,1000,1000096 = 64,3...阅读全文
posted @ 2010-05-13 18:40 Edison.Feng 阅读(164) 评论(0) 编辑
 

2010年5月12日

C#利用反射获取对象属性的修改情况
摘要: public static string GetObjectUpdateInfo<T>(T old, T current) { string info = string.Empty; Type type = typeof(T); PropertyInfo[] propertys = type.GetProperties(); foreach (PropertyInfo property...阅读全文
posted @ 2010-05-12 18:03 Edison.Feng 阅读(139) 评论(0) 编辑
 
C#利用反射获取对象属性值
摘要: public static string GetObjectPropertyValue<T>(T t, string propertyname){Type type = typeof(T);PropertyInfo property = type.GetProperty(propertyname);if (property == null) return string.Empty;ob...阅读全文
posted @ 2010-05-12 17:58 Edison.Feng 阅读(285) 评论(0) 编辑
 
Clone:Xml序列化反序列克隆对象
摘要: public static T Clone<T>(T t){T clone;System.Xml.Linq.XDocument doc = new System.Xml.Linq.XDocument(); System.Xml.XmlWriter w = doc.CreateWriter();System.Xml.Serialization.XmlSerializer s = new...阅读全文
posted @ 2010-05-12 17:55 Edison.Feng 阅读(88) 评论(0) 编辑
 
仅列出标题  下一页