迁移Google App Engine Framework 2.0后响应内容类型异常修复咨询
解决GAE Endpoints v2 JSONP MIME类型错误问题
这个问题我之前也碰到过,核心原因是GAE Endpoints Framework 2.0默认关闭了JSONP自动处理功能,而v1版本是默认开启的。当你用JSONP请求时,服务器需要返回包裹在callback函数中的JavaScript代码(MIME类型text/javascript),但v2现在直接返回纯JSON(application/json),Chrome的严格MIME类型检查就会拦截执行。
下面是几个可行的修复方案,按推荐优先级排序:
方案1:启用Endpoints的JSONP支持(最简单)
检查你的web.xml配置,给EndpointsServlet添加一个enableJsonp的初始化参数,强制开启JSONP处理:
<servlet> <servlet-name>EndpointsServlet</servlet-name> <servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class> <init-param> <param-name>services</param-name> <param-value>com.fusepoint.services</param-value> </init-param> <init-param> <param-name>restricted</param-name> <param-value>false</param-value> </init-param> <!-- 新增这个参数开启JSONP --> <init-param> <param-name>enableJsonp</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
添加后重启服务,Endpoints会自动识别请求中的callback参数,将JSON包裹成函数调用并返回text/javascript类型,Chrome的错误就会消失。
方案2:替换JSONP为CORS(更现代、安全)
JSONP是为了跨域而生的老方案,现在更推荐用CORS(跨域资源共享)替代,这样前端代码更简洁,也避免JSONP的安全风险。
步骤1:配置Endpoints的CORS支持
在web.xml的EndpointsServlet中添加CORS相关参数:
<servlet> <servlet-name>EndpointsServlet</servlet-name> <servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class> <init-param> <param-name>services</param-name> <param-value>com.fusepoint.services</param-value> </init-param> <init-param> <param-name>restricted</param-name> <param-value>false</param-value> </init-param> <!-- CORS配置 --> <init-param> <param-name>cors.allowed.origins</param-name> <param-value>https://your-frontend-domain.com</param-value> <!-- 替换成你的前端域名,或者用*允许所有(生产环境不推荐) --> </init-param> <init-param> <param-name>cors.allowed.methods</param-name> <param-value>GET,POST,PUT,DELETE,OPTIONS</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
步骤2:修改前端Ajax代码
把dataType: 'jsonp'改成dataType: 'json',jQuery会自动处理跨域请求:
return $.ajax({ method: method, url: url, data: data, dataType: 'json', // 替换jsonp为json failQuite: failQuite, async: async, success: function (data) { if (data != null) { // 你的业务逻辑 } } });
这个方案是长期最优解,建议优先考虑。
方案3:自定义Servlet处理JSONP(兜底方案)
如果上面两个方案都不适用,可以自定义一个继承EndpointsServlet的类,手动处理callback参数:
步骤1:创建自定义Servlet类
package com.fusepoint.services; import com.google.api.server.spi.EndpointsServlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; public class CustomEndpointsServlet extends EndpointsServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String callback = req.getParameter("callback"); if (callback != null && !callback.trim().isEmpty()) { // 先获取原始JSON响应 ByteArrayOutputStream baos = new ByteArrayOutputStream(); HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(resp) { private final ByteArrayServletOutputStream outputStream = new ByteArrayServletOutputStream(baos); private final PrintWriter writer = new PrintWriter(outputStream); @Override public javax.servlet.ServletOutputStream getOutputStream() throws IOException { return outputStream; } @Override public PrintWriter getWriter() throws IOException { return writer; } }; // 让父类处理请求生成JSON super.doGet(req, wrapper); wrapper.getWriter().flush(); // 用callback包裹JSON并设置正确的MIME类型 String jsonResponse = baos.toString(); resp.setContentType("text/javascript"); resp.getWriter().write(callback + "(" + jsonResponse + ");"); } else { // 没有callback参数,按正常JSON返回 super.doGet(req, resp); } } private static class ByteArrayServletOutputStream extends javax.servlet.ServletOutputStream { private final ByteArrayOutputStream baos; public ByteArrayServletOutputStream(ByteArrayOutputStream baos) { this.baos = baos; } @Override public void write(int b) throws IOException { baos.write(b); } @Override public boolean isReady() { return true; } @Override public void setWriteListener(javax.servlet.WriteListener writeListener) { // 无需实现同步处理的WriteListener } } }
步骤2:修改web.xml配置
把原来的servlet-class替换成自定义类:
<servlet> <servlet-name>EndpointsServlet</servlet-name> <servlet-class>com.fusepoint.services.CustomEndpointsServlet</servlet-class> <!-- 其他参数保持不变 --> </servlet>
这个方案适合需要兼容旧代码又无法启用官方JSONP支持的场景。
内容的提问来源于stack exchange,提问作者stanchu
相关产品推荐
相关产品推荐

