新浪微博开发时使用request拿不到数据原因分析(转载别人的,这个文章 写的很好,很透彻....)

来源:互联网 发布:jquery.cookie.js用法 编辑:程序博客网 时间:2024/05/18 03:53

转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-) 

http://blog.csdn.net/floodingfire/article/details/8142974


自动登录代码

[java] view plaincopy
  1. Weibo weibo = Weibo.getInstance();  
  2. AccessToken accessToken = new AccessToken(accessToken, SINA_API_SECRET);  
  3. accessToken.setExpiresIn("99999");  
  4. weibo.setAccessToken(accessToken);  

在使用新浪开放平台的OAuth2.0授权后自动登录时,一直在报同一个异常:

[plain] view plaincopy
  1. com.weibo.net.WeiboException: HTTP/1.1 403 Forbidden  
这个问题困扰我近月,上网搜寻答案也不得解脱。最后还是在新浪SDK源码中发现蛛丝马迹终于解决了这个问题。

Step1:在LogCat下查看错误信息

[plain] view plaincopy
  1. 11:00:03.129: W/System.err(557): com.weibo.net.WeiboException: HTTP/1.1 403 Forbidden  (1. 未授权)  
  2. 11:00:03.149: W/System.err(557):    at com.weibo.net.Utility.openUrl(Utility.java:335) (2. 错误发生在这里)  
  3. 11:00:03.149: W/System.err(557):    at com.weibo.net.Utility.openUrl(Utility.java:286)  
  4. 11:00:03.178: W/System.err(557):    at com.weibo.net.Weibo.request(Weibo.java:149)  
  5. 11:00:03.178: W/System.err(557):    at com.weibo.net.AsyncWeiboRunner$1.run(AsyncWeiboRunner.java:51)  
  6. 11:00:03.218: W/share(557): WeiboException: com.weibo.net.WeiboException: HTTP/1.1 403 Forbidden  
Step2:分析原因

首先错误消息是403状态码,有点儿常识的应该知道404代表没有访问资源、找不到资源等,而403大致是没有访问权、禁止访问等。(详细的解释Google之)

其实在新浪的文档和SDK的源码中都有关于异常的解释,比如在新浪SDK下,有这么一条注释:

com/weibo/net/WeiboException.java

[java] view plaincopy
  1. 403 Forbidden: 没有权限访问对应的资源.  

可是明明传入了accessToken,怎么会没有访问权限呢?

不要急躁,顺着LogCat的信息顺藤摸瓜:StackTrace的栈顶信息显示错误最后出现在Utility.java中的openUrl()方法中:throw new WeiboException(String.format(status.toString()), statusCode);

[java] view plaincopy
  1. public static String openUrl(Context context, String url, String method,  
  2.         WeiboParameters params, String file, Token token) throws WeiboException {  
  3.     String result = "";  
  4.     try {  
  5.         HttpClient client = getNewHttpClient(context);  
  6.         HttpUriRequest request = null;  
  7.         ByteArrayOutputStream bos = null;  
  8.         if (method.equals("GET")) {  
  9.             url = url + "?" + encodeUrl(params);  
  10.             HttpGet get = new HttpGet(url);  
  11.             request = get;  
  12.         } else if (method.equals("POST")) {  
  13.             HttpPost post = new HttpPost(url);  
  14.             byte[] data = null;  
  15.             bos = new ByteArrayOutputStream(1024 * 50);  
  16.             if (!TextUtils.isEmpty(file)) {  
  17.                 Utility.paramToUpload(bos, params);  
  18.                 post.setHeader("Content-Type", MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY);  
  19.                 Bitmap bf = BitmapFactory.decodeFile(file);  
  20.   
  21.                 Utility.imageContentToUpload(bos, bf);  
  22.   
  23.             } else {  
  24.                 post.setHeader("Content-Type""application/x-www-form-urlencoded");  
  25.                 String postParam = encodeParameters(params);  
  26.                 data = postParam.getBytes("UTF-8");  
  27.                 bos.write(data);  
  28.             }  
  29.             data = bos.toByteArray();  
  30.             bos.close();  
  31.             // UrlEncodedFormEntity entity = getPostParamters(params);  
  32.             ByteArrayEntity formEntity = new ByteArrayEntity(data);  
  33.             post.setEntity(formEntity);  
  34.             request = post;  
  35.         } else if (method.equals("DELETE")) {  
  36.             request = new HttpDelete(url);  
  37.         }  
  38.         setHeader(method, request, params, url, token);  
  39.         HttpResponse response = client.execute(request);  
  40.         StatusLine status = response.getStatusLine();  
  41.         int statusCode = status.getStatusCode();  
  42.   
  43.         if (statusCode != 200) {  
  44.             result = read(response);  
  45.             throw new WeiboException(String.format(status.toString()), statusCode);  
  46.         }  
  47.         // parse content stream from response  
  48.         result = read(response);  
  49.         return result;  
  50.     } catch (IOException e) {  
  51.         throw new WeiboException(e);  
  52.     }  
  53. }  

可以看到请求完毕以后,判断statusCode不为200(请求正常)状态码。也就是错误出现在发送请求之前,继续往上看,这个方法用的是Android中常用的一种网络通信方法:

[java] view plaincopy
  1. HttpResponse response = client.execute(request);  
那么在HttpClient执行此次请求之前,又发生了什么事情呢?

[java] view plaincopy
  1. setHeader(method, request, params, url, token);  
看方法的名称似乎像是设置HTTP的头部信息,ok,再跟进代码看看(其实有经验的人看到这个方法的参数,就已经明白是怎么回事了):
[java] view plaincopy
  1. // 设置http头,如果authParam不为空,则表示当前有token认证信息需要加入到头中  
  2. public static void setHeader(String httpMethod, HttpUriRequest request,  
  3.         WeiboParameters authParam, String url, Token token) throws WeiboException {  
  4.     if (!isBundleEmpty(mRequestHeader)) {  
  5.         for (int loc = 0; loc < mRequestHeader.size(); loc++) {  
  6.             String key = mRequestHeader.getKey(loc);  
  7.             request.setHeader(key, mRequestHeader.getValue(key));  
  8.         }  
  9.     }  
  10.     if (!isBundleEmpty(authParam) && mAuth != null) {  
  11.         String authHeader = mAuth.getWeiboAuthHeader(httpMethod, url, authParam,  
  12.                 Weibo.getAppKey(), Weibo.getAppSecret(), token);  
  13.         if (authHeader != null) {  
  14.             request.setHeader("Authorization", authHeader);  
  15.         }  
  16.     }  
  17.     request.setHeader("User-Agent", System.getProperties().getProperty("http.agent")  
  18.             + " WeiboAndroidSDK");  
  19. }  
这个方法的注释已经说明了问题,再仔细查看代码,问题就出在mAuth变量中,显然这个变量不是从刚才的方法传递过来的,那么这个方法怎么又要依赖一个莫名其妙的mAuth变量呢?

[java] view plaincopy
  1. private static HttpHeaderFactory mAuth;  
而查看整个Utility类(如何查看类中变量被使用的位置不必多说吧?),mAuth出现的次数并不多,除了在setHeader方法中有惊鸿一瞥,它的真正老家再另一个方法中:
[java] view plaincopy
  1. public static void setAuthorization(HttpHeaderFactory auth) {  
  2.     mAuth = auth;  
  3. }  
看到这里我想大家除了想骂娘,没有别的心思了。

国内的大平台如新浪、腾讯、人人一贯如此,强内聚、弱耦合似乎只是个说法而已,虽然说一个大项目要做好扩展,但是好歹也要给个提示嘛!!!

Step3:解决问题

好了,小小的抱怨一下,我们的任务还没有完成,大致思路明确,要在此方法前初始化mAuth参数
可是,参数是HttpHeaderFactory 对象啊,跟代码过去一看,又傻了:

[java] view plaincopy
  1. public abstract class HttpHeaderFactory {  
  2.     public static final String CONST_HMAC_SHA1 = "HmacSHA1";  
  3.     public static final String CONST_SIGNATURE_METHOD = "HMAC-SHA1";  
  4.     public static final String CONST_OAUTH_VERSION = "1.0";  
  5.   
  6.     public HttpHeaderFactory() {  
  7.     }  
  8. ...  
这,尼玛!竟然是抽象类?本来憋着一股气,这又要怒气冲天了。

HOLD住,冲动易怒可不是好程序员的特质!既然是用的抽象类,那么前面的错误也明了,写这个代码的人可能是为了扩展性,既然是这样,他可能写好了实现类。如果连实现类都没有,大家就尽情的愤怒,然后安静的自己实现一个吧。

方案一:自己实现

....我这么懒,是不会做这种傻事的。


方案二:查找实现类

怎么个查找法?把所有的类都看一遍,看看是不是 extends HttpHeaderFactory? 是这么想的童鞋,先去面壁5分钟。

...

ok,有经验的程序员应该知道,在Eclipse中查看继承关系是有快捷方式的,选中类以后按:Ctrl + T。

这一下又要犯愁了,一下子揪出了他一家子,可见这个程序员确实是为了扩展,但是这么多的子类,到底用哪一个好呢?

你可以一个个看看,但是我用的是OAuth2.0,直接用试试Oauth2AccessTokenHeader好了。看着注释里那个半吊子英语,我又笑了~~should not be...

[java] view plaincopy
  1. /** 
  2.  * Encapsulation a http accessToken headers. the order of weiboParameters will not be changed. 
  3.  * Otherwise the signature <strong>should not be</strong> calculated right. 
  4.  * @author  ZhangJie (zhangjie2@staff.sina.com.cn) 
  5.  */  
  6. public class Oauth2AccessTokenHeader extends HttpHeaderFactory {  
  7.   
  8.     @Override  
  9.     public String getWeiboAuthHeader(String method, String url, WeiboParameters params,  
  10.             String app_key, String app_secret, Token token) throws WeiboException {  
  11.         if(token == null){  
  12.             return null;  
  13.         }  
  14.         return "OAuth2 " + token.getToken();  
  15.           
  16.     }  
  17.     @Override  
  18.     public WeiboParameters generateSignatureList(WeiboParameters bundle) {  
  19.         return null;  
  20.     }  
  21.   
  22.     @Override  
  23.     public String generateSignature(String data, Token token) throws WeiboException{  
  24.         return "";  
  25.     }  
  26.   
  27.     @Override  
  28.     public void addAdditionalParams(WeiboParameters des, WeiboParameters src) {  
  29.         // TODO Auto-generated method stub  
  30.           
  31.     }  
  32.   
  33. }  
ok,直接使用无参构造方法初始化,作为参数,在使用相应API之前初始化mAuth参数,只有mAuth参数不为null时,Utility.setHeader才会拼凑出正确的HTTP Header来,否则header为空,token信息没有携带过去,就导致未授权错误,返回码是403了。
[java] view plaincopy
  1. Utility.setAuthorization(new Oauth2AccessTokenHeader());  


附录

正确的自动登录方法为:

[java] view plaincopy
  1. Weibo weibo = Weibo.getInstance();  
  2. Utility.setAuthorization(new Oauth2AccessTokenHeader());  
  3. AccessToken accessToken = new AccessToken(accessToken, SINA_API_SECRET);  
  4. accessToken.setExpiresIn("99999");  
  5. weibo.setAccessToken(accessToken);  
原创粉丝点击