I have an existing application that sends a Request with a parameter named 'filters'. The 'filters' parameter contains a string that is JSON encoded. Example:
[{"dataIndex":"fieldName", "value":"fieldValue"}, {"dataIndex":"field2", "value":"value2"}].
Using ServiceStack, I would like to bind this as a property on a C# object (class Grid). Is there a preferred method to handle this? Here are the options I can think of. I don't think either 'feel' correct.
Option 1:
I do have a 'ServiceModel' project and this would create a dependency on it which I don't really like.
In AppHost.Configure() method add
RequestBinders[typeof(Grid)] => httpReq => {
return new Grid() {
Filters = new ServiceStack.Text.JsonSerializer<IList<Filter>>().DeserializeFromString(httpReq.QueryString["filters"])
}
}
Option 2:
Seems kind of 'hacky'
public class Grid
{
private string _filters;
public dynamic Filters {
get
{
ServiceStack.Text.JsonSerializer<IList<Filter().DeserializeFromString(_filters);
}
set
{
_filters = value;
}
}
}
You can send Complex objects in ServiceStack using the JSV Format.
If you want to send JSON via the QueryString you can access it from inside your Service of Request filters with something like:
public object Any(Request req) {
var filters = base.Request.QueryString["Filters"].FromJson<List<Filter>>();
}
Note: Interfaces on DTOs are bad practice.
Related
We are using fluentvalidation (with service stack) to validate our request DTO's. We have recently extended our framework to accept "PATCH" requests, which means we now have a requirement to apply validation ONLY when the patch contained the field being validated.
We have done this using an extension method such as this:
RuleFor(dto => dto.FirstName).Length(1,30)).WhenFieldInPatch((MyRequest dto)=>dto.FirstName);
RuleFor(dto => dto.MiddleName).Length(1,30)).WhenFieldInPatch((MyRequest dto)=>dto.MiddleName);
RuleFor(dto => dto.LastName).Length(1,30)).WhenFieldInPatch((MyRequest dto)=>dto.LastName);
This means we can run the same validation for a POST/PUT or a PATCH.
I have been looking for a way of hooking in to the fluent validation framework in such as way that we do not need to duplicate the .WhenFieldInPatch() rule on EVERY line in our validations, but have not yet found a nice way to do this.
I have tried the following:
Creating a helper method (in a in a base class) to intercept the initial "RuleFor" which adds the .When() clause up front, but the this does not work as fluent validation requires the .When() to be last
Intercepting the calls in PreValidation, but I can only intercept based on the whole class, and not on a rule by rule basis
Adding an extension method to apply to the end of every rule (as per example), but I cannot access the initial expression in order to check whether the field should be mapped - so I need to pass it in again.
Am I missing something, or am I attempting the impossible?
Thanks
When I need to share Fluent Validation Logic I'd use extension methods, here's an example of shared Extension methods for TechStacks, e.g:
public static class ValidatorUtils
{
public static bool IsValidUrl(string arg) => Uri.TryCreate(arg, UriKind.Absolute, out _);
public static string InvalidUrlMessage = "Invalid URL";
public static IRuleBuilderOptions<T, string> OptionalUrl<T>(
this IRuleBuilderInitial<T, string> propertyRule)
{
return propertyRule
.Length(0, UrlMaxLength)
.Must(IsValidUrl)
.When(x => !string.IsNullOrEmpty(x as string))
.WithMessage(InvalidUrlMessage);
}
}
And some examples where they're shared:
public class CreatePostValidator : AbstractValidator<CreatePost>
{
public CreatePostValidator()
{
RuleSet(ApplyTo.Post, () =>
{
RuleFor(x => x.Url).OptionalUrl();
});
}
}
public class UpdatePostValidator : AbstractValidator<UpdatePost>
{
public UpdatePostValidator()
{
RuleSet(ApplyTo.Put, () =>
{
RuleFor(x => x.Url).OptionalUrl();
});
}
}
I am specifically using breezejs and the server code for breeze js converts the dbcontext into a form which is useable on the clientside using EdmxWriter.WriteEdmx. There are many properties which I have added JsonIgnore attributes to so that they don't get passed to the client side. However, the metadata that is generated (and passed to the clientside) from EdmxWriter.WriteEdmx still has those properties. Is there any additional attribute that I can add to those properties that I want ignored so that they are ignored by EdmxWriter.WriteEdmx? Or, would I need to make a separate method so as not to have any other unintended side effects.
You can sub-class your DbContext with a more restrictive variant that you use solely for metadata generation. You can continue to use your base context for persistence purposes.
The DocCode sample illustrates this technique with its NorthwindMetadataContext which hides the UserSessionId property from the metadata.
It's just a few extra lines of code that do the trick.
public class NorthwindMetadataContext : NorthwindContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Hide from clients
modelBuilder.Entity<Customer>().Ignore(t => t.CustomerID_OLD);
// Ignore UserSessionId in metadata (but keep it in base DbContext)
modelBuilder.Entity<Customer>().Ignore(t => t.UserSessionId);
modelBuilder.Entity<Employee>().Ignore(t => t.UserSessionId);
modelBuilder.Entity<Order>().Ignore(t => t.UserSessionId);
// ... more of the same ...
}
}
The Web API controller delegates to the NorthwindRepository where you'll see that the Metadata property gets metadata from the NorthwindMetadataContext while the other repository members reference an EFContextProvider for the full NorthwindContext.
public class NorthwindRepository
{
public NorthwindRepository()
{
_contextProvider = new EFContextProvider<NorthwindContext>();
}
public string Metadata
{
get
{
// Returns metadata from a dedicated DbContext that is different from
// the DbContext used for other operations
// See NorthwindMetadataContext for more about the scenario behind this.
var metaContextProvider = new EFContextProvider<NorthwindMetadataContext>();
return metaContextProvider.Metadata();
}
}
public SaveResult SaveChanges(JObject saveBundle)
{
PrepareSaveGuard();
return _contextProvider.SaveChanges(saveBundle);
}
public IQueryable<Category> Categories {
get { return Context.Categories; }
}
// ... more members ...
}
Pretty clever, eh?
Just remember that the UserSessionId is still on the server-side class model and could be set by a rogue client's saveChanges requests. DocCode guards against that risk in its SaveChanges validation processing.
You can sub-class your DbContext with a more restrictive variant that you use solely for metadata generation. You can continue to use your base context for persistence purposes.
The DocCode sample illustrates this technique with its NorthwindMetadataContext which hides the UserSessionId property from the metadata.
It's just a few extra lines of code that do the trick.
The Web API controller delegates to the NorthwindRepository where you'll see that the Metadata property gets metadata from the NorthwindMetadataContext while the other repository members reference an EFContextProvider for the full NorthwindContext.
Pretty clever, eh?
If you use the [NotMapped] attribute on a property, then it should be ignored by the EDMX process.
I am not sure if ServiceStack has a mechanism to prevent "JavaScript/HTML Injection" on Entities (Request Entities) properties.
Also as per my understanding entity's properties of type string is prone to JavaScript/HTML injection
If there is no in built mechanism please suggest me a better option.
One of the option which i see is use to validate may be using Fluent Validation or any other validating library
Use validation:
Yes you should be using Fluent Validation or another validation mechanism to sanitise all the values that are passed as a request to your ServiceStack service.
Why ServiceStack shouldn't sanitise for you:
ServiceStack won't do this for you, after all sending HTML and/or JavaScript in a request to the service may be perfectly legitimate, (i.e. where your service is a content manager for a blog), and it's wrong to assume the request is an injection attack.
ServiceStack isn't constricted to only being consumed by web applications, so it's up to the service to decide which values are appropriate.
It should be noted that ServiceStack does prevent SQL injection by escaping all parameters.
Encode HTML entities:
If you are concerned about HTML injection, then you should consider encoding HTML entities, then any unsafe values that are returned won't affect your result. You can do this easily using this request filter, and marking up your DTO with an attribute [EncodeHtml].
GlobalRequestFilters.Add((req,res,dto) => {
var dtoType = dto.GetType();
var filteredProperties = dtoType.GetPublicProperties().Where(p => p.PropertyType == typeof(string) && p.HasAttribute<EncodeHtmlAttribute>() && p.CanWrite);
foreach(var property in filteredProperties)
property.SetValue(dto, HttpUtility.HtmlEncode(property.GetValue(dto, null)), null);
});
On your DTO add the [EncodeHtml] attribute to the properties you want to protect.
[Route("/test", "GET")]
public class Test
{
public string UnsafeMessage { get; set; }
[EncodeHtml]
public string SafeMessage { get; set; }
}
The attribute declaration is simply:
public class EncodeHtmlAttribute : Attribute {}
Then when you send a request such as:
/test?unsafeMessage=<b>I am evil</b>&safeMessage=<b>I am good</b>
The result will be
UnsafeMessage: "<b>I am evil</b>"
SafeMessage: "<b>I am good</b>"
I hope this helps.
As per your suggestion if you want to throw an exception on any DTOs that may contain HTML then you could use a more general check which prevents any HTML in any strings on the DTO by checking against a regular expression, but I'd do this sparingly.
GlobalRequestFilters.Add((req,res,dto) => {
var dtoType = dto.GetType();
if(!dtoType.HasAttribute<PreventHtmlAttribute>())
return;
var filteredProperties = dtoType.GetPublicProperties().Where(p => p.PropertyType == typeof(string));
foreach(var property in filteredProperties){
var value = property.GetValue(dto, null) as string;
if(value != null && Regex.Match(value, #"<[^>]*>", RegexOptions.IgnoreCase).Success)
throw new HttpError(System.Net.HttpStatusCode.BadRequest, "400", "HTML is not permitted in the request");
}
});
Then use this attribute:
public class PreventHtmlAttribute : Attribute {}
On the DTO:
[PreventHtml]
[Route("/test", "GET")]
public class Test
{
...
}
I am using Breeze with much success in my SPA, but seem to be stuck when trying to return parent->child data in a single query by using expand().
When doing a single table query, the $type in the JSON return is correct:
$type: MySPA.Models.Challenge, MySPA
However if I use expand() in my query I get the relational data, but the $type is this:
System.Collections.Generic.Dictionary 2[[System.String, mscorlib],[System.Object, mscorlib]]
Because of the $type is not the proper table + namespace, the client side code can't tell that this is an entity and exposes it as JSON and not a Breeze object (with observables, entityAspect, etc.).
At first I was using my own ContextProvider so that I could override the Before/After saving methods. When I had these problems, I reverted back to the stock EFContextProvider<>.
I am using EF5 in a database first mode.
Here's my controller code:
[BreezeController]
public class DataController : ApiController
{
// readonly ModelProvider _contextProvider = new ModelProvider();
readonly EFContextProvider<TestEntities> _contextProvider = new EFContextProvider<TestEntities>();
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
[HttpGet]
public IQueryable<Challenge> Challenges()
{
return _contextProvider.Context.Challenges;
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
public IQueryable<ChallengeNote> ChallengeNotes()
{
return _contextProvider.Context.ChallengeNotes;
}
}
Here's my BreezeWebApiConfig.cs
public static void RegisterBreezePreStart()
{
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{controller}/{action}"
);
}
Is there a configuration setting that I am missing?
Did you try "expanding" on server side? Is it needed to do expand on client side? I tried to do expand before but failed for me as well, did some research and decided I'd rather place it on server:
[HttpGet]
public IQueryable<Challenge> ChallengesWithNotes()
{
return _contextProvider.Context.Challenges.Include("ChallengeNotes");
}
This should be parsed as expected. On client side you would query for "ChallengeNotes" instead of "Challenges" and you wouldn't need to write expand part.
I strongly suspect that the problem is due to your use of the [Queryable] attribute.
You must use the [BreezeQueryable] attribute instead!
See the documentation on limiting queries.
We are aware that Web API's QueryableAttribute has been deprecated in favor of EnableQueryAttribute in Web API v.1.5. Please stick with BreezeQueryable until we've had a chance to write a corresponding derived attribute for EnableQuery. Check with the documentation for the status of this development.
For example, if I have standard request and response DTOs, linked up via IReturn<T>, what are the reasons to have a service method signature like the following, as seen in various online examples (such as this one, although not consistently throughout):
public object Get(DTO.MyRequest request)
rather than:
public IList<DTO.MyResponse> Get(DTO.MyRequest request)
Is an object return type here simply to support service features like gzip compression of the output stream, which results in the output being a byte array? It seems that one would want to have the appropriate stronger return type from these so-called "action" calls, unless I'm missing some common scenario or use case.
It used to be a limitation that the New API only supported an object return type, but that hasn't been the case for a while where all examples on the New API wiki page now use strong-typed responses.
One of the reasons where you might want to return an object return type is if you want to decorate the response inside a HttpResult, e.g:
public object Post(Movie movie)
{
var isNew = movie.Id == null;
Db.Save(movie); //Inserts or Updates
var movie = new MovieResponse {
Movie = Db.Id<Movie>(newMovieId),
};
if (!isNew) return movie;
//Decorate the response if it was created
return new HttpResult(movie) {
StatusCode = HttpStatusCode.Created,
Headers = {
{ HttpHeaders.Location, Request.AbsoluteUri.CombineWith(movieId) }
}
};
}
It's also useful if you want to return different responses based on the request (though it's not something I recommend), e.g:
public object Get(FindMovies request)
{
if (request.Id != null)
return Db.Id<Movie>(movie.Id);
return Db.Select<Movie>();
}
If you do choose to return an object I highly recommend decorating your Request DTO with the IReturn<T> marker to give a hint to ServiceStack what the expected response of the service should be.