![](/img/trans.png)
[英]How do i read an incoming JSON from JavaScript inside a servlet in doPost()?
[英]How do I validate incoming JSON data inside a REST service?
休息服務需要根據 json 模式驗證所有傳入的 json 數據。 json 模式可公開訪問,並可通過 http 請求檢索。
我正在使用 jackson-framework 在 java 和 json 之間進行編組和解組。 到目前為止,我找不到通過使用 jackson 來根據模式驗證數據的任何可能性。
我還嘗試了JsonTools框架,它顯然提供了這樣的驗證功能。 但不幸的是,我無法進行驗證。 為什么 JsonTool 架構驗證不起作用?
我怎樣才能做這樣的驗證?
我搜索了將傳入 json 數據強制驗證到 RESTful 服務的最佳實踐。 我的建議是使用MessageBodyReader
在readFrom
方法中執行驗證。 下面是一個 message-body-reader 示例,為了簡單起見,它是非通用的。
我也有興趣找到進行 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"));
}
}
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());
}
}
看起來您並未綁定到 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();
}
}
讓我們一行一行地看看這里發生了什么:
ValidatedJsonObjectRepresentingRequestOrResponse
Line 1
聲明。
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
: Discount
塊是一個命名塊,由其他命名條目(對象或標量)組成。
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
這個例子取自這里。 在這里您可以找到一個完整的json 請求驗證示例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.