GAE/J changes Content-Type from JSON to HTML when

2019-04-28 16:56发布

I wrote a Web API in Java (JAX-RS by Jersey) which returns "403 Forbidden" with JSON.

HTTP/1.1 403 Forbidden
Content-Type: application/json; charset=UTF-8
...

{"resultCode":"..."}

It works on the local GAE dev server as expected. However, on the real GAE, the content type is changed from JSON to HTML.

HTTP/1.1. 403 Forbidden
Content-Type: text/html; charset=utf-8
...

<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>403 Forbidden</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Forbidden</h1>
</body></html>

How can I prevent GAE from changing the content type and the entity body?


Additional Information

My endpoint does not throw any exception. It returns a Response instance. The code snippet below is a test endpoint. On the local GAE dev server, this endpoint returns JSON. On the real GAE, it returns HTML. Too much of a good thing.

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

@Path("/test")
public class TestEndpoint
{
    @GET
    public Response get()
    {
        return Response
                .status(Status.BAD_REQUEST)
                .type(MediaType.APPLICATION_JSON_TYPE)
                .entity("{\"id\":1}")
                .build();
    }
}


Additional Information 2

I wrote a simpler example code like below. This code returns JSON even on the real GAE! What's the difference?

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestServlet extends HttpServlet
{
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        response.setStatus(400);
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.write("{\"hello\":\"world\"}");
    }
}

3条回答
我命由我不由天
2楼-- · 2019-04-28 17:20

You'll have to define your own exception mapper and payload extensions in your service context file:

<jaxrs:server id="my-resources" address="/some-context-path">
    <jaxrs:serviceBeans>
        <ref bean="my-resource-1" />
        <ref bean="my-resource-2" />
    </jaxrs:serviceBeans>
    <jaxrs:extensionMappings>
        <entry key="json" value="application/json"/>
        <entry key="xml" value="application/xml"/>
    </jaxrs:extensionMappings>
    <jaxrs:providers>
        <ref bean="jaxbProvider" />
        <ref bean="my-custom-exception-mapper" />
    </jaxrs:providers>
    <jaxrs:features>
        <cxf:logging/>
    </jaxrs:features>
</jaxrs:server>

my-custom-exception-mapper implements ExceptionMapper, ResponseExceptionMapper. Something like this is a good start: http://www.luckyryan.com/2013/06/15/apache-cxf-exception-handler-for-jaxrs-rest/

查看更多
Ridiculous、
3楼-- · 2019-04-28 17:20

Mentioned solution without web.xml may look like:

new ResourceConfig()
       .property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);
查看更多
不美不萌又怎样
4楼-- · 2019-04-28 17:39

I read Jersey's source code and intuited this answer.

Setting "jersey.config.server.response.setStatusOverSendError" (one of Jersey's server configuration properties) to true solved the issue.

The following is an excerpt from my new web.xml.

<servlet>
  <servlet-name>API</servlet-name>
  <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>jersey.config.server.provider.classnames</param-name>
    <param-value>
      ......
    </param-value>
  </init-param>
  <init-param>
    <param-name>jersey.config.server.response.setStatusOverSendError</param-name>
    <param-value>true</param-value>
  </init-param>
</servlet>
查看更多
登录 后发表回答