QMLèªäœã¯ãQtãã¬ãŒã ã¯ãŒã¯ã®äžéšã§ãã宣èšåJavaScriptã®ãããªããã°ã©ãã³ã°èšèªã§ãã Qtéçºè ã¯çé¢ç®ã§ãã€ã³ã¿ãŒãã§ã€ã¹ãäœæããããã®äž»èŠãªããŒã«ãšããŠå®£äŒããŠããŸãã ããã«ãWebãµãŒããŒãæäœããæ©èœãªã©ãC ++ã«é Œãããšãªããéåžžã«å€ãã®ããšãå®è¡ã§ããŸãã
Webãã¯ãããžãŒã¯ç§ãã¡ã®ç掻ã«ãŸããŸã浞éããŠãããå€ãã®å ŽåãããŸããŸãªWebãªãœãŒã¹ã䜿çšããŠããŸãã ãã®ããã«ãã©ãŠã¶ãŒãèµ·åããããšã¯å¿ ããã䟿å©ã§ã¯ãããŸãããå¥ã®ã¯ã©ã€ã¢ã³ãã¢ããªã±ãŒã·ã§ã³ãã¯ããã«äŸ¿å©ãªå ŽåããããŸããããã¯ãããšãã°ã¢ãã€ã«ãã©ãããã©ãŒã ãªã©ãããŸããŸãªãœãŒã·ã£ã«ãããã¯ãŒã¯ã®ã¯ã©ã€ã¢ã³ãæ°ã«ãã£ãŠéåŒã«ç€ºãããŸãã
å é±ãªãªãŒã¹ãããã¢ã«ãã¡çã®Qt 5.1ã«ã¯AndroidãšiOSã®åæãµããŒããå«ãŸããŠããããšãèãããšããã®ãããã¯ã¯QtãèŠãŠããããç©æ¥µçã«ãã¹ã¿ãŒããŠãã人ã«ãšã£ãŠç¹ã«èå³æ·±ããã®ã«ãªããŸãã ãã®èšäºã§ã¯ãVK APIã®äŸã䜿çšããŠQMLã¢ããªã±ãŒã·ã§ã³ããWebãªãœãŒã¹ã䜿çšããŠäœæ¥ãæŽçããæ¹æ³ã説æããŸãã
念ã®ãããQt 5.0.2ã®ææ°ã®å®å®ããŒãžã§ã³ãæ€èšããŠããããšã«æ³šæããŠãã ããã 以åã®ããŒãžã§ã³ã§ã¯ãäžéšã®æ©èœãããã§ãªãå ŽåããããŸãã
XMLHttpRequestãšã¯äœãããªãå¿ èŠãªã®ã
確ãã«ãèªè ã®å€ãã¯AJAXïŒAsynchronous JavaScript And XMLïŒãªã©ã®ãã¯ãããžãŒã«ã€ããŠèããããšãããã§ãããã ããã«ãããéåæãªã¯ãšã¹ãããµãŒããŒã«éä¿¡ãããªããŒãããã«ããŒãžã®ã³ã³ãã³ããæŽæ°ã§ããŸãã æè¿ã®ãã©ãŠã¶ã«ã¯ããã®ããã®ããŸããŸãªããŒã«ããããXMLHttpRequestããã®1ã€ã§ãã QMLã¯JavaScriptã«äŒŒãèšèªã§ããããã®äžã®JavaScriptç°å¢ã¯ãã©ãŠã¶ãŒã«äŒŒãŠãããããXMLHttpRequestãååšããŸãã ããã¹ãã®åŸåã§ã¯ããã®ååãçç¥åœ¢-XHRã§æžãçããŸãã
å®éãããã¯äœã§ãããç§ãã¡ã«äœãäžããŸããïŒ ããã¯ãéåæïŒãã©ãŠã¶ãŒã§åæãã©ãŠã¶ãŒããµããŒããããŠããïŒHTTPèŠæ±çšã®ããŒã«ã§ãã ãã®ååã«ãããããããå ã ã¯ãã®ç®çã®ããã«æå³ãããŠããŸããããXML圢åŒã ãã§ãªãããŒã¿ã転éããããšãã§ããŸãã QMLãšã³ãžã³ã®å®è£ ã¯ãGETãPOSTãHEADãPUTãããã³DELETEã®HTTPèŠæ±ããµããŒãããŠããŸãã åºæ¬çã«ãæåã®2ã€ã䜿çšããŸãã
QMLã§ã®XHRã®å®è£ ã®ç¹åŸŽçãªæ©èœã¯ããªã¯ãšã¹ããä»»æã®ãã¹ãã«éä¿¡ã§ããããšã§ãããã©ãŠã¶ã®ãããªå¶éã¯ãããŸããã
XMLHttpRequestããã·ãŒãžã£
XHRã䜿çšããããã»ã¹ã¯æ¬¡ã®ãšããã§ãã
1. XHRãªããžã§ã¯ããäœæããŸãã
var request = new XMLHttpRequest()
2.ãªããžã§ã¯ããåæåããŠããªã¯ãšã¹ãã®ã¿ã€ãïŒå¥åHTTPã¡ãœããïŒãã¢ãã¬ã¹ãããã³å¿ èŠã«å¿ããŠãªã¯ãšã¹ããã©ã¡ãŒã¿[1]ã瀺ãããµãŒããŒã«éä¿¡ããå¿ èŠããããŸãã
request.open('GET', 'http://site.com?param1=value1¶m2=value2')
æåã®ãã©ã¡ãŒã¿ãŒã¯ãªã¯ãšã¹ãã®ã¿ã€ãã2çªç®ã¯URLã§ãã GETãªã¯ãšã¹ãã®å Žåããã©ã¡ãŒã¿ãŒã¯ããã«æž¡ãããèšå·ãïŒãã§ã¢ãã¬ã¹ããåé¢ãããŠããå¿ èŠããããŸãã ãã©ã¡ãŒã¿ãŒã¯ãïŒãæåã§åºåãããŸãã
POSTãªã¯ãšã¹ãã®å Žåãã³ã³ãã³ãã®ã¿ã€ããæå®ããå¿ èŠããããŸãã ã¯ãšãªãã©ã¡ãŒã¿ã§ããŒã¿ãæž¡ãå Žåãããã¯æ¬¡ã®ããã«è¡ãããŸãã
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
3.ãã³ãã©ãŒãèšå®ããŠããªã¯ãšã¹ãã®ç¶æ ãå€æŽããŸãã ã»ãšãã©ã®å Žåããªã¯ãšã¹ããå®äºãããŸã§åŸ ã£ãŠãããçµæãŸãã¯ãšã©ãŒãåŠçããã ãã§ãã ãªã¯ãšã¹ãã®æåŸã§ãreadyStateãã©ã¡ãŒã¿ãŒã¯XMLHttpRequest.DONEãšçãããªããŸãïŒå€ã®è©³çŽ°ã«ã€ããŠã¯ã [2]ãåç §ããŠãã ããïŒã
request.onreadystatechange = function () { if (request.readyState === XMLHttpRequest.DONE) { if (request.status === 200) { console.log(request.responseText) } else { console.log("HTTP request failed", request.status) } } }
å¿åé¢æ°ã¯ãå€æŽã®ãã³ã«readyStateãåŒã³åºããŸãã ãªã¯ãšã¹ããå®äºããããšã«é¢å¿ããããŸãããã®åŸããªã¯ãšã¹ããæ£åžžã«å®äºãããã©ããã確èªããŸãã ãããè¡ãã«ã¯ãæåã³ãŒãïŒ200ïŒã§ã¹ããŒã¿ã¹ã³ãŒãã確èªããŸãã HTTPã¯ããã¹ããããã³ã«ã§ãããã³ãŒãã®æ°å€ã«å ããŠãããã¹ãã®èª¬æãéä¿¡ããããããstatusTextããããã£ããã®ã¹ããŒã¿ã¹ã«å¯Ÿå¿ããæååãšæ¯èŒã§ããŸãããã®å Žåãããã¯æååãOKãã§ãã
if (request.statusText === 'OK')
ãšã©ãŒã®å Žåãstatusããã³statusTextã«ã¯ãHTTPã¹ããŒã¿ã¹ã³ãŒãã®ã³ãŒããšããã¹ãã®èª¬æãå«ãŸããŸãïŒããšãã°ããããã404ããã³ãNot FoundãïŒã
4.ãªã¯ãšã¹ããéä¿¡ããŸãã
request.send()
POSTã®å Žåãããã§ãªã¯ãšã¹ããã©ã¡ãŒã¿ãæž¡ãå¿ èŠããããŸãã
request.send('param1=value1¶m2=value2')
ã¯ãšãªãã©ã¡ãŒã¿ã§ãã¹ãŠã®æåãéä¿¡ã§ããããã§ã¯ãããŸããã ãããã£ãŠããã©ã¡ãŒã¿ãŒãšå€ã®äž¡æ¹ããšã³ã³ãŒãããå¿ èŠã«å¿ããŠãç¹å¥ãªé¢æ°encodeURIComponentïŒïŒããã³decodeURIComponentïŒïŒã§é©å®ãã³ãŒãããå¿ èŠããããŸãã 䜿çšäŸïŒ
request.send('%1=%2'.arg(encodeURIComponent(param)).arg(encodeURIComponent(value)))
ãšã³ã³ãŒããããæååãããã«åŠçããã·ãŒã±ã³ã¹ãïŒ 20ãïŒã€ãŸãããšã³ã³ãŒããããã¹ããŒã¹ïŒãã+ãæåã«çœ®ãæããããšããå§ãããŸãã ãã³ãŒãããåã«ãããããéãè¡ããŸãã
éåžžãã¯ãšãªãã©ã¡ãŒã¿ã¯åçŽåã®å€ãæž¡ããŸãã é åãæž¡ãããšãã§ããŸãããæ§æã¯ããæ¿ã£ãŠããŸãã ããšãã°ã2ã€ã®å€ã®ãã©ã¡ãŒã¿ãŒã®é åãéä¿¡ãããšã次ã®ããã«ãªããŸãã
request.send('params[]=value1¶ms[]=value2')
å°çããæãåºãã°ããªããžã§ã¯ããå€ãšããŠè»¢éããããšããã§ããŸãïŒïŒïŒãããããããã¯å®å šã«ä¿¡é Œã§ãããšã¯éããŸãããåä¿¡åŽã§é åã«å€æã§ãããšããæå³ã§:)
POSTãªã¯ãšã¹ãã䜿çšãããšããªã¯ãšã¹ããã©ã¡ãŒã¿ã ãã§ãªãããªã¯ãšã¹ãæ¬æã«ãããŒã¿ã転éã§ããŸãã ããšãã°ãJSON圢åŒã§ããŒã¿ãéä¿¡ã§ããŸãã ãããè¡ãã«ã¯ãæ£ããContent-Typeãšã³ã³ãã³ããµã€ãºïŒContent-LengthïŒãèšå®ããŸãã ãã®ãããªãªã¯ãšã¹ããéä¿¡ããäŸïŒ
request.setRequestHeader('Content-Type', 'application/json') var params = { param1: value1, param2: value2 } var data = JSON.stringify(params) request.setRequestHeader('Content-Length', data.length) request.send(data)
ããã§ãJSONã¯QMLã§å©çšå¯èœãªã°ããŒãã«ãªããžã§ã¯ãã§ããããã®åœ¢åŒã§äœæ¥ããããã®ããŒã«ãæäŸããŸã[3] ã
å®éãããŒã¿ã転éã§ãã圢åŒã¯ãµãŒããŒã«ãã£ãŠæ±ºå®ãããŸãã JSONãåãå ¥ããå Žå-眰éãJSONãã«ã¡ããã ããŒã¿ã¯èŠæ±ãã©ã¡ãŒã¿ãŒã«ãã£ãŠååŸãããããšãæåŸ ããŠãããããéä¿¡ããå¿ èŠããããŸãã
å¿ èŠãªçè«çæ å ±ãåŠç¿ããã®ã§ãVKontakteã§ã®ç·Žç¿ãšäœæ¥ãéå§ããŸãã
åéã®ãªã¹ããååŸããŠè¡šç€ºãã
ãŸããæ¿èªããã®ä»ã®äžèŠãªãžã§ã¹ãã£ãå¿ èŠãšããªãã¡ãœããã䜿çšããç°¡åãªäŸãæ€èšããŠãã ããã åéãªã¹ããååŸãããšããã®ã«ããŽãªã«åé¡ãããŸãã XHRãéä¿¡ããŠèµ·åæã«å人ã®ãªã¹ããååŸããåä¿¡åŸã«ãŠãŒã¶ãŒåãšã¢ãã¿ãŒã衚瀺ããç°¡åãªããã°ã©ã ãäœæããŸãã
ã»ãšãã©ã®ã³ãŒãã¯ãã£ã¹ãã¬ã€ã€ã³ã¿ãŒãã§ã€ã¹ã§ãããå ·äœçã«èª¬æããæå³ã¯ãããŸããã JavaScriptãªããžã§ã¯ããŸãã¯é åãã¢ãã«ãšããŠäœ¿çšãããå Žåãã¢ãã«ã®ä»£ããã«modelDataãã¢ãã«ããŒã¿ãååŸããããã«äœ¿çšãããããšã«æ³šæããŠãã ããã
ããã§æãèå³æ·±ãéšåã¯ããµãŒããŒã§ã®äœæ¥ã§ãã VK APIã«ã¢ã¯ã»ã¹ããã«ã¯ãç¹å¥ãªã¢ãã¬ã¹api.vk.com/methodããããŸãã ã¡ãœããã®ååãåä¿¡ããã¢ãã¬ã¹ã«è¿œå ããŸãïŒã¡ãœããã®ãªã¹ãã¯[4]ã«ãããŸã ïŒããã®å Žåã¯friends.getã¡ãœããã§ãã ãã®ã¢ãã¬ã¹ã«ãå¿ èŠãªãã©ã¡ãŒã¿ãŒãæå®ããŠPOSTãŸãã¯GETãªã¯ãšã¹ããéä¿¡ããå¿ èŠããããŸãã çãã¯JSON圢åŒã§ãã uidãã©ã¡ãŒã¿ãŒã§ãŠãŒã¶ãŒIDãæž¡ãå¿ èŠããããŸãã ãŸããfieldsãã©ã¡ãŒã¿ãŒã§ãå¥ã®photo_mediumãæž¡ããŠåçãååŸããŸããããã«ãããåçãæå°ãµã€ãºã«ãªããŸããã
以äžã¯ãããã°ã©ã ã®å®éã®ãœãŒã¹ã³ãŒãã§ãã mainã®userIdã¯ãŠãŒã¶ãŒIDã§ãã
import QtQuick 2.0 Rectangle { id: main property int userId: XXX property var friends width: 320 height: 640 color: 'skyblue' function getFriends() { var request = new XMLHttpRequest() request.open('POST', 'https://api.vk.com/method/friends.get') request.onreadystatechange = function() { if (request.readyState === XMLHttpRequest.DONE) { if (request.status && request.status === 200) { console.log("response", request.responseText) var result = JSON.parse(request.responseText) main.friends = result.response } else { console.log("HTTP:", request.status, request.statusText) } } } request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') request.send('fields=photo_medium&uid=%1'.arg(main.userId)) } ListView { id: view anchors.margins: 10 anchors.fill: parent model: friends spacing: 10 delegate: Rectangle { width: view.width height: 100 anchors.horizontalCenter: parent.horizontalCenter color: 'white' border { color: 'lightgray' width: 2 } radius: 10 Row { anchors.margins: 10 anchors.fill: parent spacing: 10 Image { id: image height: parent.height fillMode: Image.PreserveAspectFit source: modelData['photo_medium'] } Text { width: parent.width - image.width - parent.spacing anchors.verticalCenter: parent.verticalCenter elide: Text.ElideRight renderType: Text.NativeRendering text: "%1 %2".arg(modelData['first_name']).arg(modelData['last_name']) } } } } Component.onCompleted: { getFriends() } }
ç§ã¯çãã«äœãæ¥ããã«ã€ããŠã³ã³ãœãŒã«ã«çµè«ãåºããŸããããã®äŸãè©ŠããŠã¿ãããšããé¡æãããã°äŸ¿å©ã§ãã
æå¹ãªIDãæå®ãããŠããå Žåãããã°ã©ã ãå®è¡ãããšã次ã®å³ã衚瀺ãããŸãã
ããã§ã®æ倧ã®é£ç¹ã¯ãæ£ç¢ºã«XHRãæäœããããšã§ãã ãããç解ããŠãã³ãŒããå°ãåçŽåããŠã¿ãŸãããã
XMLHttpRequestã䜿çšããäœæ¥ã®ç°¡çŽ å
XHRã䜿çšããã«ã¯2ã€ã®èª²é¡ããããŸãã
1.ãªã¯ãšã¹ããã©ã¡ãŒã¿ã§ããŒã¿ãéä¿¡ããå Žåããã®ãªã¯ãšã¹ããã³ã³ãã€ã«ããå¿ èŠããããŸãã ãããã®ãã©ã¡ãŒã¿ãŒãå€æŽãããå¯èœæ§ãããå Žåãã»ãšãã©ã®å ŽåãèŠæ±ãã©ã¡ãŒã¿ãŒãæçããæ¥çããã³ãŒãã«å€ãã®æäœããããŸãã ããã«ãäžã§æžããããã«ãã³ã³ãã€ã«æã«encodeURIComponentã䜿çšããŠããŒãšå€ããšã³ã³ãŒãããã®ãè¯ãããšãå¿ããŠã¯ãªããŸããã å šäœãšããŠããããã®ãã©ã¡ãŒã¿ãŒã圢æããã³ãŒãã¯æ±ãã«ãããããŸãæ確ã§ã¯ãªãããšãå€æããå ŽåããããŸãã 察å¿ãããã£ãŒã«ãããã©ã¡ãŒã¿ãšããŠèšå®ãããŠãããªããžã§ã¯ãã䜿çšããæ¹ãã¯ããã«äŸ¿å©ã§ãã
ãªããžã§ã¯ããã¯ãšãªãã©ã¡ãŒã¿ãŒã«å€æããå°ããªJavaScriptã©ã€ãã©ãªãèšè¿°ããŸããããã¹ãŠããšã³ã³ãŒããããšãäžè¬ã«ãããã«éä¿¡ã§ããå®æããæååãçæãããŸãã ã¯ãšãªãã©ã¡ãŒã¿ããã³ãŒããããããããªããžã§ã¯ããäœæããé¢æ°ããããŸãïŒãã ããåçŽãªåã®ã¿ããµããŒããããã©ã¡ãŒã¿å ã®é åãŸãã¯ãªããžã§ã¯ãã¯è§£æãããŸããããå¿ èŠã«ãªãããšã¯ã»ãšãã©ãããŸããïŒã github.com/krnekit/qml-utils/blob/master/qml/URLQuery.jsã§å ¥æã§ããŸãã
2.èŠæ±ã®ã¿ã€ãã«å¿ããŠãããŒã¿ã¯ããŸããŸãªæ¹æ³ã§éä¿¡ããå¿ èŠããããè¿œå ã®ããããŒãèšå®ããå¿ èŠãããå ŽåããããŸãã åäžã®ã€ã³ã¿ãŒãã§ã€ã¹ãæäŸããããšã§ãXHRãç°¡åã«éä¿¡ã§ããã©ã€ãã©ãªãäœæããŸããã ããŒã¿ãä»»æã®åœ¢åŒã§éä¿¡ã§ããŸãããã®ãããã³ã³ãã³ãã¿ã€ãããã©ã¡ãŒã¿ãŒãšããŠæž¡ãããšãã§ããŸããããã©ã«ãã§ã¯ãåããapplication / x-www-form-urlencodedããšèŠãªãããŸãããGETãªã¯ãšã¹ãã䜿çšããŠç°ãªãã¿ã€ãã®ããŒã¿ãéä¿¡ããããšã¯ã§ããŸããããã®å ŽåãPOSTã䜿çšããå¿ èŠããããŸãã Content-Lengthãèªåçã«èšç®ããã³èšå®ãããŸãã ãªã¯ãšã¹ãã¿ã€ããURLãã³ãŒã«ããã¯é¢æ°ïŒãªãã·ã§ã³ïŒããªã¯ãšã¹ãã®å®äºæã«åŒã³åºãããã³ãŒã«ããã¯é¢æ°ãããã³ããŒã¿ã¿ã€ãïŒãªãã·ã§ã³ïŒãåãå ¥ããŸãã ãã®é¢æ°ã¯ãèŠæ±ãªããžã§ã¯ãèªäœãè¿ããããšã©ãŒã®å Žåã¯nullãè¿ããŸãã ãã¡ãã§å ¥æã§ããŸãïŒ github.com/krnekit/qml-utils/blob/master/qml/XHR.js
2ã€ã®ã©ã€ãã©ãªããŒã¿ã䜿çšããŠãåã®äŸãåçŽåããŸããã ããã§ã¯ãã¹ãŠã®ã³ãŒããæäŸããããã§ã¯ãããŸãããå€æŽç¹ã®ã¿ãèæ ®ããŸãã
ãã¡ã€ã«ã®å é ã«ãã©ã€ãã©ãªãå«ããŸãïŒãã®äŸã§ã¯ãã©ã€ãã©ãªãã¡ã€ã«ã¯qmlãã¡ã€ã«ãšåããã£ã¬ã¯ããªã«ãããŸãïŒã
import 'URLQuery.js' as URLQuery import 'XHR.js' as XHR
ã©ã€ãã©ãªãã€ã³ããŒãããã©ã€ãã©ãªã®é¢æ°ã«ã¢ã¯ã»ã¹ããããã®ã©ã€ãã©ãªã®åå空éãèšå®ããŸãã
XHRãéä¿¡ããé¢æ°ã¯æ¬¡ã®ããã«ãªããŸãã
function getFriends() { var params = { fields: 'photo_medium', uid: main.userId } function callback(request) { if (request.status && request.status === 200) { console.log("response", request.responseText) var result = JSON.parse(request.responseText) main.friends = result.response } else { console.log("HTTP:", request.status, request.statusText) } } XHR.sendXHR('POST', 'https://api.vk.com/method/friends.get', callback, URLQuery.serializeParams(params)) }
ãŸããã¯ãšãªãã©ã¡ãŒã¿ã䜿çšããŠãªããžã§ã¯ããå®çŸ©ããŸãã 次ã«ãèŠæ±ãå®äºãããšåŒã³åºãããã³ãŒã«ããã¯é¢æ°ã é¢æ°ã¯ããªã¯ãšã¹ãèªäœããã©ã¡ãŒã¿ãŒãšããŠåãåããŸãã ãããŠããªã¯ãšã¹ãèªäœãéä¿¡ããserializeParamsé¢æ°ã䜿çšããŠãã©ã¡ãŒã¿ãŒã§ãªããžã§ã¯ããå€æããŸãã
ãã®çµæãã³ãŒãã®ãµã€ãºã¯å€æŽãããŠããªããããããŸããããæ§é åãããç解ãããããªã£ãŠããŸãã
å°æ¥ãããã®é¢æ°ã䜿çšããŠãã³ãŒããåçŽã«ããŸãã 誰ãã«åœ¹ç«ã€å Žåã¯ãMITã©ã€ã»ã³ã¹ãååŸããŠäœ¿çšã§ããŸãã
QMLããã®æ¿èªVK
ãã¹ãŠã®ã¡ãœãããèš±å¯ãªãæ©èœããããã§ã¯ãªãããããããããã°ã€ã³ããå¿ èŠããããŸãã ãã®çµæããããã èš±å¯ããŒã¯ã³ãVKontakteã«ãªã¯ãšã¹ãã§è»¢éããŸãã ãã°ã€ã³ã§ããããã«ãVKontakteã§ã¢ããªã±ãŒã·ã§ã³ãäœæããå¿ èŠããããŸãã ããã¯ã vk.com / editappïŒact = createã§å®è¡ã§ããŸãã ã¹ã¿ã³ãã¢ãã³ã¢ããªã±ãŒã·ã§ã³ã®ã¿ã€ããéžæããŸãã 次ã«ããªã¯ãšã¹ããã©ã¡ãŒã¿ã®1ã€ã§ãã®IDã転éããŸãã
1.èªèšŒæ¹æ³
ã¹ã¿ã³ãã¢ãã³ã¢ããªã±ãŒã·ã§ã³ãã€ãŸã2ã€ã®èªèšŒæ¹æ³ãäœæããŠãããããã©ã¡ãã«ãç¬èªã®åé¡ããããŸãããã®ãããæã害ã®å°ãªããã®ãéžæããå¿ èŠããããŸãã
1.çŽæ¥èªèšŒã HTTPãªã¯ãšã¹ãã¯ããã°ã€ã³æ å ±ãšãšãã«ç¹å®ã®ã¢ãã¬ã¹ã«éä¿¡ãããŸãã å¿çã¯ãããŒã¯ã³ãŸãã¯ãšã©ãŒã®èª¬æãå«ãJSON圢åŒã®ããŒã¿ã«ãªããŸãã
å©ç¹ïŒ
- ã·ã³ãã«ã
çæïŒ
- ã¢ããªã±ãŒã·ã§ã³ã®ã·ãŒã¯ã¬ããã³ãŒãã転éããå¿ èŠããããŸãïŒããã°ã©ã ã«çž«ãä»ããªããã°ãªããªãå ŽåããããŸãïŒããã®ããããã®ãªãŒã¯ã®ãªã¹ã¯ããããŸãã
- ãã®æ¹æ³ã¯ãä¿¡é Œã§ããã¢ããªã±ãŒã·ã§ã³ã§ã®ã¿æ©èœããŸãã æ°ããã¢ããªã±ãŒã·ã§ã³ãäœæãããšã䜿çšã§ããªããªãããµããŒããäœæããŠæå¹ã«ããå¿ èŠããããŸãã
2. OAuthèªèšŒã 次ã®ããã«å®è£ ãããŸãã ãŠãŒã¶ãŒã«ç¹å¥ãªãã°ã€ã³ããŒãžã衚瀺ãããããã°ã©ã ã«ãã©ãŠã¶ãçµã¿èŸŒãå¿ èŠããããŸãã æ¿èªåŸãå¥ã®ããŒãžãžã®ãªãã€ã¬ã¯ããçºçããçŸåšã®URLã«ããŒã¯ã³ãŸãã¯ãšã©ãŒã®èª¬æãå«ãŸããŸãã VKontakteã¯ãã®æ¹æ³ãäž»ãªæ¹æ³ãšããŠäœçœ®ä»ããŠããŸãã
å©ç¹ïŒ
- äž»ãªéåžžã«éèŠãªå©ç¹ã¯ããã¹ãŠã®ã¢ããªã±ãŒã·ã§ã³ãšçŽæ¥èªèšŒãèš±å¯ãããŠããªãã¢ããªã±ãŒã·ã§ã³ã§æ©èœããããšã§ãããããäžè¬çã«å¯äžã®æ¹æ³ã§ãã
- ç§å¯éµãæž¡ãå¿ èŠã¯ãããŸããã
- OAuthã¯æšæºã§ãããããšãã°Facebookã«ãã°ã€ã³ããããšãã§ããŸãã
ãã ããæ¬ ç¹ãéèŠã§ãã
- VKããŒãžãéãå¿ èŠããããŸããã€ãŸããããã°ã©ã ãŠã£ã³ããŠã«åã蟌ãããå¥ã®ãŠã£ã³ããŠã§éãããéžæããŸãã
- ããŒãžãéãããããã©ãŠã¶ãå¿ èŠã§ãã ãããã£ãŠãQtWebkitãšãããåŒãå¯ãããã¹ãŠã®ãã®ããã©ãã°ããå¿ èŠããããŸããããã«ãããããã°ã©ã ãéããªããŸãã
- çµã¿èŸŒã¿ãã©ãŠã¶ã®URLãå€æŽããã€ãã³ããã€ã³ã¿ãŒã»ãããããã®URLã解æããŠããããããã©ã¡ãŒã¿ãéžæããå¿ èŠããããŸããããã¯ãXHRããããè€éã§ãã
2.çŽæ¥èªèšŒ
ãã¡ãããçŽæ¥èªèšŒãæå¹ã«ããããã«èŠæ±ããŸããããVKontakteã®ãµããŒãã¯æåã«äœãå¿ èŠãããã£ãããšå°ãããããå®å šã«ã¯ã©ã³ãããŸãã:(ãããã£ãŠãçŽç²ã«çè«çã«èããŸãã次ã®ããã«ãªããŸãã
function login() { var params = { grant_type: 'password', client_id: 123456, client_secret: 'XXX', username: 'XXX', password: 'XXX', scope: 'audio' } function callback(request) { if (request.status && request.status === 200) { console.log("response", request.responseText) var result = JSON.parse(request.responseText) if (result.error) { console.log("Error:", result.error, result.error_description) } else { main.authToken = result.auth_token // Now do requests with this token } } else { console.log("HTTP:", request.status, request.statusText) } } XHR.sendXHR('POST', 'https://oauth.vk.com/token', callback, URLQuery.serializeParams(params)) }
æåã«ããã©ã¡ãŒã¿ãŒãäœæããŸãããã©ã¡ãŒã¿ãŒã«ã¯ãããšãã°ããŠãŒã¶ãŒã®ãªãŒãã£ãªã¬ã³ãŒããžã®ã¢ã¯ã»ã¹ãå¿ èŠã§ããããšã瀺ããŸããïŒã¹ã³ãŒããã©ã¡ãŒã¿ãŒïŒã 次ã«ãã³ãŒã«ããã¯é¢æ°ã¯ããšã©ãŒãçºçããå Žåã«ã³ã³ãœãŒã«ã«æžã蟌ã¿ãæåããå Žåã¯ããŒã¯ã³ãä¿åããAPIãªã¯ãšã¹ããç¶è¡ã§ããŸãã
念ã®ãããããã¥ã¡ã³ããžã®ãªã³ã¯ãæ®ããŸãïŒ vk.com/dev/auth_direct
3. OAuthã«ããæ¿èªã
ãã®ã¿ã€ãã®èªèšŒã§ã¯ããŠãŒã¶ãŒã«ãã°ã€ã³WebããŒãžã衚瀺ããå¿ èŠããããŸãã QtQuickã«ã¯ãWebKitãšã³ãžã³ãQMLã¢ããªã±ãŒã·ã§ã³ã«åã蟌ãããšãã§ããWebViewã³ã³ããŒãã³ãããããŸãã ãŠãŒã¶ãŒããã°ã€ã³ãããšããã©ãŠã¶ãŒã®URLãå€æŽãããèªèšŒãæåããå Žåããªã¯ãšã¹ããã©ã¡ãŒã¿ãŒã«ããŒã¯ã³ãŸãã¯ã¢ã³ã«ãŒã®ãšã©ãŒã®èª¬æãå«ãŸããŸã[5] ã
ãã®URLã解æããŠã ãŸãããªãããã«ãURLQueryã®parseParamsé¢æ°ã䜿çšããŸãã URLå šäœãäžåºŠã«æž¡ãããšãã§ããåºåæã«ãã©ã¡ãŒã¿ãŒä»ãã®ãªããžã§ã¯ããååŸããŸãã
ãã®æ©èœãå®è£ ããã³ã³ããŒãã³ãã以äžã«èª¬æããŸãã
LoginWindow.qmlïŒ
import QtQuick 2.0 import QtQuick.Window 2.0 import QtWebKit 3.0 import "URLQuery.js" as URLQuery Window { id: loginWindow property string applicationId property string permissions property var finishRegExp: /^https:\/\/oauth.vk.com\/blank.html/ signal succeeded(string token) signal failed(string error) function login() { var params = { client_id: applicationId, display: 'popup', response_type: 'token', redirect_uri: 'http://oauth.vk.com/blank.html' } if (permissions) { params['scope'] = permissions } webView.url = "https://oauth.vk.com/authorize?%1".arg(URLQuery.serializeParams(params)) } width: 1024 height: 768 WebView { id: webView anchors.fill: parent onLoadingChanged: { console.log(loadRequest.url.toString()) if (loadRequest.status === WebView.LoadFailedStatus) { loginWindow.failed("Loading error:", loadRequest.errorDomain, loadRequest.errorCode, loadRequest.errorString) return } else if (loadRequest.status === WebView.LoadStartedStatus) { return } if (!finishRegExp.test(loadRequest.url.toString())) { return } var result = URLQuery.parseParams(loadRequest.url.toString()) if (!result) { loginWindow.failed("Wrong responce from server", loadRequest.url.toString()) return } if (result.error) { loginWindow.failed("Error", result.error, result.error_description) return } if (!result.access_token) { loginWindow.failed("Access token absent", loadRequest.url.toString()) return } succeeded(result.access_token) return } } }
ãã®ã³ã³ããŒãã³ããå¥ã®ãŠã£ã³ããŠã«è¡šç€ºããŸãã loginïŒïŒã¡ãœãããåŒã³åºããåŸããã°ã€ã³ããŒãžãããŒããããŸãã
æ¿èªåŸã oauth.vk.com / blank.htmlãã¢ãã¬ã¹ãšããŠäœ¿çšãããURLã«ãªãã€ã¬ã¯ãããããïŒããä»ããŠãªãã€ã¬ã¯ããããŸãã ãŸãã¯ãïŒãã®çµæã衚瀺ãããŸãã permissionsãã©ã¡ãŒã¿ãŒã䜿çšããŠãå¿ èŠãªã¢ã¯ã»ã¹æš©ãèšå®ããŸãã ããã«äœããæå®ãããšããŠã£ãžã§ãããä»ããŠãã°ã€ã³ãããšãã«ãã¢ããªã±ãŒã·ã§ã³ã«ã¢ã¯ã»ã¹æš©ãä»äžããããã®ãã€ã¢ãã°ã衚瀺ãããŸãã
æ£ããã¢ãã¬ã¹ã«å°éããã¿ã€ãã³ã°ãç解ããããã«ãonLoadingChangedãã³ãã©ãŒãèšå®ããŸãã loadRequestãªããžã§ã¯ããåãåããããããå¿ èŠãªãã¹ãŠã®æ å ±ãååŸããŸãã ããã¯æ°ååŒã³åºããããšã©ãŒãçºçãããšãããã®å Žåã¯é©åãªä¿¡å·ãéä¿¡ãããšãããŸãã¯ç®çã®ããŒãžãããŒãããããšãã®ç¶æ³ã«é¢å¿ããããŸãã ãã®å ŽåãããŒã¯ã³ãå°çãããã©ããã確èªããå°çããå Žåã¯æ¿èªã®æåã«é¢ããã·ã°ãã«ãéä¿¡ããããã§ãªãå Žåã¯ãšã©ãŒã·ã°ãã«ãéä¿¡ããŸãã
ããŠããã®ãŠã£ãžã§ããã䜿çšããããã°ã©ã èªäœãèããŠã¿ãŸãããã èªèšŒã«æåããå Žåãããã°ã©ã ã¯ãŠãŒã¶ãŒã¹ããŒã¿ã¹ãããã¹ããã«èšå®ããŸãã ãŠãŒã¶ãŒIDã¯ãmainã®userIdããããã£ã«ãã£ãŠèšå®ãããŸãã
import QtQuick 2.0 import 'URLQuery.js' as URLQuery import 'XHR.js' as XHR Rectangle { id: main property int userId: XXX property var authToken width: 640 height: 320 function processLoginSuccess(token) { loginWindow.visible = false authToken = token setStatus() } function setStatus() { var params = { access_token: main.authToken, text: 'test' } function callback(request) { if (request.status == 200) { console.log('result', request.responseText) var result = JSON.parse(request.responseText) if (result.error) { console.log('Error:', result.error.error_code,result.error.error_msg) } else { console.log('Success') } } else { console.log('HTTP:', request.status, request.statusText) } Qt.quit() } XHR.sendXHR('POST', 'https://api.vk.com/method/status.set', callback, URLQuery.serializeParams(params)) } LoginWindow { id: loginWindow applicationId: XXX permissions: 'status' visible: false onSucceeded: processLoginSuccess(token) onFailed: { console.log('Login failed', error) Qt.quit() } } Component.onCompleted: { loginWindow.visible = true loginWindow.login() } }
ããŒãåŸããã°ã€ã³ãŠã£ã³ããŠã衚瀺ãããŸãã ãã°ã€ã³åŸãé衚瀺ã«ãªãããŠãŒã¶ãŒã®ã¹ããŒã¿ã¹ãå€æŽããèŠæ±ããµãŒããŒã«éä¿¡ãããŸãã ãã®åŸãããã°ã©ã ã¯çµæãã³ã³ãœãŒã«ã«æžã蟌ã¿ãçµäºããŸãã
ãã°ã€ã³åŸãè¿œå ã®ã¢ã¯ã»ã¹æš©ãäžèŠã§ãããããã®æå¹æéãåããŠããªãå Žåã¯ãããŒã¯ã³ããªã¯ãšã¹ãããå¿ èŠã¯ãããŸããïŒèªèšŒãæåããå ŽåãããŒã¯ã³ãšãšãã«è¿ãããŸãïŒã
XMLHttpRequestãä»ã«äœ¿çšã§ãããã®
VKontakteã§ã¯ãªãXHRã«é¢é£ããç§ã®çµéšããã®çã話ãã話ãããŸãã
ã©ããããããç§ã®ååã«ã¯ãQMLã§XMLããŒã¿ãåä¿¡ããŠââåŠçããã¿ã¹ã¯ããããŸããã
QtQuickã«ã¯ãXMLãã¡ã€ã«ãã¢ãã«ãšããŠåŒãåºãã解æãã¬ã³ããªã³ã°ã§ããç¹å¥ãªã¿ã€ãã®XmlListModelããããŸãã 圌ã¯ãXPathã¿ã€ãã®ã¯ãšãªãèšå®ããå¿ èŠããããããã«å¿ããŠã¢ãã«ãæºããããŸãã åé¡ã¯ãXMLãã¡ã€ã«ã«ãéžæããŠã¢ãã«ã«é 眮ããå¿ èŠãããèŠçŽ ã ãã§ãªããååŸããå¿ èŠãããè¿œå æ å ±ãå«ãŸããŠããããšã§ãã
ããã€ãã®è§£æ±ºæ¹æ³ããããŸãã 2ã€ã®XmlListModelãªããžã§ã¯ãã䜿çšã§ããŸãããããã¯æ確ãªæŸèæã§ããããã«ãXMLãã¡ã€ã«ã2åããŠã³ããŒãããããããŸããïŒãã§ãã¯ãããŸãïŒã ãã®æ©èœã¯ãããã€ãã®ããŒãµãŒãªãã·ã§ã³ãå«ãQtã䜿çšããŠå®è£ ã§ããŸãããçŽç²ãªQMLã®åé¡ã解決ããããšããèŠæããããŸããã
XMLHttpRequestã¯å ã XMLãæäœããããšãç®çãšããŠãããããXMLãæäœããããã®ããŒã«ããããŸãã ãããã£ãŠãããŒã«ã䜿çšããŠXMLãååŸããã³è§£æããå¿ èŠãªæ å ±ãéžæã§ããŸãã 次ã«ãåãXMLãXmlListModelã«è»¢éã§ããŸãïŒURIã ãã§ãªããXMLãã¡ã€ã«ã®ã³ã³ãã³ããæž¡ãããšãã§ããŸãïŒã
ãããã£ãŠãçŸåšXMLHttpRequestããããããã®ã«äœ¿çšãããŠãããšããäºå®ã«ããããããããªãäœæãããã®ããXMLãæäœããããã®ããŒã«ãããããšãå¿ããŠã¯ãªããŸããã
çãèŠçŽ
QMLã«ã¯ããã©ãŠã¶ã§JavaScriptçšã«äœ¿çšã§ããå€ãã®ããŒã«ãå«ãŸããŠããŸãã XMLHttpRequestã䜿çšãããšãHTTPèŠæ±ãéä¿¡ã§ãããããQMLã¢ããªã±ãŒã·ã§ã³ãšWebãªãœãŒã¹ã確å®ã«çµ±åã§ããŸãã XHRã䜿çšãããšãå€ãã®å ŽåãC ++ã䜿çšããã«ãµãŒããŒãšããŒã¿ã亀æã§ãããããéçºãç°¡åã«ãªããŸãã