このJsonp Filterを実装しようとしています。
ただし、URL を呼び出すたびに/server/api.jsonp?callback=something
、応答の最後の部分が切り取られます (ContentLength には元の応答出力の長さが含まれます。この場合{test: 'blaat'}
、元の応答出力であるため、15 になります。コントローラーを参照してください)。
少なくともこれは、私が得る出力がtest({test: 'bl
. フィルターを機能させて正しい応答を返す方法を知っている人はいますか? (私は Content-length を強制しようとさえします - JsonpCallbackFilter を見てください)
フィルター:
public class JsonpCallbackFilter implements Filter {
protected final Log logger = LogFactory.getLog(JsonpCallbackFilter.class);
@Override
public void init(FilterConfig fConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
Map<String, String[]> params = httpRequest.getParameterMap();
if (params.containsKey("callback")) {
if (logger.isDebugEnabled()) {
logger.debug("Wrapping response with JSONP callback: " + params.get("callback")[0]);
}
OutputStream out = httpResponse.getOutputStream();
GenericResponseWrapper wrapper = new GenericResponseWrapper(httpResponse);
chain.doFilter(request, wrapper);
byte[] callBack = (params.get("callback")[0] + "(").getBytes();
byte[] callBackEnd = (");").getBytes();
byte[] jsonpResponse = new byte[callBack.length + wrapper.getData().length + callBackEnd.length];
System.arraycopy(callBack, 0, jsonpResponse, 0, callBack.length);
System.arraycopy(wrapper.getData(), 0, jsonpResponse, callBack.length, wrapper.getData().length);
System.arraycopy(callBackEnd, 0, jsonpResponse, callBack.length + wrapper.getData().length, callBackEnd.length);
logger.debug(new String(jsonpResponse));
logger.debug("Length: " + jsonpResponse.length);
out.write(jsonpResponse);
wrapper.setContentType("text/javascript;charset=UTF-8");
wrapper.setContentLength(jsonpResponse.length);
} else {
if (logger.isDebugEnabled()) {
logger.debug("No callback found, resort to default json request!");
}
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
}
ジェネリック レスポンス ラッパー:
public class GenericResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream output;
private FilterServletOutputStream filterStream;
private PrintWriter printWriter;
private int contentLength;
private String contentType;
public GenericResponseWrapper(HttpServletResponse response) {
super(response);
output = new ByteArrayOutputStream();
filterStream = new FilterServletOutputStream(output);
printWriter = new PrintWriter(output, true);
}
public byte[] getData() {
printWriter.close();
return output.toByteArray();
}
@Override
public ServletOutputStream getOutputStream() {
return filterStream;
}
@Override
public PrintWriter getWriter() {
return printWriter;
}
public int getContentLength() {
return contentLength;
}
@Override
public void setContentLength(int length) {
this.contentLength = length;
super.setContentLength(length);
}
@Override
public String getContentType() {
return contentType;
}
@Override
public void setContentType(String contentType) {
this.contentType = contentType;
super.setContentType(contentType);
}
}
カスタム フィルター出力ストリーム:
public class FilterServletOutputStream extends ServletOutputStream {
private DataOutputStream stream;
public FilterServletOutputStream(OutputStream output) {
stream = new DataOutputStream(output);
}
@Override
public void write(int b) throws IOException {
stream.write(b);
}
@Override
public void write(byte[] b) throws IOException {
stream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
stream.write(b, off, len);
}
}
web.xml フィルター マッピング:
<filter>
<filter-name>jsonpCallbackFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>jsonpCallbackFilter</filter-name>
<url-pattern>*.jsonp</url-pattern>
</filter-mapping>
applicationContext.xml の Bean マッピング
<bean id="jsonpCallbackFilter" class="com.world2.utils.spring.filters.jsonp.JsonpCallbackFilter" />
コントローラ:
@Controller
public class ApiRequests {
protected final Logger logger = Logger.getLogger(ApiRequests.class);
@RequestMapping(value = "api.json", produces = {"application/json"}, method = RequestMethod.GET)
@ResponseBody
public String handleActionJson(final HttpServletRequest request){
return "{test: 'blaat'}";
}
@RequestMapping(value = "api.jsonp", produces = {"text/javascript", "application/javascript", "application/json"}, method = RequestMethod.GET)
@ResponseBody
public String handleActionJsonp(final HttpServletRequest request){
return "{test: 'blaat'}";
}
}
呼び出したい URL:
http://host:8084/MyProject/server/api.jsonp?callback=test&_=1364980436087
デバッグ出力:
11:13:57,776 DEBUG JsonpCallbackFilter:57 - test({test: 'blaat'});
11:13:57,776 DEBUG JsonpCallbackFilter:58 - Length: 22
結果:
test({test: 'bl
期待される結果の代わりに:
test({test: 'blaat'});