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

使用@Produces(MediaType.APPLICATION_JSON)时异常返回字符串报JSON解析错误的解决方法

问题根源解析

首先咱们得搞清楚为什么会触发JSON解析错误:你的接口用@Produces(MediaType.APPLICATION_JSON)声明了所有响应都应该是JSON格式,但异常发生时你直接返回了纯字符串(比如"That id is not correct")。JSON解析器会尝试把这个字符串当作JSON解析,可纯字符串(没被双引号正确包裹,或者客户端预期是JSON对象)不符合JSON规范,自然就报错了。

另外你的代码还有个小bug:你先执行了lista.stream().filter(x -> x.getId()==id).findAny().get(),之后才判断id > lista.size()-1。如果id超出范围,findAny().get()会先抛出NoSuchElementException,根本走不到你自己抛异常的步骤,返回的错误信息也不是你预期的内容,这个顺序得先修正。


解决方案

下面提供几种靠谱的解决方式,你可以根据需求选择:

方案1:统一返回JSON格式的错误响应(推荐)

这种方式最规范,无论成功还是失败都返回JSON,完全符合@Produces的声明,不会有解析问题。

首先创建一个错误响应的实体类:

public class ErrorResponse {
    private String message;
    private int statusCode;

    public ErrorResponse(String message, int statusCode) {
        this.message = message;
        this.statusCode = statusCode;
    }

    // JAX-RS需要getter来序列化JSON
    public String getMessage() {
        return message;
    }

    public int getStatusCode() {
        return statusCode;
    }
}

然后修正接口代码(同时调整判断顺序):

@GET 
@Path("/paises/{id}") 
@Produces(MediaType.APPLICATION_JSON) 
public Response findCountryList(@PathParam("id") int id){ 
    try{
        ArrayList<Country> lista = new ArrayList<>(); 
        for(int i=0; i<10 ;i++){ 
            Country c = new Country(); 
            c.setId(i); 
            c.setName("country"+i); 
            c.setCode("countryIsoCode"+i); 
            c.setRegion("region"+i); 
            lista.add(c); 
        } 

        // 先判断id是否合法,避免触发NoSuchElementException
        if(id < 0 || id > lista.size()-1) {
            throw new IllegalArgumentException("That id is not correct");
        }

        Country co = lista.stream().filter(x -> x.getId()==id).findAny().get(); 
        return Response.ok(co).build(); 
    } catch(IllegalArgumentException e){ 
        ErrorResponse error = new ErrorResponse(e.getMessage(), 404);
        return Response.status(404).entity(error).build(); 
    } catch(Exception e){ 
        ErrorResponse error = new ErrorResponse("Unexpected error occurred", 500);
        return Response.status(500).entity(error).build(); 
    } 
}

异常时返回的是标准JSON对象,比如:

{"message":"That id is not correct","statusCode":404}

客户端可以正常解析,不会报错。

方案2:动态设置响应的媒体类型

如果不想创建错误实体类,可以在异常时把响应类型改成纯文本,这样客户端就会用文本方式解析,而非JSON。

修正后的代码:

@GET 
@Path("/paises/{id}") 
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) // 声明支持两种响应类型
public Response findCountryList(@PathParam("id") int id){ 
    try{
        ArrayList<Country> lista = new ArrayList<>(); 
        for(int i=0; i<10 ;i++){ 
            Country c = new Country(); 
            c.setId(i); 
            c.setName("country"+i); 
            c.setCode("countryIsoCode"+i); 
            c.setRegion("region"+i); 
            lista.add(c); 
        } 

        if(id < 0 || id > lista.size()-1) {
            throw new IllegalArgumentException("That id is not correct");
        }

        Country co = lista.stream().filter(x -> x.getId()==id).findAny().get(); 
        return Response.ok(co).build(); // 成功时默认返回JSON
    } catch(IllegalArgumentException e){ 
        return Response.status(404)
                       .type(MediaType.TEXT_PLAIN) // 明确设置为纯文本
                       .entity(e.getMessage())
                       .build(); 
    } catch(Exception e){ 
        return Response.status(500)
                       .type(MediaType.TEXT_PLAIN)
                       .entity("Unexpected error occurred")
                       .build(); 
    } 
}

注意这里要把@Produces改成支持两种媒体类型,否则JAX-RS可能会拒绝返回TEXT_PLAIN类型的响应。

方案3:使用ExceptionMapper全局处理异常

如果项目中有多个接口需要处理类似异常,可以用JAX-RS的ExceptionMapper全局统一处理,不用每个接口都写catch块。

首先创建一个异常映射器:

@Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
    @Override
    public Response toResponse(IllegalArgumentException exception) {
        ErrorResponse error = new ErrorResponse(exception.getMessage(), 404);
        return Response.status(404)
                       .type(MediaType.APPLICATION_JSON)
                       .entity(error)
                       .build();
    }
}

然后接口代码可以大幅简化:

@GET 
@Path("/paises/{id}") 
@Produces(MediaType.APPLICATION_JSON) 
public Response findCountryList(@PathParam("id") int id){ 
    ArrayList<Country> lista = new ArrayList<>(); 
    for(int i=0; i<10 ;i++){ 
        Country c = new Country(); 
        c.setId(i); 
        c.setName("country"+i); 
        c.setCode("countryIsoCode"+i); 
        c.setRegion("region"+i); 
        lista.add(c); 
    } 

    if(id < 0 || id > lista.size()-1) {
        throw new IllegalArgumentException("That id is not correct");
    }

    Country co = lista.stream().filter(x -> x.getId()==id).findAny().get(); 
    return Response.ok(co).build(); 
}

当抛出IllegalArgumentException时,JAX-RS会自动调用IllegalArgumentExceptionMapper生成响应,非常优雅。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:56:21