最近开发API接口时,用Ajax调用远程服务上API的接口时,出现以下错误 :
XMLHttpRequest cannot load http://192.168.1.101:8080/CDHAPI/bond/quote/minutely/1m/112188.SZ. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. 产生此种问题是由于Ajax跨域限制而引起的问题。Access-Control-Allow-Origin是HTML5中定义的一种服务器端返回Response header,用来解决资源(比如字体)的跨域权限问题。它定义了该资源允许被哪个域引用,或者被所有域引用。
根据这个思路,在服务端返回时在响应体的添加Header,设置Access-Control-Allow-Origin允许可访问的域。具体工作如下:
(1)写一个过滤器,在Reponse中Header中设置Access-Control-Allow-Origin:代码如下:
package com.sumscope.cdh.api.interceptor;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class CrossFilter implements Filter {
private static final boolean debug = true;
private FilterConfig filterConfig = null;
public CrossFilter() {
super();
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
if (filterConfig != null) {
if (debug) {
log("CrossFilter:Initializing filter");
}
}
}
@Override
public String toString() {
if (filterConfig == null) {
return ("CrossFilter()");
}
StringBuffer sb = new StringBuffer("CrossFilter(");
sb.append(filterConfig);
sb.append(")");
return (sb.toString());
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (debug) {
log("CrossFilter:doFilter()");
}
if(response instanceof HttpServletResponse){
HttpServletResponse alteredResponse = ((HttpServletResponse)response);
// I need to find a way to make sure this only gets called on 200-300 http responses
// TODO: see above comment
addHeadersFor200Response(alteredResponse);
}
doBeforeProcessing(request, response);
Throwable problem = null;
try {
chain.doFilter(request, response);
} catch (Throwable t) {
// If an exception is thrown somewhere down the filter chain,
// we still want to execute our after processing, and then
// rethrow the problem after that.
problem = t;
t.printStackTrace();
}
doAfterProcessing(request, response);
// If there was a problem, we want to rethrow it if it is
// a known type, otherwise log it.
if (problem != null) {
if (problem instanceof ServletException) {
throw (ServletException) problem;
}
if (problem instanceof IOException) {
throw (IOException) problem;
}
sendProcessingError(problem, response);
}
}
@Override
public void destroy() {
}
private void doBeforeProcessing(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (debug) {
log("CrossFilter:DoBeforeProcessing");
}
}
private void doAfterProcessing(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (debug) {
log("CrossFilter:DoAfterProcessing");
}
}
private void addHeadersFor200Response(HttpServletResponse response){
//TODO: externalize the Allow-Origin
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, HEAD");
response.addHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Origin, X-Requested-With, Content-Type, Accept");
response.addHeader("Access-Control-Max-Age", "1728000");
}
private void sendProcessingError(Throwable t, ServletResponse response) {
String stackTrace = getStackTrace(t);
if (stackTrace != null && !stackTrace.equals("")) {
try {
response.setContentType("text/html");
PrintStream ps = new PrintStream(response.getOutputStream());
PrintWriter pw = new PrintWriter(ps);
pw.print("<html>\n<head>\n<title>Error</title>\n</head>\n<body>\n"); //NOI18N
// PENDING! Localize this for next official release
pw.print("<h1>The resource did not process correctly</h1>\n<pre>\n");
pw.print(stackTrace);
pw.print("</pre></body>\n</html>"); //NOI18N
pw.close();
ps.close();
response.getOutputStream().close();
} catch (Exception ex) {
}
} else {
try {
PrintStream ps = new PrintStream(response.getOutputStream());
t.printStackTrace(ps);
ps.close();
response.getOutputStream().close();
} catch (Exception ex) {
}
}
}
public static String getStackTrace(Throwable t) {
String stackTrace = null;
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
pw.close();
sw.close();
stackTrace = sw.getBuffer().toString();
} catch (Exception ex) {
}
return stackTrace;
}
public void log(String msg) {
filterConfig.getServletContext().log(msg);
}
}
在Web.xml配置域名访问过滤器
<filter>
<filter-name>crossFilter</filter-name>
<filter-class>com.sumscope.cdh.api.interceptor.CrossFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>crossFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>ajax调用:
<html>
<meta charset="utf-8">
<script src="jquery.min.js"></script>
<script src="d3.min.js"></script>
<script src="techan.min.js"></script>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
text.symbol {
fill: #BBBBBB;
}
path {
fill: none;
stroke-width: 1;
}
path.candle {
stroke: #000000;
}
path.candle.body {
stroke-width: 0;
}
path.candle.up {
fill: #00AA00;
stroke: #00AA00;
}
path.candle.down {
fill: #FF0000;
stroke: #FF0000;
}
.close.annotation.up path {
fill: #00AA00;
}
path.volume {
fill: #DDDDDD;
}
.indicator-plot path.line {
fill: none;
stroke-width: 1;
}
.ma-0 path.line {
stroke: #1f77b4;
}
.ma-1 path.line {
stroke: #aec7e8;
}
.ma-2 path.line {
stroke: #ff7f0e;
}
button {
position: absolute;
right: 110px;
top: 25px;
}
path.macd {
stroke: #0000AA;
}
path.signal {
stroke: #FF9999;
}
path.zero {
stroke: #BBBBBB;
stroke-dasharray: 0;
stroke-opacity: 0.5;
}
path.difference {
fill: #BBBBBB;
opacity: 0.5;
}
path.rsi {
stroke: #000000;
}
path.overbought, path.oversold {
stroke: #FF9999;
stroke-dasharray: 5, 5;
}
path.middle, path.zero {
stroke: #BBBBBB;
stroke-dasharray: 5, 5;
}
.analysis path, .analysis circle {
stroke: blue;
stroke-width: 0.8;
}
.trendline circle {
stroke-width: 0;
display: none;
}
.mouseover .trendline path {
stroke-width: 1.2;
}
.mouseover .trendline circle {
stroke-width: 1;
display: inline;
}
.dragging .trendline path, .dragging .trendline circle {
stroke: darkblue;
}
.interaction path, .interaction circle {
pointer-events: all;
}
.interaction .body {
cursor: move;
}
.trendlines .interaction .start, .trendlines .interaction .end {
cursor: nwse-resize;
}
.supstance path {
stroke-dasharray: 2, 2;
}
.supstances .interaction path {
pointer-events: all;
cursor: ns-resize;
}
.mouseover .supstance path {
stroke-width: 1.5;
}
.dragging .supstance path {
stroke: darkblue;
}
.crosshair {
cursor: crosshair;
}
.crosshair path.wire {
stroke: #DDDDDD;
stroke-dasharray: 1, 1;
}
.crosshair .axisannotation path {
fill: #DDDDDD;
}
</style>
<body>
<div id="formQuote">
<input type="text" name="symbol" placeholder="Enter Symbol">
<input type="button" name="btnQuote" value="Get Quote" />
<select name="symbolList">
<option value=""></option>
<option value="110031.SH">110031.SH</option>
<option value="112188.SZ">112188.SZ</option>
</select>
</div>
</body>
<script>
var api_root = "http://<span style="font-family: Arial, Helvetica, sans-serif;">192.168.1.101</span>:8080/CDHAPI/bond";
var formQuote = $("#formQuote");
$.extend(formQuote, {
in_symbol: formQuote.find("input[name=symbol]"),
symbol_list: formQuote.find("select[name=symbolList]"),
selectSymbol: function() {
symbol = this.symbol_list.children("option:checked").val();
this.in_symbol.val(symbol);
},
submit: function() {
symbol = $.trim(this.in_symbol.val());
if (!symbol.length) {
alert("Please input a Symbol!");
return false;
}
var url = api_root + "/quote/minutely/1m/" + symbol;
console.log(url);
this.showQuoteChart(url);
},
showQuoteChart: function(url) {
var self = this;
//d3.csv("data.csv", function(error, data) {
d3.json(url, function(error, data) {
//d3.json("quote.json", function(error, data) {
var accessor = candlestick.accessor(),
indicatorPreRoll = 0; // Don't show where indicators don't have data
console.log(data);
data = data.map(function(d) {
return {
/*
date: parseDate(d.Date),
open: +d.Open,
high: +d.High,
low: +d.Low,
close: +d.Close,
volume: +d.Volume
*/
date: new Date(d.durationTime),
open: +d.openCleanPrice,
high: +d.highCleanPrice,
low: +d.lowCleanPrice,
close: +d.closeCleanPrice,
volume: +d.volume
};
}).sort(function(a, b) { return d3.ascending(accessor.d(a), accessor.d(b)); });
x.domain(techan.scale.plot.time(data).domain());
y.domain(techan.scale.plot.ohlc(data.slice(indicatorPreRoll)).domain());
yPercent.domain(techan.scale.plot.percent(y, accessor(data[indicatorPreRoll])).domain());
yVolume.domain(techan.scale.plot.volume(data).domain());
svg.select("g.candlestick").datum(data).call(candlestick);
svg.select("g.close.annotation").datum([data[data.length-1]]).call(closeAnnotation);
svg.select("g.volume").datum(data).call(volume);
svg.select("g.sma.ma-0").datum(techan.indicator.sma().period(10)(data)).call(sma0);
svg.select("g.sma.ma-1").datum(techan.indicator.sma().period(20)(data)).call(sma1);
svg.select("g.ema.ma-2").datum(techan.indicator.ema().period(50)(data)).call(ema2);
svg.select("g.crosshair.ohlc").call(ohlcCrosshair).call(zoom);
var zoomable = x.zoomable();
zoomable.domain([indicatorPreRoll, data.length]); // Zoom in a little to hide indicator preroll
self.drawChart();
// Associate the zoom with the scale after a domain has been applied
zoom.x(zoomable).y(y);
zoomPercent.y(yPercent);
});
},
resetChart: function() {
zoom.scale(1);
zoom.translate([0,0]);
this.drawChart();
},
drawChart: function() {
zoomPercent.translate(zoom.translate());
zoomPercent.scale(zoom.scale());
svg.select("g.x.axis").call(xAxis);
svg.select("g.ohlc .axis").call(yAxis);
svg.select("g.volume.axis").call(volumeAxis);
svg.select("g.percent.axis").call(percentAxis);
// We know the data does not change, a simple refresh that does not perform data joins will suffice.
svg.select("g.candlestick").call(candlestick.refresh);
svg.select("g.close.annotation").call(closeAnnotation.refresh);
svg.select("g.volume").call(volume.refresh);
svg.select("g .sma.ma-0").call(sma0.refresh);
svg.select("g .sma.ma-1").call(sma1.refresh);
svg.select("g .ema.ma-2").call(ema2.refresh);
svg.select("g.crosshair.ohlc").call(ohlcCrosshair.refresh);
},
initialize: function() {
var self = this;
this.find("input[name=btnQuote]").click(function(event) { self.submit(); });
this.symbol_list.on("change", function(event) { self.selectSymbol(); });
},
});
formQuote.initialize();
/* Techanjs */
var dim = {
width: 960, height: 360,
margin: { top: 20, right: 50, bottom: 30, left: 50 },
ohlc: { height: 305 },
};
dim.plot = {
width: dim.width - dim.margin.left - dim.margin.right,
height: dim.height - dim.margin.top - dim.margin.bottom
};
var parseDate = d3.time.format("%d-%b-%y").parse;
var zoom = d3.behavior.zoom()
.on("zoom", formQuote.drawChart);
var zoomPercent = d3.behavior.zoom();
var x = techan.scale.financetime()
.range([0, dim.plot.width]);
var y = d3.scale.linear()
.range([dim.ohlc.height, 0]);
var yPercent = y.copy(); // Same as y at this stage, will get a different domain later
var yVolume = d3.scale.linear()
.range([y(0), y(0.2)]);
var candlestick = techan.plot.candlestick()
.xScale(x)
.yScale(y);
var sma0 = techan.plot.sma()
.xScale(x)
.yScale(y);
var sma1 = techan.plot.sma()
.xScale(x)
.yScale(y);
var ema2 = techan.plot.ema()
.xScale(x)
.yScale(y);
var volume = techan.plot.volume()
.accessor(candlestick.accessor()) // Set the accessor to a ohlc accessor so we get highlighted bars
.xScale(x)
.yScale(yVolume);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var timeAnnotation = techan.plot.axisannotation()
.axis(xAxis)
.format(d3.time.format('%Y-%m-%d'))
.width(65)
.translate([0, dim.plot.height]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("right");
var ohlcAnnotation = techan.plot.axisannotation()
.axis(yAxis)
.format(d3.format(',.2fs'))
.translate([x(1), 0]);
var closeAnnotation = techan.plot.axisannotation()
.axis(yAxis)
.accessor(candlestick.accessor())
.format(d3.format(',.2fs'))
.translate([x(1), 0]);
var percentAxis = d3.svg.axis()
.scale(yPercent)
.orient("left")
.tickFormat(d3.format('+.1%'));
var percentAnnotation = techan.plot.axisannotation()
.axis(percentAxis);
var volumeAxis = d3.svg.axis()
.scale(yVolume)
.orient("right")
.ticks(3)
.tickFormat(d3.format(",.3s"));
var volumeAnnotation = techan.plot.axisannotation()
.axis(volumeAxis)
.width(35);
var ohlcCrosshair = techan.plot.crosshair()
.xScale(timeAnnotation.axis().scale())
.yScale(ohlcAnnotation.axis().scale())
.xAnnotation(timeAnnotation)
.yAnnotation([ohlcAnnotation, percentAnnotation, volumeAnnotation])
.verticalWireRange([0, dim.plot.height]);
var svg = d3.select("body").append("svg")
.attr("width", dim.width)
.attr("height", dim.height);
var defs = svg.append("defs");
defs.append("clipPath")
.attr("id", "ohlcClip")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", dim.plot.width)
.attr("height", dim.ohlc.height);
svg = svg.append("g")
.attr("transform", "translate(" + dim.margin.left + "," + dim.margin.top + ")");
svg.append('text')
.attr("class", "symbol")
.attr("x", 20)
.text("Facebook, Inc. (FB)");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + dim.plot.height + ")");
var ohlcSelection = svg.append("g")
.attr("class", "ohlc")
.attr("transform", "translate(0,0)");
ohlcSelection.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + x(1) + ",0)")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -12)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
ohlcSelection.append("g")
.attr("class", "close annotation up");
ohlcSelection.append("g")
.attr("class", "volume")
.attr("clip-path", "url(#ohlcClip)");
ohlcSelection.append("g")
.attr("class", "candlestick")
.attr("clip-path", "url(#ohlcClip)");
ohlcSelection.append("g")
.attr("class", "indicator sma ma-0")
.attr("clip-path", "url(#ohlcClip)");
ohlcSelection.append("g")
.attr("class", "indicator sma ma-1")
.attr("clip-path", "url(#ohlcClip)");
ohlcSelection.append("g")
.attr("class", "indicator ema ma-2")
.attr("clip-path", "url(#ohlcClip)");
ohlcSelection.append("g")
.attr("class", "percent axis");
ohlcSelection.append("g")
.attr("class", "volume axis");
// Add trendlines and other interactions last to be above zoom pane
svg.append('g')
.attr("class", "crosshair ohlc");
svg.append('g')
.attr("class", "crosshair macd");
svg.append('g')
.attr("class", "crosshair rsi");
</script>
</html>
在开发中遇到Ajax调用远程Restful API接口时遭遇跨域错误。通过在服务端设置响应Header,添加Access-Control-Allow-Origin,允许特定域进行访问。实现方式是在服务端创建一个过滤器,将该Header添加到响应体中。

5194

被折叠的 条评论
为什么被折叠?



