归纳能力不好,不知怎么写一个简洁的标题。。。

平台:Java, Jersey, Jackson

当restful api接收的json数据格式不对的时候,通常我们会返回异常信息,这些信息很有利于定位究竟是哪段json数据出了错。
我们也可以修改返回的信息。由于这些错误信息是框架给出的,所以可以通过重载相应的exception来定制返回的信息。
这么做有什么意义?要求比较严格的情况下,会不想让客户看到具体的信息,比如用到的包的信息(下面的错误信息中就包括包信息);这些信息会被利用,进而分析是用什么实现的,进而利用这种实现潜在的漏洞进行攻击。

这么复杂的东西,对我来说不知道怎么实现,还好,可以参考这里:
http://javareferencegv.blogspot.com/2015/07/handle-invalid-json-request-by.html

我呢,用了一下,没问题;把整个方案记录一下:

假定一个restful pai,对应的输入参数的POJO是:

  1. class mydata{
  2. String name;
  3. String[] days;
  4. Map price;
  5. }

正常的输入应该是:
  1. {"name":"TOM","days":["20161223"],"things":{"apple":"11","orange:"12"}}

但是如果给的json数据是非法的,那么会抛出如下错误:

case 1:
输入:
  1. {"name":"TOM","days":["20161223"],abcdefg,"things":{"apple":"11","orange:"12"}}
得到返回的错误信息:
  1. Unexpected character ('a' (code 97)): was expecting double-quote to start field name at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@7483; line: 1, column: 35]
case 2:
输入:
  1. {"name":"TOM","days":"what","things":{"apple":"11","orange:"12"}}
得到返回的错误信息:
  1. Can not deserialize instance of java.lang.String[] out of VALUE_STRING token at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@983232; line: 1, column: 22] (through reference chain: xxx.yyy.mydata["days"])
case 3:
输入:
  1. {"name":"TOM","days":["20161223"],"things":{"apple"}}
得到返回的错误信息:
  1. Unexpected character ('}' (code 125)): was expecting a colon to separate field name and value at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@7632323; line: 1, column: 51] at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@2432432; line: 1, column: 55] (through reference chain: xxx.yyy.mydata["price"])

如果说不想给出这样详细的信息,而只是想提示一下有问题,那么可以通过重载相应的exception来做到。具体修改如下:

"web.xml"

  1. <servlet>
  2.         <servlet-name>myServlet</servlet-name>
  3.         <servlet-class>
  4.           org.glassfish.jersey.servlet.ServletContainer
  5.         </servlet-class>
  6.         <init-param>
  7.           <param-name>javax.ws.rs.Application</param-name>
  8.           <param-value>xxx.yyy.register</param-value>
  9.         </init-param>
  10.         <init-param>
  11.             <param-name>
  12.                 com.sun.jersey.api.json.POJOMappingFeature
  13.             </param-name>
  14.             <param-value>true</param-value>
  15.         </init-param>
  16.         <load-on-startup>1</load-on-startup>
  17.     </servlet>



"xxx.yyy.register.java"

  1. package xxx.yyy;
  2. import java.io.File;
  3. import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
  4. import org.glassfish.jersey.server.ResourceConfig;
  5. import com.fasterxml.jackson.annotation.JsonInclude;
  6. import com.fasterxml.jackson.annotation.JsonInclude.Include;
  7. import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
  8. import javax.ws.rs.ext.MessageBodyReader;
  9. import javax.ws.rs.ext.MessageBodyWriter;
  10. public class register extends ResourceConfig {
  11. public register() {
  12. packages("xxx.yyy");
  13. register(YourJsonProvider.class);
  14. register(MyJsonParseExceptionMapper.class);
  15. register(MyJsonMappingExceptionMapper.class);
  16. }
  17. }

"MyJsonParseExceptionMapper.java"
  1. package xxx.yyy;
  2. import com.fasterxml.jackson.core.JsonParseException;
  3. import javax.ws.rs.core.MediaType;
  4. import javax.ws.rs.core.Response;
  5. import javax.ws.rs.ext.ExceptionMapper;
  6. import javax.ws.rs.ext.Provider;
  7. @Provider
  8. public class MyJsonParseExceptionMapper implements ExceptionMapper{
  9. @Override
  10. public Response toResponse(JsonParseException exception)
  11. {
  12. String info = "{\"Error\":\"It is an error, invalid JSON format.\"}";
  13. return Response
  14. .status(Response.Status.BAD_REQUEST)
  15. .entity(info)
  16. .type( MediaType.APPLICATION_JSON)
  17. .build();
  18. }
  19. }

"MyJsonMappingExceptionMapper.java"

  1. package xxx.yyy;
  2. import com.fasterxml.jackson.databind.JsonMappingException;
  3. import javax.ws.rs.core.MediaType;
  4. import javax.ws.rs.core.Response;
  5. import javax.ws.rs.ext.ExceptionMapper;
  6. import javax.ws.rs.ext.Provider;
  7. @Provider
  8. public class MyJsonMappingExceptionMapper implements ExceptionMapper{
  9. @Override
  10. public Response toResponse(JsonMappingException exception)
  11. {
  12. String info = "{\"Error\":\"JSON format is OK, but some part's type is wrong.\"}";
  13. return Response
  14. .status(Response.Status.BAD_REQUEST)
  15. .entity(info)
  16. .type( MediaType.TEXT_PLAIN)
  17. .build();
  18. }
  19. }

此时,再执行前面那几个错误的输入,可以得到如下输出:

case 1:
输入:
  1. {"name":"TOM","days":["20161223"],abcdefg,"things":{"apple":"11","orange:"12"}}
输出:
  1. {"Error":"It is an error, invalid JSON format."}

case 2:
输入:
  1. {"name":"TOM","days":"what","things":{"apple":"11","orange:"12"}}
输出:
  1. {"Error":"It is an error, invalid JSON format."}

case 3:
输入:
  1. {"name":"TOM","days":["20161223"],"things":{"apple"}}
输出:
  1. {"Error":"It is an error, invalid JSON format."}

用到的一些jar (其实不需要这么多):

jackson-annotations-2.8.4.jar 
jackson-databind-2.8.4.jar   
jackson-jaxrs-json-provider-2.8.4.jar     
jackson-xc-1.9.2.jar
jackson-core-2.8.4.jar         
jackson-jaxrs-1.9.2.jar       
jackson-mapper-asl-1.9.2.jar
jackson-core-asl-1.9.2.jar     
jackson-jaxrs-base-2.8.4.jar 
jackson-module-jaxb-annotations-2.8.4.jar

javax.ws.rs-api-2.0.1.jar

jersey-client-2.25.jar             
jersey-container-servlet-core-2.25.jar 
jersey-guava-2.25.jar       
jersey-media-json-jackson-2.25.jar
jersey-common-2.25.jar             
jersey-core-1.19.3.jar                 
jersey-json-1.19.3.jar     
jersey-server-2.25.jar
jersey-container-servlet-2.25.jar 
jersey-entity-filtering-2.25.jar       
jersey-media-jaxb-2.25.jar

jsr305-3.0.0.jar











08-30 21:57