json.net Schema IsValid slow - c#

I have a web service that communicates with client with JSON messages, the run-time itself is not aware of the data-model which is why I use json.net Schema to validate messages from client and inside the service itself, however Its causing a great amount of overhead in terms of performance.
Simplified code that still contain enough context to understand what I am doing.
public class Template
{
/// <summary>
/// Template known as
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Razor Template
/// </summary>
public string RazorTemplate { get; private set; }
/// <summary>
/// Json Schema definition
/// </summary>
public string Schema { get; private set; }
private JSchema _schema { get; set; }
private JSchema JSchema
{
get
{
if (_schema == null)
_schema = JShema.Parse(Schema);
return _schema;
}
}
private void Validate(JObject obj)
{
// Schema validation Error messages.
IList<string> ValidationError;
// Schema validation.
if (!obj.IsValid(JSchema, out ValidationError))
{
throw new Exception(string.Join(",", ValidationError.ToArray()));
}
}
public string RunTemplate(JObject jobj)
{
// Validate Json Object.
Validate(jobj);
// Code here that access our RazorEngine cache, and add then run Razor Template, or directly run a cached Razor Template...
return "Here we return string generated by RazorEngine to sender.";
}
}
Lets say i run a simple "Hello #Model.Name!" template that validates that json has string Name this is 15-20 times slower then if i comment out validation entirely.
Are there more efficient ways to use IsValid in Json.Net Schema?

Make sure you update to the latest version of Json.NET Schema available on NuGet, performance was slow when validating certain schemas with older versions.
Also there is documentation on the Json.NET Schema website for performance best practices.

Related

Swashbuckle XML Comments with Property Hiding

Is there a way how to make Swashbuckle take the XML Comments for properties which hide those in the base class?
class Base {
/// <summary>
/// This comment shows in swagger UI.
/// </summary>
public int Value { get; set; }
}
class Model: Base {
/// <summary>
/// This comment is not visible in swagger UI.
/// </summary>
public new int Value { get; set; }
}
Is there any workaround, e.g. using a kind of SchemaFilter?
UPDATED
The above simple example works as expected.
Our problem is probably caused by a wrong configuration of the IncludeXmlComments option as our models come from multiple assemblies.
I found a related discussion on GitHub

How to show custom request examples in the .net api documentation?

/// <summary>
/// updates information of a job
/// </summary>
/// <param name="job"></param>
/// <returns>Ok or Error</returns>
/// <example>
/// {
/// "info_id": 1,
/// "some_other_id": 2
/// }
/// </example>
[HttpPost]
[Route("api/job/update")]
public IHttpActionResult update(Models.Job job) {
}
//the model
public class Job {
[Required(AllowEmptyStrings = false)]
[Range(1, Int64.MaxValue)]
public Int64 info_id { get; set; }
public Int64? some_other_id{ get; set; }
public DateTime last_log_time { get; set; }
}
Imagine the setup above. I would like to show the example JSON written inside the doc block of update in the documentation. However, there is shown the serialized JSON of an object typed Job with default values instead.
I don't want developers to think they could or should provide last_log_time to function update. This property shall be shown in a response message, but not sent to the api.
How can I customize the examples for the requests formats per function? Ideally I would state it inside the doc block as shown (the API should only take requests in JSON-format), or maybe per annotations on the properties of the Job-class.
How can we hide a property in WebAPI? the answer provodided here does not help because, as stated above, last_log_time shall be given in the response. If I annotate it with [IgnoreDataMember] it will be ignored globally.
You can add [ApiExplorerSettings(IgnoreApi = true)] to your last_log_time property, but it only hides last_log_time in body parameters.
If you want to hide in sample format, you need customize source code of method WriteSampleObjectUsingFormatter at file Areas\HelpPage\SampleGeneration\HelpPageSampleGenerator.cs

Generate model description in AspNet WebApi help pages

How do I generate a description for my model in Asp.Net Web Api help pages?
Example:
As you can see from the example, I can already generate Name, Type and Additional Information. But how do I generate Description?
I've tried nothing and I'm all out of ideas.
No, that's not true. I've tried adding comments to my TransactionDto class, but it does not work.
/// <summary>
/// A DTO (Data Transfer Object) for Transaction objects.
/// </summary>
public class TransactionDto
{
/// <summary>
/// The manager who registered the transaction.
/// </summary>
public string FromId { get; set; }
/// <summary>
/// The receiving manager.
/// </summary>
[Required]
public string ToId { get; set; }
/// <summary>
/// Optional expiration date.
/// </summary>
public DateTime? Expires { get; set; }
/// <summary>
/// Date the transaction was created.
/// </summary>
public DateTime Created { get; set; }
}
I have configured HelpPageConfig.cs to use an XmlDocumentationProvider like so:
config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
So how do I generate these descriptions for models?
I believe you have models in the separate project other than the Web API project?
If that is the case, the web API project is not aware of the help XML file generated for the models. you need to set the XML output path for both web API project and the models project and then combine both XML files in the register method of HelpPageConfig.cs file.
public static void Register(HttpConfiguration config)
{
XmlDocument apiDoc = new XmlDocument();
apiDoc.Load(HttpContext.Current.Server.MapPath("~/App_Data/WebApi.Orders.xml"));
XmlDocument contractsDoc = new XmlDocument();
contractsDoc.Load(HttpContext.Current.Server.MapPath("~/App_Data/Contracts.xml"));
if (contractsDoc.DocumentElement != null && apiDoc.DocumentElement!=null)
{
XmlNodeList nodes = contractsDoc.DocumentElement.ChildNodes;
foreach (XmlNode node in nodes)
{
XmlNode copiedNode = apiDoc.ImportNode(node, true);
apiDoc.DocumentElement.AppendChild(copiedNode);
}
apiDoc.Save(HttpContext.Current.Server.MapPath("~/App_Data/WebApi.Orders.xml"));
}
config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/WebApi.Orders.xml")));
......
}

Documenting descriptions on Complex Types

Within my API I'm trying to document the different field descriptions, however none of attributes seem to work. I know this functionality is supposed to have been recently implemented within WebAPI 5.1 (running WebAPI.HelpPage 5.1.2).
ASP.Net Web API Help Pages: Document Model Data Annotations - Work Item 877
I'm trying to document both my response model:
And the individual fields/properties
I've tried a mixture of XML comments, DataMember and Display attributes but none seem to be picked up.
/// <summary>
/// blah blah blah
/// </summary>
[DataContract(Name = "Application")]
public class Application
{
/// <summary>
/// Please Display!
/// </summary>
[DataMember(Order = 0)]
[Display(Description="Please Display!")]
[StringLength(11, MinimumLength = 11)]
public string ApplicationId { get; set; }
Here is a sample from my Areas/HelpPage/App_Start/HelpPageConfig.cs
namespace WebAPI.Areas.HelpPage
{
#pragma warning disable 1591
/// <summary>
/// Use this class to customize the Help Page.
/// For example you can set a custom <see cref="System.Web.Http.Description.IDocumentationProvider"/> to supply the documentation
/// or you can provide the samples for the requests/responses.
/// </summary>
public static class HelpPageConfig
{
public static void Register(HttpConfiguration config)
{
// remove unwanted formatters
config.Formatters.Clear();
var jsonsettings = new JsonSerializerSettings() { DateParseHandling = DateParseHandling.None };
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.Add(new XmlMediaTypeFormatter());
config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/WebAPI.XML")));
// create sample objects
config.SetSampleObjects(new Dictionary<Type, object>
{
{ typeof(MyResponse), new MyResponse() {
Message = "Key d795677d-6477-494f-80c5-874b318cc020 is not recognised",
Code = ResponseCode.InvalidKey, Id = null }
}
});
//*** More Sample Requests ***
}
}
#pragma warning restore 1591
}
Update 10/06/2014: My class definitions are stored in a separate library. I've noticed a discrepancy here. The main API and class definition library were generating separate XML files.
API Project
Definition Project
I've attempted to rectify this by making the Definition write to the same XML project. However this doesn't work, and the class definition entries aren't added.
To have a content displayed in Description section you need to feel section of XML comments. If you would have your model class placed inside your webapi project - then this would be a solution. Your problem is that you need to read xml documentation form 2 xml files and one time and XmlDocumentationProvider does not support that. My suggestion is to create your own MultipleFilesXmlDocumentationProvider with a little effort like this:
public class MultipleFilesXmlDocumentationProvider : IDocumentationProvider
{
IEnumerable<XmlDocumentationProvider> xmlDocumentationProviders;
public MultipleFilesXmlDocumentationProvider(IEnumerable<string> documentPaths)
{
xmlDocumentationProviders = documentPaths.Select(path => new XmlDocumentationProvider(path));
}
public string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
{
foreach(XmlDocumentationProvider provider in xmlDocumentationProviders)
{
string documentation = provider.GetDocumentation(parameterDescriptor);
if(documentation != null)
return documentation;
}
return null;
}
public string GetDocumentation(HttpActionDescriptor actionDescriptor)
{
foreach (XmlDocumentationProvider provider in xmlDocumentationProviders)
{
string documentation = provider.GetDocumentation(actionDescriptor);
if (documentation != null)
return documentation;
}
return null;
}
public string GetDocumentation(HttpControllerDescriptor controllerDescriptor)
{
foreach (XmlDocumentationProvider provider in xmlDocumentationProviders)
{
string documentation = provider.GetDocumentation(controllerDescriptor);
if (documentation != null)
return documentation;
}
return null;
}
public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor)
{
foreach (XmlDocumentationProvider provider in xmlDocumentationProviders)
{
string documentation = provider.GetDocumentation(actionDescriptor);
if (documentation != null)
return documentation;
}
return null;
}
}
This will be just wrapper over XmlDocumentationProvider - it will work with a collection of XmlDocumentationProvider and looks for the first one that will provide desired documentation. Then you change your configuration in HelpPageConfig to use your MultipleFilesXmlDocumentationProvider:
config.SetDocumentationProvider(
new MultipleFilesXmlDocumentationProvider(
new string[] {
HttpContext.Current.Server.MapPath("~/bin/WebAPI.XML"),
HttpContext.Current.Server.MapPath("~/bin/EntityModel.Definitions.XML")
}
)
);
Of course take into account that for the configuration above both XML files should be within WebAPI project bin folder.

How do you do web forms model validation?

We have an application with three layers: UI, Business, and Data. The data layer houses Entity Framework v4 and auto-generates our entity objects. I have created a buddy class for the entity VendorInfo:
namespace Company.DataAccess
{
[MetadataType(typeof(VendorInfoMetadata))]
public partial class VendorInfo
{
}
public class VendorInfoMetadata
{
[Required]
public string Title;
[Required]
public string Link;
[Required]
public string LinkText;
[Required]
public string Description;
}
}
I want this validation to bubble up to the UI, including custom validation messages assigned to them. In MVC this is a piece of cake but in web forms I have no clue where to begin. What is the best way to utilize model validation in asp.net web forms?
I did find an article that explains how to build a server control for it, but I can't seem to get it working. It compiles and even recognizes the control but I can never get it to fire.
Any ideas?
Thanks everyone.
I solved it. It would appear that the server control I found was not designed to read fields in a buddy class via the MetadataType attribute. I modified the code to look for its validation attributes in the buddy class rather than the entity class itself.
Here is the modified version of the linked server control:
[DefaultProperty("Text")]
[ToolboxData("<{0}:DataAnnotationValidator runat=server></{0}:DataAnnotationValidator>")]
public class DataAnnotationValidator : BaseValidator
{
#region Properties
/// <summary>
/// The type of the source to check
/// </summary>
public string SourceTypeName { get; set; }
/// <summary>
/// The property that is annotated
/// </summary>
public string PropertyName { get; set; }
#endregion
#region Methods
protected override bool EvaluateIsValid()
{
// get the type that we are going to validate
Type source = GetValidatedType();
// get the property to validate
FieldInfo property = GetValidatedProperty(source);
// get the control validation value
string value = GetControlValidationValue(ControlToValidate);
foreach (var attribute in property.GetCustomAttributes(
typeof(ValidationAttribute), true)
.OfType<ValidationAttribute>())
{
if (!attribute.IsValid(value))
{
ErrorMessage = attribute.ErrorMessage;
return false;
}
}
return true;
}
private Type GetValidatedType()
{
if (string.IsNullOrEmpty(SourceTypeName))
{
throw new InvalidOperationException(
"Null SourceTypeName can't be validated");
}
Type validatedType = Type.GetType(SourceTypeName);
if (validatedType == null)
{
throw new InvalidOperationException(
string.Format("{0}:{1}",
"Invalid SourceTypeName", SourceTypeName));
}
IEnumerable<MetadataTypeAttribute> mt = validatedType.GetCustomAttributes(typeof(MetadataTypeAttribute), false).OfType<MetadataTypeAttribute>();
if (mt.Count() > 0)
{
validatedType = mt.First().MetadataClassType;
}
return validatedType;
}
private FieldInfo GetValidatedProperty(Type source)
{
FieldInfo field = source.GetField(PropertyName);
if (field == null)
{
throw new InvalidOperationException(
string.Format("{0}:{1}",
"Validated Property Does Not Exists", PropertyName));
}
return field;
}
#endregion
}
This code only looks in the buddy class. If you want it to check an actual class and then its buddy class, you'll have to modify it accordingly. I did not bother doing that because usually if you are using a buddy class for validation attributes it's because you are not able to use the attributes in the main entity class (e.g. Entity Framework).
For model validation in web forms I'm using DAValidation library. It supports validation on client side (including unobtrusive validation), extensibility based on same principles as in MVC. It is MS-PL licensed and available via Nuget.
And here is bit out of date article describing with what thoughts control was build.

Categories