如何验证 REST 服务中的传入 JSON 数据?
Posted
技术标签:
【中文标题】如何验证 REST 服务中的传入 JSON 数据?【英文标题】:How do I validate incoming JSON data inside a REST service? 【发布时间】:2013-08-11 21:00:21 【问题描述】:rest 服务需要根据 json 模式验证所有传入的 json 数据。 json 模式是公共可访问的,可以通过 http 请求检索。
我正在使用 jackson-framwork 在 java 和 json 之间进行编组和解组。到目前为止,我找不到任何使用 jackson 验证架构数据的可能性。
我还尝试了JsonTools 框架,它显然提供了这样的验证功能。但不幸的是,我无法让验证工作。 Why JsonTool schema validation isn't working?
我怎样才能进行这样的验证?
【问题讨论】:
重复的***.com/questions/2499126/…? 我终于弄清楚了如何为 REST 服务执行 json 验证。一旦这个问题不再是on hold
,我将发布完整的示例作为答案
嗨。仅供参考,否决票将是因为 *** 不被认为是基于意见的问题的最佳论坛,例如什么是做 X 的最佳库。查看help section
@theon 谢谢你的提示。我现在更新了这个问题。如果问题需要进一步考虑,请告诉我。
太棒了。这样看起来更好。
【参考方案1】:
看起来您并没有绑定到 JSONSchema,尽管它似乎是您的默认选择。口味不同,但通常看起来更复杂。此外,就个人而言,我希望将数据和验证规则放在同一个地方。当在 java 代码而不是任何类型的配置文件中使用时,自定义验证器似乎更自然地适合。
这是这种方法的外观。比如说,您有以下 json 对象代表一些付款(无论是请求还是响应),但为简洁起见仅包含 discount
块:
"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();
让我们逐行看看这里发生了什么:
Line 1
ValidatedJsonObjectRepresentingRequestOrResponse
的声明。Line 6
其构造函数接受原始 json 字符串。它可能是传入的请求或收到的响应,或者几乎是其他任何东西。Line 13
:在调用此方法时开始验证。Lines 16
:更高级别的验证对象是FastFail
块.如果第一个参数无效,则立即返回错误。Lines 17-19
: 检查 json 是否格式正确。如果是后者,验证会快速失败并返回相应的错误。Line 20
: 如果 json 格式正确,则调用闭包,并将 json 数据作为其单个参数传递。Line 21
: json数据得到验证。它的结构是命名块的未命名块。它对应一个 JSON 对象。Line 26
:第一个(也是唯一的)块称为 discount
。Line 25
:它是必需的。Line 24
:它必须是一个 json对象。Line 23
:如果不是,将立即返回错误,因为它是FailFast
对象。Line 29
:否则,将调用闭包。Line 30
:@987654343 @ 块是由其他命名条目(对象或标量)组成的命名块。Line 36
:第一个称为 valid_until
Line 35
:它是必需的。Line 34
:并表示作为一个字符串,如果它真的是一个字符串。如果没有,则返回错误。Line 33
:最后,检查它是否过期。Line 43
:第二个参数称为promo_code
。Line 42
:也是必需的.Line 41
: 必须同时包含字母和数字。Line 40
: 并且它不应该已经被兑换了。这个事实肯定会保存在我们的数据库中,因此 ...Line 46
: ...this.dbConnection
参数。Line 49
: 如果所有先前的验证检查都成功,则创建一个 Discount
类的对象。Line 53
: 最后,JsonObjectRepresentingRequestOrResponse
被创建并返回。
验证成功后调用代码如下所示:
Result<JsonObjectRepresentingRequestOrResponse> result = new ValidatedJsonObjectRepresentingRequestOrResponse(jsonRequestString).result();
result.isSuccessful();
result.value().raw().discount().promoCode(); // VASYA1988
这个例子取自here。在这里你可以找到一个成熟的json request validation example。
【讨论】:
【参考方案2】:import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import com.github.fge.jackson.JsonLoader;
import com.fasterxml.jackson.databind.JsonNode;
public class ValidationJSON
public static void main(String[] arr)
String jsonData = "\"name\": \"prem\"";
String jsonSchema = ""; //Schema we can generate online using http://jsonschema.net/
final JsonNode data = JsonLoader.fromString(jsonData);
final JsonNode schema = JsonLoader.fromString(jsonSchema);
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
JsonValidator validator = factory.getValidator();
ProcessingReport report = validator.validate(schema, data);
System.out.println(report.isSuccess());
【讨论】:
【参考方案3】:我搜索了将传入的 json 数据强制验证到 RESTful 服务的最佳实践。我的建议是使用MessageBodyReader
在readFrom
方法中执行验证。下面有一个消息体阅读器示例,为了简单起见,它是非通用的。
我也有兴趣找到进行 json 数据验证的最佳框架。因为我使用 jackson 框架(版本 1.8.5)在 json 和 java 之间进行编组和解组,所以如果这个框架能够提供 json 数据验证功能会很好。不幸的是,我找不到与杰克逊一起做这件事的任何可能性。最后,我使用https://github.com 提供的 json-schema-validator 让它工作。我使用的版本是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"));
【讨论】:
以上是关于如何验证 REST 服务中的传入 JSON 数据?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Java 防止 XSS 攻击或 Rest API JSON 中的不可信数据?
Django REST 的 JSON Web 令牌不会向用户数据库进行身份验证