用NodeJs通過OAuth2發新浪微博

jopen 12年前發布 | 59K 次閱讀 JavaScript開發 NodeJS

想用nodejs寫個微博客戶端發微博,無奈新浪微博的nodejs sdk是OAuth1.0的。
只能自己根據OAuth1.0 改了改。
只寫了statuses/update 和 statuses/upload,其他的實現基本都類似了。
update是簡單參數的post,upload是multipart 包含二進制圖片的post。
改改帳號參數和發送的圖片路徑,node weibotest.js就能成功發送了。
如果中文出現亂碼,請把這兩個js文件保存成utf-8編碼。


weibo.js

var querystring = require('querystring'),
crypto = require('crypto'),
https = require('https'),
URL = require('url'),
path = require('path'),
fs = require('fs');

var apiprefix = '

var baseurl = "https://api.weibo.com/2/";

var weibo = module.exports = function() { this._accesstoken = ""; this._accessTokenName = "access_token"; }; weibo.prototype = { //oauth2/authorize getAuthorize: function(callback) { var params = {}; params['client_id'] = appkey; // appkey params['redirect_uri'] = ""; // oauth2 回調地址 params['response_type'] = "code"; params['action'] = "submit"; params['userId'] = userId; // 微博帳號 params['passwd'] = passwd; // 帳號密碼 var post_data = querystring.stringify(params); var post_headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; var url = apiprefix + "oauth2/authorize"; //https request var m = "POST"; var code = ""; var tt = this; this._request(m, url, post_headers, post_data, null, function(error, body, response) { if (error) { console.log("error:" + error); } else { code = response.headers.location.substr(6); console.log("code:" + code); tt.getAccesstoken(code, function(err, access_token, refresh_token) { console.log(access_token); tt._accesstoken = access_token; callback(err, access_token, refresh_token); }); } }); }, //oauth2/access_token getAccesstoken: function(code, callback) { var params = {}; params['grant_type'] = "authorization_code"; // appkey params['redirect_uri'] = ""; // oauth2 回調地址 params['client_id'] = appkey; params['client_secret'] = appsecret; params['code'] = code;

    var post_data = querystring.stringify(params);
    var post_headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        "contentType":"text/html;charset=uft-8"
    };
    var url = apiprefix + "oauth2/access_token";
    //https request
    var m = "POST";
    this._request(m, url, post_headers, post_data, null,
    function(error, data, response) {
        if (error) callback(error);
        else {
            var results;
            try {
                // As of http://tools.ietf.org/html/draft-ietf-oauth-v2-07
                // responses should be in JSON
                results = JSON.parse(data);
            }
            catch(e) {
                // .... However both 非死book + Github currently use rev05 of the spec
                // and neither seem to specify a content-type correctly in their response headers :(
                // clients of these services will suffer a *minor* performance cost of the exception
                // being thrown
                results = querystring.parse(data);
            }
            var access_token = results["access_token"];
            var refresh_token = results["refresh_token"];
            delete results["refresh_token"];
            callback(null, access_token, refresh_token);
        }
    });
},
//通用https request,從node-oauth上oauth2摘出來的
_request: function(method, url, headers, post_body, access_token, callback) {
    var creds = crypto.createCredentials({});
    var parsedUrl = URL.parse(url, true);
    if (parsedUrl.protocol == "https:" && !parsedUrl.port) parsedUrl.port = 443;

    var realHeaders = {};
    if (headers) {
        for (var key in headers) {
            realHeaders[key] = headers[key];
        }
    }
    realHeaders['Host'] = parsedUrl.host;

    if (Buffer.isBuffer(post_body)) {
        realHeaders['Content-Length'] = post_body ? post_body.length: 0;
    }
    else {
        realHeaders['Content-Length'] = post_body ? Buffer.byteLength(post_body) : 0;
    }
    if (access_token) {
        if (!parsedUrl.query) parsedUrl.query = {};
        parsedUrl.query[this._accessTokenName] = access_token;
    }

    var result = "";
    var queryStr = querystring.stringify(parsedUrl.query);
    if (queryStr) queryStr = "?" + queryStr;
    var options = {
        host: parsedUrl.hostname,
        port: parsedUrl.port,
        path: parsedUrl.pathname + queryStr,
        method: method,
        headers: realHeaders
    };
    // Some hosts *cough* google appear to close the connection early / send no content-length header
    // allow this behaviour.
    var allowEarlyClose = false;
    var callbackCalled = false;
    function passBackControl(response, result) {
        if (!callbackCalled) {
            callbackCalled = true;
            if (response.statusCode != 200 && (response.statusCode != 301) && (response.statusCode != 302)) {
                callback({
                    statusCode: response.statusCode,
                    data: result
                });
            } else {
                callback(null, result, response);
            }
        }
    }
    console.log("options:");
    console.log(options);
    var request = https.request(options,
    function(response) {
        response.on("data",
        function(chunk) {
            result += chunk
        });
        response.on("close",
        function(err) {
            if (allowEarlyClose) {
                passBackControl(response, result);
            }
        });
        response.addListener("end",
        function() {
            passBackControl(response, result);
        });
    });
    request.on('error',
    function(e) {
        callbackCalled = true;
        callback(e);
    });
    if (method == 'POST' && post_body) {
        request.write(post_body);
    }
    request.end();
},
//普通post,執行類似statuses/update.json
post: function(url, params, callback) {
    if (!this._accesstoken) return callback("not authorize");
    var post_data = querystring.stringify(params);
    var post_headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    };
    if (params.ContentType) {
        post_headers['Content-Type'] = params.ContentType;
    }
    this._request("POST", baseurl + url + '.json', post_headers, post_data, this._accesstoken, callback);
},
/********** statuses *********/
//statuses/repost 轉發一條微博信息
//statuses/destroy 刪除微博信息
//statuses/update 發布一條微博信息
//statuses/upload 上傳圖片并發布一條微博
//statuses/upload_url_text 發布一條微博同時指定上傳的圖片或圖片url 
//emotions 獲取官方表情
repost: function(args, callback) {
    /* args參數:
     *  id : 微博id
     *  status : 轉發文本 
     *  is_comment 0-不發評論 1-發評論給當前微博 2-發評論給原微博 3-都發
     */
    if (!args.id) return callback('missing argument id');
    this.post('statuses/repost', args, callback);
},

update: function(params, callback) {
    if (!params.status) return callback('missing argument status');
    this.post('statuses/update', params, callback);
},

FILE_CONTENT_TYPES: {
    '.gif': 'image/gif',
    '.jpeg': 'image/jpeg',
    '.jpg': 'image/jpeg',
    '.png': 'image/png'

},
    //獲取文件信息,用于statuses/upload 上傳圖片并發布一條微博
fileinfo: function(file) {
    var name, content_type;
    if (typeof(file) === 'string') {
        var ext = path.extname(file);
        content_type = this.FILE_CONTENT_TYPES[ext];
        name = path.basename(file);
    } else {
        name = file.name || file.fileName;
        content_type = file.fileType || file.type;
    }
    return {
        name: name,
        content_type: content_type
    };
},

/** 
 * 上傳圖片
 * pic: filepath
 * callback: finish callback function
 **/
upload: function(params, callback) {
    if (!params.status) return callback('missing argument status');
    var pic = params.pic;
    var pic_field = 'pic';

    var boundary = 'boundary' + (new Date).getTime();
    var dashdash = '--';
    var crlf = '\r\n';

    /* Build RFC2388 string. */
    var builder = '';

    builder += dashdash;
    builder += boundary;
    builder += crlf;
            //微博文字
    builder += 'Content-Disposition: form-data; name="status"';
    builder += crlf;
    builder += crlf;
    /* Append form data. */
    builder += params.status;
    builder += crlf;

    /* Write boundary. */
    builder += dashdash;
    builder += boundary;
    builder += crlf;

    var fileinfo = this.fileinfo(pic);

    /* Generate headers. [PIC] */
    builder += 'Content-Disposition: form-data; name="' + pic_field + '"';

    builder += '; filename="' + fileinfo.name + '"';
    builder += crlf;

    builder += 'Content-Type: ' + fileinfo.content_type + ';';
    builder += crlf;
    builder += crlf;

    var tt = this;
    // 處理文件內容
    //微博圖片
    this.read_file(pic,
    function(file_buffer) {
        var endstr = crlf + dashdash + boundary + dashdash + crlf,
        buffer = null;
        if (typeof(BlobBuilder) === 'undefined') {
            var builderLength = new Buffer(builder).length;
            var size = builderLength + file_buffer.length + endstr.length;
            buffer = new Buffer(size);
            var offset = 0;
            buffer.write(builder);
            offset += builderLength;
            file_buffer.copy(buffer, offset);
            offset += file_buffer.length;
            buffer.write(endstr, offset);
        } else {
            buffer = new BlobBuilder(); //NOTE WebKitBlogBuilder
            buffer.append(builder);
            buffer.append(pic);
            buffer.append(endstr);
            buffer = buffer.getBlob();
        }
        if (!tt._accesstoken) return callback("not authorize");
        var post_data = buffer;
        //必須使用multipart/form-data
        var post_headers = {
            'Content-Type': 'multipart/form-data;boundary=' + boundary
        };
        console.log(builder);
        tt._request("POST", baseurl + 'statuses/upload' + '.json', post_headers, post_data, tt._accesstoken, callback);

    });
},

read_file: function(pic, callback) {
    if (typeof(pic) === 'string') {
        fs.stat(pic,
        function(err, stats) {
            fs.readFile(pic,
            function(err, file_buffer) {
                    console.log(err);
                if (!err) callback(file_buffer);
            });
        });
    } else {
        callback(pic);
    }
},

};</pre>weibotest.js

var weibo = require('./weibo');

var wb = new weibo(); wb.getAuthorize(function(err, access_token, refresh_token) { //wb.update({ // "status": "中文不行?" //}, //function(error, body, response) { // if (error) { // console.log("error:"); // console.log(error); // } // else { // console.log("body:" + body); // } //}); wb.upload({ "status": "中文不行?", "pic": "E:/XX.jpg" }, function(error, body, response) { if (error) { console.log("error:"); console.log(error); } else { console.log("body:" + body); } }); });</pre>來自:http://blog.csdn.net/problc/article/details/7225419

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!