决定js框架
在写完 一个后端的前端学习之旅——1.决定学什么 后我决定用coffeescript来看一些js框架,本来想用react,结果发现用它跟gulp配起来略烦,选来选去选择了小半天决定最终用 spinejs, 看起来比较小,而且源码有coffee和js两个版本,还方便看,当然我知道这不是什么主流框架,可能文档什么的少一些,但这样更可以看出看源码的重要性,无论是不是python。
好的,开始学习
首先脚手架的项目就丢到一边,基于脚手架建了一个项目 leaning-frontend, 由于我把bower,npm的安装文件放到git中管理,项目比较大,github传输太慢了,先放着开源中国的git上。
然后bower装了下spine,npm装了下spine,我知道bower装的东西会被gulp编译到vendor.js里面,然而貌似我不用npm装spine的话在coffee里面会require报错(我暂时没有管require到底干毛的,看起来是import),bower装的spine有个问题,因为gulp中采用了一个main-bower-file还是什么东西的找包的主文件,然后gulp讲这个文件粘贴到vendor.js里面,但是看起来spine是模块分离的,需要单独引用(或者是其他什么原因),它并没有bower.json。所以我修改了下gulp中的tasks/bower.coffee将所有的外部库丢到一个文件夹里面这样可以直接引用(有没有更好的方案之后再说)。
第一步目录结构
spine是mvc的,然而都是js,所以我在source下面建了controllers、models、views这三个文件夹,views里面采用eco这么种东西,看起来很像djang的模板。
文档
class Contact extends Spine.Model
@configure "Contact", "name"
@extend Spine.Model.Ajax
@url: "/users
class App extends Spine.Controller
constructor: ->
super
# Instantiate other controllers..
Photo.fetch()
根据这个文档我知道了继承Model后@extend一个Spine.Model.Ajax
然后添加一个url就可以调用Model.fetch()了,so我在main.coffee里面试了一下然后就出了些事情,容我慢慢道来。
我准备用学堂在线的某些api做一些事情: http://www.xuetangx.com/api/v2/courses,下面是我的Model。
class Course extends Spine.Model
@configure "Course", "name"
@extend Spine.Model.Ajax
@url: "/api/v2/courses"
module.exports = Course
fecth调用的时候首先404了,因为gulp在dev时启动的是一个localhost的本地服务器(改成0.0.0.0:3000了), spine在请求的时候url是相对路径所以拼上的,好的吧,那我写绝对路径 @url: http://www.xuetangx.com/api/v2/courses
, 然而他依然是拼接的(http://192.168.9.191:3000/http://www.xue...),我丢你老母。
然后继续看文档发现了 Spine.Model.host = "http://my-endpoint"
,强行把host设走,我就在main.coffee第一行把host设置了学堂在线的host,然后 就跨域了。
喜闻乐见的跨域问题
首先我们知道,跨域是浏览器的某种安全限制,服务器端发起request并不存在跨域问题(这也是你可以愉快的用脚本requests.get或者写爬虫的原因)。
解决跨域问题有很多方案:
如果外域本来就是你的,并且你觉得允许所有人访问的话(一般不用,出发你就是一个专门的api服务器),nginx配上三行代码即可(一搜就有)
同1,只是不是nginx上加,而是在代码里面做一些事情,例如django,用django-cors-headers
chrome可以关闭跨域 启动的时候加上一个参数即可
C:\Users\Administrator\AppData\Local\Google\Chrome\Application\chrome.exe --disable-web-security
写一个proxy,这样你就有了1 2两点的条件随便搞
我选择的是4+2
proxy server
根据上面的方案选择我在项目根目录用django建了一个simple_server
cd 你的项目位置/leaning-frontend/simple_server
pip install -r requirements.txt # 如果你懂python请用virtualenv什么的
./manage.py runserver 0.0.0.0:12345 # 12345是你想运行的端口号
讲解
urls.py, 只有一个url所有的请求都用CrossDomainAjaxView来处理
url(r'^(?P.*)$', CrossDomainAjaxView.as_view(), name='ajax-hanler')
views.py, 这个view是做这么一件事情的,将请求的url通过requests这个三方库在我的服务器端请求外域服务器,然后讲response转变成django的response来返回给前端,暂时只写了get(非get django要穿csrf什么的比较烦)。这里有件事情想讲一下:
请求的header,我本来是想把接受到的所有header丢给requests的然而获取不到数据,暂时不管;
response的header,我本来也是想把所有的response header都带回来的,然而并不行,python级别的东西报了一个http 1.1有关的异常is_hop_by_hop,所以我也暂时没带过来。
rest framework仅仅是我想用他的view支持下非get post请求(然而我并没做具体处理,实际上没有任何作用)
# -*- coding: utf-8 -*-
#from rest_framework.views import View
#from rest_framework.generics import GenericAPIView as View
from django.views.generic import View
from django.conf import settings
from simple_server.utils import get_request_header, get_request_headers
from urlparse import urljoin
from django.http.response import HttpResponse
from wsgiref.util import is_hop_by_hop
import requests
class CrossDomainAjaxView(View):
def transform_request_data(self, request, request_url):
cross_domain_host = get_request_header(request, 'cross_domain_host') or settings.CROSS_DOMAIN_HOST
url = urljoin(cross_domain_host, request_url)
headers = get_request_headers(request)
data = dict(request.GET)
data.update(dict(request.POST))
request_data = {
'cross_domain_host': cross_domain_host,
'url': url,
'data': data,
'headers': headers,
}
return request_data
def transform_response(self, requests_response):
response = HttpResponse(requests_response.content, content_type=
requests_response.headers.get('Content-Type', 'text/plain'))
return response
def get(self, request, request_url):
request_data = self.transform_request_data(request, request_url)
response = requests.get(request_data['url'], request_data['data'])
#headers=request_data['headers'])
print response.content
return self.transform_response(response)
settings.py
middleware加上这两个来允许跨域'corsheaders.middleware.CorsMiddleware'
、CORS_ORIGIN_ALLOW_ALL = True
, 然后由于前端传的是相对路径所以我加了一个host CROSS_DOMAIN_HOST = 'http://www.xuetangx.com'
, get的时候支持在header里面改变不同的host支持不同网站的api调用而不是写死一个。
well done
chrome的Network的XHR里面这次看到请求了,哇哈哈拦路虎解决。
oh对了,顺便fix了一些gulp对于spine需要支持的东西,主要是eco。
btw,发现学堂在线一个交互做的不错的页面 (好吧其实是产品强行让我贴的友链,各位随意)
2016-03-30更新
nginx可以添加location来直接通过反向代理跨域
location /xuetangx {
rewrite ^.+xuetangx/?(.*)$ /$1 break;
include uwsgi_params;
proxy_pass http://www.xuetangx.com/;
}
location /videoid2source/ {
#include uwsgi_params;
proxy_pass http://www.xuetangx.com/videoid2source/;
}