Java 如何在REST服务中验证传入的JSON数据?
rest服务需要根据json模式验证所有传入的json数据。json模式是公共可访问的,可以通过http请求检索 我使用jackson框架在java和json之间进行封送和解封。到目前为止,我找不到任何可能使用jackson根据模式验证数据 我还尝试了这个框架,它显然提供了这样一个验证功能。但不幸的是,我不可能让验证生效Java 如何在REST服务中验证传入的JSON数据?,java,json,validation,rest,jsonschema,Java,Json,Validation,Rest,Jsonschema,rest服务需要根据json模式验证所有传入的json数据。json模式是公共可访问的,可以通过http请求检索 我使用jackson框架在java和json之间进行封送和解封。到目前为止,我找不到任何可能使用jackson根据模式验证数据 我还尝试了这个框架,它显然提供了这样一个验证功能。但不幸的是,我不可能让验证生效 如何进行这种验证?我搜索了将传入json数据验证到RESTful服务中的最佳实践。我的建议是使用MessageBodyReader,它在readFrom方法中执行验证。下面是一
如何进行这种验证?我搜索了将传入json数据验证到RESTful服务中的最佳实践。我的建议是使用
MessageBodyReader
,它在readFrom
方法中执行验证。下面是一个消息体读取器示例,为了简单起见,它是非泛型的
我还对寻找进行json数据验证的最佳框架感兴趣。因为我使用jackson框架(版本1.8.5)在json和java之间进行封送和解封送,如果这个框架能够提供json数据验证功能,那就太好了。不幸的是,我找不到与杰克逊合作的任何可能性。最后,我使用了json模式验证程序,该验证程序位于。我使用的版本是2.1.7
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.ObjectMapper;
import at.fhj.ase.dao.data.Address;
import at.fhj.ase.xmlvalidation.msbreader.MessageBodyReaderValidationException;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jsonschema.exceptions.ProcessingException;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import com.github.fge.jsonschema.main.JsonValidator;
import com.github.fge.jsonschema.report.ProcessingReport;
@Provider
@Consumes(MediaType.APPLICATION_JSON)
public class AddressJsonValidationReader implements MessageBodyReader<Address> {
private final String jsonSchemaFileAsString;
public AddressJsonValidationReader(@Context ServletContext servletContext) {
this.jsonSchemaFileAsString = servletContext
.getRealPath("/json/Address.json");
}
@Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
if (type == Address.class) {
return true;
}
return false;
}
@Override
public Address readFrom(Class<Address> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
final String jsonData = getStringFromInputStream(entityStream);
System.out.println(jsonData);
InputStream isSchema = new FileInputStream(jsonSchemaFileAsString);
String jsonSchema = getStringFromInputStream(isSchema);
/*
* Perform JSON data validation against schema
*/
validateJsonData(jsonSchema, jsonData);
/*
* Convert stream to data entity
*/
ObjectMapper m = new ObjectMapper();
Address addr = m.readValue(stringToStream(jsonData), Address.class);
return addr;
}
/**
* Validate the given JSON data against the given JSON schema
*
* @param jsonSchema
* as String
* @param jsonData
* as String
* @throws MessageBodyReaderValidationException
* in case of an error during validation process
*/
private void validateJsonData(final String jsonSchema, final String jsonData)
throws MessageBodyReaderValidationException {
try {
final JsonNode d = JsonLoader.fromString(jsonData);
final JsonNode s = JsonLoader.fromString(jsonSchema);
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
JsonValidator v = factory.getValidator();
ProcessingReport report = v.validate(s, d);
System.out.println(report);
if (!report.toString().contains("success")) {
throw new MessageBodyReaderValidationException(
report.toString());
}
} catch (IOException e) {
throw new MessageBodyReaderValidationException(
"Failed to validate json data", e);
} catch (ProcessingException e) {
throw new MessageBodyReaderValidationException(
"Failed to validate json data", e);
}
}
/**
* Taken from <a href=
* "http://www.mkyong.com/java/how-to-convert-inputstream-to-string-in-java/"
* >www.mkyong.com</a>
*
* @param is
* {@link InputStream}
* @return Stream content as String
*/
private String getStringFromInputStream(InputStream is) {
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
String line;
try {
br = new BufferedReader(new InputStreamReader(is));
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
private InputStream stringToStream(final String str) throws UnsupportedEncodingException {
return new ByteArrayInputStream(str.getBytes("UTF-8"));
}
}
导入java.io.BufferedReader;
导入java.io.ByteArrayInputStream;
导入java.io.FileInputStream;
导入java.io.IOException;
导入java.io.InputStream;
导入java.io.InputStreamReader;
导入java.lang.annotation.annotation;
导入java.lang.reflect.Type;
导入javax.servlet.ServletContext;
导入javax.ws.rs.Consumes;
导入javax.ws.rs.WebApplicationException;
导入javax.ws.rs.core.Context;
导入javax.ws.rs.core.MediaType;
导入javax.ws.rs.core.MultivaluedMap;
导入javax.ws.rs.ext.MessageBodyReader;
导入javax.ws.rs.ext.Provider;
导入org.codehaus.jackson.map.ObjectMapper;
导入at.fhj.ase.dao.data.Address;
导入at.fhj.ase.xmlvalidation.msbreader.MessageBodyReaderValidationException;
导入com.fasterxml.jackson.databind.JsonNode;
导入com.github.fge.jackson.JsonLoader;
导入com.github.fge.jsonschema.exceptions.ProcessingException;
导入com.github.fge.jsonschema.main.JsonSchemaFactory;
导入com.github.fge.jsonschema.main.JsonValidator;
导入com.github.fge.jsonschema.report.ProcessingReport;
@提供者
@使用(MediaType.APPLICATION_JSON)
公共类AddressJsonValidationReader实现MessageBodyReader
*
*@param是
*{@link InputStream}
*@返回流内容作为字符串
*/
私有字符串getStringFromInputStream(InputStream为){
BufferedReader br=null;
StringBuilder sb=新的StringBuilder();
弦线;
试一试{
br=新的BufferedReader(新的InputStreamReader(is));
而((line=br.readLine())!=null){
某人附加(行);
}
}捕获(IOE异常){
e、 printStackTrace();
}最后{
如果(br!=null){
试一试{
br.close();
}捕获(IOE异常){
e、 printStackTrace();
}
}
}
使某人返回字符串();
}
私有InputStream stringToStream(最终字符串str)引发UnsupportedEncodingException{
返回新的ByteArrayInputStream(str.getBytes(“UTF-8”);
}
}
看起来您并没有被JSONSchema束缚住,尽管这似乎是您的默认选择。口味各不相同,但通常情况下,它看起来比可能的更复杂。此外,就个人而言,我希望数据和验证规则都在同一个地方。当在java代码中使用自定义验证器而不是在任何类型的配置文件中使用时,可以说自定义验证器似乎更适合
下面是这种方法的样子。比方说,您有以下json对象,表示某种支付(无论是请求还是响应),但为了简洁起见,它只包含折扣
块:
{
"discount":{
"valid_until":"2032-05-04 00:00:00+07",
"promo_code":"VASYA1988"
}
}
以下是验证代码的外观:
/*1 */ public class ValidatedJsonObjectRepresentingRequestOrResponse implements Validatable<JsonObjectRepresentingRequestOrResponse>
{
private String jsonString;
private Connection dbConnection;
/*6 */ public ValidatedJsonObjectRepresentingRequestOrResponse(String jsonString, Connection dbConnection)
{
this.jsonString = jsonString;
this.dbConnection = dbConnection;
}
@Override
/*13*/ public Result<JsonObjectRepresentingRequestOrResponse> result() throws Exception
{
return
/*16*/ new FastFail<>(
/*17*/ new WellFormedJson(
/*18*/ new Unnamed<>(Either.right(new Present<>(this.jsonRequestString)))
/*19*/ ),
/*20*/ requestJsonObject ->
/*21*/ new UnnamedBlocOfNameds<>(
List.of(
/*23*/ new FastFail<>(
/*24*/ new IsJsonObject(
/*25*/ new Required(
/*26*/ new IndexedValue("discount", requestJsonObject)
)
),
/*29*/ discountObject ->
/*30*/ new NamedBlocOfNameds<>(
/*31*/ "discount",
/*32*/ List.of(
/*33*/ new PromoCodeIsNotExpired(
/*34*/ new AsString(
/*35*/ new Required(
/*36*/ new IndexedValue("valid_until", discountObject)
)
)
),
/*40*/ new PromoCodeIsNotAlreadyRedeemed(
/*41*/ new PromoCodeContainsBothLettersAndDigits(
/*42*/ new Required(
/*43*/ new IndexedValue("promo_code", discountObject)
)
),
/*46*/ this.dbConnection
)
),
/*49*/ Discount.class
)
)
),
/*53*/ JsonObjectRepresentingRequestOrResponse.class
)
)
.result();
}
}
这个例子取自。在这里,您可以找到一个完整的。副本?最后,我找到了如何对REST服务执行json验证的方法。当这个问题不再被保留时,我将发布完整的示例作为答案。仅供参考,反对票将是因为stackoverflow不被认为是基于意见的问题的最佳论坛,例如什么是最好的库来做X。请查看@theon谢谢你的提示。我现在更新了问题。如果这个问题需要进一步考虑,你能告诉我吗。看起来好多了。
/*1 */ public class ValidatedJsonObjectRepresentingRequestOrResponse implements Validatable<JsonObjectRepresentingRequestOrResponse>
{
private String jsonString;
private Connection dbConnection;
/*6 */ public ValidatedJsonObjectRepresentingRequestOrResponse(String jsonString, Connection dbConnection)
{
this.jsonString = jsonString;
this.dbConnection = dbConnection;
}
@Override
/*13*/ public Result<JsonObjectRepresentingRequestOrResponse> result() throws Exception
{
return
/*16*/ new FastFail<>(
/*17*/ new WellFormedJson(
/*18*/ new Unnamed<>(Either.right(new Present<>(this.jsonRequestString)))
/*19*/ ),
/*20*/ requestJsonObject ->
/*21*/ new UnnamedBlocOfNameds<>(
List.of(
/*23*/ new FastFail<>(
/*24*/ new IsJsonObject(
/*25*/ new Required(
/*26*/ new IndexedValue("discount", requestJsonObject)
)
),
/*29*/ discountObject ->
/*30*/ new NamedBlocOfNameds<>(
/*31*/ "discount",
/*32*/ List.of(
/*33*/ new PromoCodeIsNotExpired(
/*34*/ new AsString(
/*35*/ new Required(
/*36*/ new IndexedValue("valid_until", discountObject)
)
)
),
/*40*/ new PromoCodeIsNotAlreadyRedeemed(
/*41*/ new PromoCodeContainsBothLettersAndDigits(
/*42*/ new Required(
/*43*/ new IndexedValue("promo_code", discountObject)
)
),
/*46*/ this.dbConnection
)
),
/*49*/ Discount.class
)
)
),
/*53*/ JsonObjectRepresentingRequestOrResponse.class
)
)
.result();
}
}
Result<JsonObjectRepresentingRequestOrResponse> result = new ValidatedJsonObjectRepresentingRequestOrResponse(jsonRequestString).result();
result.isSuccessful();
result.value().raw().discount().promoCode(); // VASYA1988