RestController.class.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. namespace Think\Controller;
  12. use Think\Controller;
  13. use Think\App;
  14. /**
  15. * ThinkPHP REST控制器类
  16. */
  17. class RestController extends Controller {
  18. // 当前请求类型
  19. protected $_method = '';
  20. // 当前请求的资源类型
  21. protected $_type = '';
  22. // REST允许的请求类型列表
  23. protected $allowMethod = array('get','post','put','delete');
  24. // REST默认请求类型
  25. protected $defaultMethod = 'get';
  26. // REST允许请求的资源类型列表
  27. protected $allowType = array('html','xml','json','rss');
  28. // 默认的资源类型
  29. protected $defaultType = 'html';
  30. // REST允许输出的资源类型列表
  31. protected $allowOutputType= array(
  32. 'xml' => 'application/xml',
  33. 'json' => 'application/json',
  34. 'html' => 'text/html',
  35. );
  36. /**
  37. * 架构函数
  38. * @access public
  39. */
  40. public function __construct() {
  41. // 资源类型检测
  42. if(''==__EXT__) { // 自动检测资源类型
  43. $this->_type = $this->getAcceptType();
  44. }elseif(!in_array(__EXT__,$this->allowType)) {
  45. // 资源类型非法 则用默认资源类型访问
  46. $this->_type = $this->defaultType;
  47. }else{
  48. $this->_type = __EXT__ ;
  49. }
  50. // 请求方式检测
  51. $method = strtolower(REQUEST_METHOD);
  52. if(!in_array($method,$this->allowMethod)) {
  53. // 请求方式非法 则用默认请求方法
  54. $method = $this->defaultMethod;
  55. }
  56. $this->_method = $method;
  57. parent::__construct();
  58. }
  59. /**
  60. * 魔术方法 有不存在的操作的时候执行
  61. * @access public
  62. * @param string $method 方法名
  63. * @param array $args 参数
  64. * @return mixed
  65. */
  66. public function __call($method,$args) {
  67. if( 0 === strcasecmp($method,ACTION_NAME.C('ACTION_SUFFIX'))) {
  68. if(method_exists($this,$method.'_'.$this->_method.'_'.$this->_type)) { // RESTFul方法支持
  69. $fun = $method.'_'.$this->_method.'_'.$this->_type;
  70. App::invokeAction($this,$fun);
  71. }elseif($this->_method == $this->defaultMethod && method_exists($this,$method.'_'.$this->_type) ){
  72. $fun = $method.'_'.$this->_type;
  73. App::invokeAction($this,$fun);
  74. }elseif($this->_type == $this->defaultType && method_exists($this,$method.'_'.$this->_method) ){
  75. $fun = $method.'_'.$this->_method;
  76. App::invokeAction($this,$fun);
  77. }elseif(method_exists($this,'_empty')) {
  78. // 如果定义了_empty操作 则调用
  79. $this->_empty($method,$args);
  80. }elseif(file_exists_case($this->view->parseTemplate())){
  81. // 检查是否存在默认模版 如果有直接输出模版
  82. $this->display();
  83. }else{
  84. E(L('_ERROR_ACTION_').':'.ACTION_NAME);
  85. }
  86. }
  87. }
  88. /**
  89. * 获取当前请求的Accept头信息
  90. * @return string
  91. */
  92. protected function getAcceptType(){
  93. $type = array(
  94. 'xml' => 'application/xml,text/xml,application/x-xml',
  95. 'json' => 'application/json,text/x-json,application/jsonrequest,text/json',
  96. 'js' => 'text/javascript,application/javascript,application/x-javascript',
  97. 'css' => 'text/css',
  98. 'rss' => 'application/rss+xml',
  99. 'yaml' => 'application/x-yaml,text/yaml',
  100. 'atom' => 'application/atom+xml',
  101. 'pdf' => 'application/pdf',
  102. 'text' => 'text/plain',
  103. 'png' => 'image/png',
  104. 'jpg' => 'image/jpg,image/jpeg,image/pjpeg',
  105. 'gif' => 'image/gif',
  106. 'csv' => 'text/csv',
  107. 'html' => 'text/html,application/xhtml+xml,*/*'
  108. );
  109. foreach($type as $key=>$val){
  110. $array = explode(',',$val);
  111. foreach($array as $k=>$v){
  112. if(stristr($_SERVER['HTTP_ACCEPT'], $v)) {
  113. return $key;
  114. }
  115. }
  116. }
  117. return false;
  118. }
  119. // 发送Http状态信息
  120. protected function sendHttpStatus($code) {
  121. static $_status = array(
  122. // Informational 1xx
  123. 100 => 'Continue',
  124. 101 => 'Switching Protocols',
  125. // Success 2xx
  126. 200 => 'OK',
  127. 201 => 'Created',
  128. 202 => 'Accepted',
  129. 203 => 'Non-Authoritative Information',
  130. 204 => 'No Content',
  131. 205 => 'Reset Content',
  132. 206 => 'Partial Content',
  133. // Redirection 3xx
  134. 300 => 'Multiple Choices',
  135. 301 => 'Moved Permanently',
  136. 302 => 'Moved Temporarily ', // 1.1
  137. 303 => 'See Other',
  138. 304 => 'Not Modified',
  139. 305 => 'Use Proxy',
  140. // 306 is deprecated but reserved
  141. 307 => 'Temporary Redirect',
  142. // Client Error 4xx
  143. 400 => 'Bad Request',
  144. 401 => 'Unauthorized',
  145. 402 => 'Payment Required',
  146. 403 => 'Forbidden',
  147. 404 => 'Not Found',
  148. 405 => 'Method Not Allowed',
  149. 406 => 'Not Acceptable',
  150. 407 => 'Proxy Authentication Required',
  151. 408 => 'Request Timeout',
  152. 409 => 'Conflict',
  153. 410 => 'Gone',
  154. 411 => 'Length Required',
  155. 412 => 'Precondition Failed',
  156. 413 => 'Request Entity Too Large',
  157. 414 => 'Request-URI Too Long',
  158. 415 => 'Unsupported Media Type',
  159. 416 => 'Requested Range Not Satisfiable',
  160. 417 => 'Expectation Failed',
  161. // Server Error 5xx
  162. 500 => 'Internal Server Error',
  163. 501 => 'Not Implemented',
  164. 502 => 'Bad Gateway',
  165. 503 => 'Service Unavailable',
  166. 504 => 'Gateway Timeout',
  167. 505 => 'HTTP Version Not Supported',
  168. 509 => 'Bandwidth Limit Exceeded'
  169. );
  170. if(isset($_status[$code])) {
  171. header('HTTP/1.1 '.$code.' '.$_status[$code]);
  172. // 确保FastCGI模式下正常
  173. header('Status:'.$code.' '.$_status[$code]);
  174. }
  175. }
  176. /**
  177. * 编码数据
  178. * @access protected
  179. * @param mixed $data 要返回的数据
  180. * @param String $type 返回类型 JSON XML
  181. * @return string
  182. */
  183. protected function encodeData($data,$type='') {
  184. if(empty($data)) return '';
  185. if('json' == $type) {
  186. // 返回JSON数据格式到客户端 包含状态信息
  187. $data = json_encode($data);
  188. }elseif('xml' == $type){
  189. // 返回xml格式数据
  190. $data = xml_encode($data);
  191. }elseif('php'==$type){
  192. $data = serialize($data);
  193. }// 默认直接输出
  194. $this->setContentType($type);
  195. //header('Content-Length: ' . strlen($data));
  196. return $data;
  197. }
  198. /**
  199. * 设置页面输出的CONTENT_TYPE和编码
  200. * @access public
  201. * @param string $type content_type 类型对应的扩展名
  202. * @param string $charset 页面输出编码
  203. * @return void
  204. */
  205. public function setContentType($type, $charset=''){
  206. if(headers_sent()) return;
  207. if(empty($charset)) $charset = C('DEFAULT_CHARSET');
  208. $type = strtolower($type);
  209. if(isset($this->allowOutputType[$type])) //过滤content_type
  210. header('Content-Type: '.$this->allowOutputType[$type].'; charset='.$charset);
  211. }
  212. /**
  213. * 输出返回数据
  214. * @access protected
  215. * @param mixed $data 要返回的数据
  216. * @param String $type 返回类型 JSON XML
  217. * @param integer $code HTTP状态
  218. * @return void
  219. */
  220. protected function response($data,$type='',$code=200) {
  221. $this->sendHttpStatus($code);
  222. exit($this->encodeData($data,strtolower($type)));
  223. }
  224. }