-
Notifications
You must be signed in to change notification settings - Fork 4
Going from Schema to Code Using C# (RESTful Web Service)
The software artifacts in the ES-CIM repository are meant for use in either SOAP or RESTful web services. Although the majority of web services utilize REST, SOAP is still a major part of the CIM world, and can be challenging for new developers unfamiliar with the technology to use. This page will go into detail on creating a RESTful CIM Web Service using the ES-CIM artifacts.
Code can be generated from either XSDs or JSON Schema to develop code representing the structure of a message. For this project, use xsd.exe
to generate code directly from an XSD. Although this is described in XML, the service can still be set up to send/receive either JSON or XML. The snippet below show code built from the XSD.
Because of the nature of CIM messages potentially being in either XML or JSON format, content-type must be parsed and serialization and validation handled properly. The content types should be application/json
or application/xml
. While System.Xml
libraries can be used to handle XML payloads, Newtonsoft.Json
works best for handling JSON.
Sample code for parsing JSON/XML for PWRWaterChemistry:
Models.PWRWaterChemistryDataSetsRequestMessageType dataSetRequest = null;
Stream content = await Request.Content.ReadAsStreamAsync();
content.Seek(0, SeekOrigin.Begin);
// If validation passes, statusCode should be Created (201). NoContent (204) or OK (200) are alternatives.
switch (contentType.ToLower())
{
case "application/json":
string jsonSchemaFileName = HostingEnvironment.MapPath("~/Schema/PWRWaterChemistryDataSets/PWRWaterChemistryDataSets.draft-07.schema.json")
//string jsonSchemaFileName = HostingEnvironment.MapPath("~/Schema/PWRWaterChemistryDataSets/ConvertedFrom_PWRWaterChemistry.xsd.json");
try
{
using (StreamReader file = File.OpenText(jsonSchemaFileName))
using (JsonTextReader schemaReader = new JsonTextReader(file))
using (StreamReader contentReader = new StreamReader(content))
using (JSchemaValidatingReader validatingReader = new JSchemaValidatingReader(new JsonTextReader(contentReader)))
{
validatingReader.Schema = JSchema.Load(schemaReader);
validatingReader.ValidationEventHandler += ValidatingReader_ValidationEventHandler;
JsonSerializer serializer = new JsonSerializer();
dataSetRequest = serializer.Deserialize<Models.PWRWaterChemistryDataSetsRequestMessageType>(validatingReader);
}
}
catch (Exception exc)
{
if (!errors.Any())
{
if (exc.InnerException == null)
{
errors.Add(new Models.ErrorType(10.0, exc.Message));
}
else
{
errors.Add(new Models.ErrorType(10.1, exc.InnerException.ToString()));
}
}
}
if (errors.Any())
{
//result = BadRequest($"PWRWaterChemistry - Message validation failed - " + string.Join(" - ", messages));
responseMessage.Reply.Result = Models.ReplyTypeResult.FAILED;
responseMessage.Reply.Error = errors.ToArray();
result = ResponseMessage(Request.CreateResponse(HttpStatusCode.BadRequest, responseMessage));
}
else
{
responseMessage.Reply.Result = Models.ReplyTypeResult.OK;
responseMessage.Payload = dataSetRequest.Payload;
result = Created(Request.RequestUri, responseMessage);
}
break;
case "application/xml":
string mainSchemaFileName = HostingEnvironment.MapPath("~/Schema/PWRWaterChemistryDataSets/PWRWaterChemistryDataSetsMessage.xsd");
string messageSchemaFileName = HostingEnvironment.MapPath("~/Schema/Message.xsd");
string dataSetSchemaFileName = HostingEnvironment.MapPath("~/Schema/PWRWaterChemistryDataSets/PWRWaterChemistryDataSets.xsd");
using (StreamReader mainSchemaFile = File.OpenText(mainSchemaFileName))
using (StreamReader messageFile = File.OpenText(messageSchemaFileName))
using (StreamReader dataSetFile = File.OpenText(dataSetSchemaFileName))
{
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(XmlSchema.Read(messageFile, ValidationMessageCallBack));
schemaSet.Add(XmlSchema.Read(dataSetFile, ValidationMessageCallBack));
schemaSet.Add(XmlSchema.Read(mainSchemaFile, ValidationMessageCallBack));
// Set the validation settings.
XmlReaderSettings settings = new XmlReaderSettings
{
ValidationType = ValidationType.Schema,
Schemas = schemaSet,
ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings,
IgnoreComments = true
};
settings.ValidationEventHandler += ValidationMessageCallBack;
try
{
// Create the XmlReader object to process the content stream.
using (XmlReader reader = XmlReader.Create(content, settings))
{
XmlRootAttribute dataSetsRoot = new XmlRootAttribute
{
ElementName = "PWRWaterChemistryDataSetsRequestMessage",
IsNullable = true,
DataType = "tns:PWRWaterChemistryDataSetsRequestMessageType",
Namespace = "http://epri.com/powergeneration/2020/PWRWaterChemistryDataSetsMessage"
};
XmlSerializer serializer = new XmlSerializer(typeof(Models.PWRWaterChemistryDataSetsRequestMessageType), dataSetsRoot);
dataSetRequest = serializer.Deserialize(reader) as Models.PWRWaterChemistryDataSetsRequestMessageType;
}
}
catch (Exception exc)
{
if (!errors.Any())
{
if (exc.InnerException == null)
{
errors.Add(new Models.ErrorType(10.0, exc.Message));
}
else
{
errors.Add(new Models.ErrorType(10.1, exc.InnerException.ToString()));
}
}
}
}
XmlSerializer serializeResponse = new XmlSerializer(responseMessage.GetType());
using (Utf8StringWriter sw = new Utf8StringWriter())
{
if (errors.Any())
{
responseMessage.Reply.Result = Models.ReplyTypeResult.FAILED;
responseMessage.Reply.Error = errors.ToArray();
serializeResponse.Serialize(sw, responseMessage);
result = ResponseMessage(new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(sw.ToString(), Encoding.UTF8, contentType)
});
}
else
{
responseMessage.Reply.Result = Models.ReplyTypeResult.OK;
responseMessage.Header.CorrelationID = dataSetRequest.Header.CorrelationID;
responseMessage.Payload = dataSetRequest.Payload;
serializeResponse.Serialize(sw, responseMessage);
result = ResponseMessage(new HttpResponseMessage(HttpStatusCode.Created)
{
Content = new StringContent(sw.ToString(), Encoding.UTF8, contentType)
});
}
}
break;
default:
result = ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.UnsupportedMediaType, $"Content-Type '{contentType}' is not supported"));
break;
}