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

使用Jersey时预检请求后出现请求解析异常的问题求助

解决跨域调用时的JSON解析错误问题

我帮你分析下这个问题:你用Postman调用REST接口一切正常,但用跨域客户端(比如resttesttest.com)调用时就触发了JSON解析错误,错误提示里的Unexpected char 117是关键线索,这基本和CORS预检请求的处理不完整有关。

先把你提供的代码整理出来,方便排查:

1. 现有CORS过滤器代码

@Provider 
public class CORSFilter implements ContainerResponseFilter { 
    @Override 
    public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException { 
        // TODO: Allow only from *our* Web front 
        response.getHeaders().add("Access-Control-Allow-Origin", "*"); 
        response.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept,x-request-id, x-api-key, x-api-secret, authorization"); 
        response.getHeaders().add("Access-Control-Allow-Credentials", "true"); 
        response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); 
    } 
}

2. REST资源接口代码

@Path("MyResource") 
@POST 
@Produces(MediaType.APPLICATION_JSON) 
@Consumes(MediaType.APPLICATION_JSON) 
public String MyResource(TestInput input) { 
    return input.getName(); 
}

3. TestInput实体类代码

public class TestInput { 
    private String name; 
    public String getName() { return name; } 
    public void setName(String name) { this.name = name; } 
}

你遇到的错误信息

javax.servlet.ServletException: javax.json.stream.JsonParsingException: Unexpected char 117 at (line no=1, column no=1, offset=0) org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:432) org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

根本原因
javax.json.stream.JsonParsingException: Unexpected char 117 at (line no=1, column no=1, offset=0) org.glassfish.json.JsonTokenizer.unexpectedChar(JsonTokenizer.java:601) org.glassfish.json.JsonTokenizer.nextToken(JsonTokenizer.java:418) org.glassfish.json.JsonParserImpl$NoneContext.getNextEvent(JsonParserImpl.java:413) org.glassfish.json.JsonParserImpl.next(JsonParserImpl.java:363) org.eclipse.yasson.internal.JsonbRiParser.next(JsonbRiParser.java:140) org.eclipse.yasson.internal.Unmarshaller.getRootEvent(Unmarshaller.java:66) org.eclipse.yasson.internal.Unmarshaller.deserializeItem(Unmarshaller.java:56) org.eclipse.yasson.internal.Unmarshaller.deserialize(Unmarshaller.java:50) org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:45) org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:85) org.glassfish.jersey.jsonb.internal.JsonBindingProvider.readFrom(JsonBindingProvider.java:99) org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:257) org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:236) org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:156) org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundReadFrom(MappableExceptionWrapperInterceptor.java:73) org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:156) org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1091) org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:874) org.glassfish.jersey.server.ContainerRequest.readEntity(ContainerRequest.java:271) org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:97) org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:80) org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource.apply(ParamValueFactoryWithSource.java:74) org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:92) org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:133) org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:200) org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:103) org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:493) org.glassfish.jersey.server.model.Res

问题根源

跨域客户端在发送POST请求前,会自动发起OPTIONS预检请求——这是浏览器同源策略的要求,用来确认服务器是否允许跨域操作。你的过滤器虽然添加了CORS响应头,但没有专门处理OPTIONS请求:Jersey会默认把OPTIONS请求路由到你的POST资源方法,尝试解析请求体(但OPTIONS请求本身没有符合JSON格式的请求体),所以就触发了JSON解析错误(错误里的char 117是小写字母u,大概率是OPTIONS响应的某个内容被误当成POST请求体解析了)。

修复方案

修改你的CORS过滤器,让它同时实现ContainerRequestFilter,专门拦截OPTIONS请求,直接返回200状态码,不让请求到达资源方法:

@Provider 
public class CORSFilter implements ContainerResponseFilter, ContainerRequestFilter { 
    @Override 
    public void filter(ContainerRequestContext requestContext) throws IOException { 
        // 拦截OPTIONS预检请求,直接返回成功响应
        if ("OPTIONS".equalsIgnoreCase(requestContext.getRequest().getMethod())) {
            requestContext.abortWith(Response.ok().build());
        }
    }

    @Override 
    public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException { 
        // TODO: 把*替换成你的实际前端域名,比如https://your-frontend.com,更安全
        response.getHeaders().add("Access-Control-Allow-Origin", "*"); 
        response.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept,x-request-id, x-api-key, x-api-secret, authorization"); 
        response.getHeaders().add("Access-Control-Allow-Credentials", "true"); 
        response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); 
    } 
}

另外给你两个小建议:

  • 按照代码里的TODO注释,把Access-Control-Allow-Origin从*改成你的前端实际域名,这样能避免恶意网站跨域调用你的接口,提升安全性
  • 既然Postman能正常工作,说明你的JSON-B(Yasson)依赖配置是没问题的,这部分不用改动

验证方法

修改过滤器后,用resttesttest.com重新测试:

  • 发送POST请求,携带JSON请求体{"name":"test"}
  • 此时OPTIONS预检请求会被过滤器直接处理,返回200,之后的POST请求就能正常解析JSON并返回结果了

内容的提问来源于stack exchange,提问作者Vrushali Sabnis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:45:15