You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迁移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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:42:01