RestController.class.php 8.4 KB

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