I'm wondering is there a way to flag to servicestack that you only want to execute the fluent validation filter for a request, and not go on to run the real code?
Ideally you could add a property to any request, something like
`ExecuteValidationOnly = true`
This would be really useful for tests, and for validating forms client side.
Thanks.
There's no such feature built into ServiceStack, but you can easily add a Request Filter that short-circuits the request based on a QueryString, e.g:
public class SkipRequestFeature : IPlugin
{
public void Register(IAppHost appHost)
{
appHost.GlobalRequestFilters.Add((req, res, dto) => {
if (req.QueryString["SkipRequest"] == "true")
res.EndRequestWithNoContent();
});
}
}
And register the plugin just after the ValidationFeature so it's executed after validation, e.g:
Plugins.Add(new ValidationFeature());
Plugins.Add(new SkipRequestFeature());
For this case, I usually unit test my actual AbstractValidator<T> class then when I need to test the service I create a mock on the validator and setup validation calls.
So there is a MyValidatorTests class and a MyServiceTests class.
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 have a custom RequestFilterAttribute that I am applying to my ServiceStack services:
[MyCustomAttribute]
public class MyService : ServiceStack.Service {...
I have recently begun using the AutoQuery feature (which is awesome) but I'm wondering how to apply MyCustomAttribute to the auto-generated services that you "get for free" when your request DTO inherits from QueryBase.
I could certainly add methods to my service with the "magic" AutoQuery code:
SqlExpression<DTO> sqlExpression = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<DTO> response = AutoQuery.Execute(request, sqlExpression);
but I'm hoping there's a better way?
If you wanted to customize the AutoQuery behavior you should first take a look at the extensibility options using Query Filters provides.
Otherwise you should be able to add the RequestFilter Attribute to the Request DTO itself, i.e:
[MyCustomAttribute]
public class MyQuery : QueryBase<Poco> {}
Alternatively you can get a reference to the auto-generated Service using:
var autoQueryService = appHost.Metadata.GetServiceTypeByRequest(typeof(MyQuery));
And then use the dynamic API to add custom attributes to it, e.g:
autoQueryService
.AddAttributes(new MyCustomAttribute { ... });
Since the Services are only generated and registered once the AutoQueryFeature Plugin is executed you'll only be able to access the service after all plugins are loaded which you can do:
1) In your own plugin by implementing the IPostInitPlugin Interface
2) By registering a AfterInitCallbacks handler:
this.AfterInitCallbacks.Add(appHost => { ... });
3) By overriding OnAfterInit() virtual method in your AppHost, e.g:
public override void OnAfterInit()
{
...
base.OnAfterInit();
}
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
{
...
}
...guess I'm the first to ask about this one?
Say you have the following routes, each declared on a different controller:
[HttpGet, Route("sign-up/register", Order = 1)]
[HttpGet, Route("sign-up/{ticket}", Order = 2)]
... you could do this in MVC 5.0 with the same code except for the Order parameter. But after upgrading to MVC 5.1, you get the exception message in the question title:
Multiple controller types were found that match the URL. This can
happen if attribute routes on multiple controllers match the requested
URL.
So the new RouteAttribute.Order property is only controller-level? I know in AttributeRouting.NET you can do SitePrecedence too. Is the only way to have routes like the above when all actions are in the same controller?
Update
Sorry, I should have mentioned these routes are on MVC controllers, not WebAPI. I am not sure how this affects ApiControllers.
If you know that ticket will be an int you can specify that type in the route to help resolve the route:
[HttpGet, Route("sign-up/register")]
[HttpGet, Route("sign-up/{ticket:int}")]
This approach worked for me, per user1145404's comment that includes a link to Multiple Controller Types with same Route prefix ASP.NET Web Api
In case of Attribute routing, Web API tries to find all the controllers which match a request. If it sees that multiple controllers are able to handle this, then it throws an exception as it considers this to be possibly an user error. This route probing is different from regular routing where the first match wins.
As a workaround, if you have these two actions within the same controller, then Web API honors the route precedence and you should see your scenario working.
There are two ways to fix this:
A regex constraint, like here: MVC Route Attribute error on two different routes
Or a custom route constraint, like here: https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/
You can create custom route constraints by implementing the IRouteConstraint interface. For example, the following constraint restricts a parameter to set of valid values:
public class ValuesConstraint : IRouteConstraint
{
private readonly string[] validOptions;
public ValuesConstraint(string options)
{
validOptions = options.Split('|');
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
object value;
if (values.TryGetValue(parameterName, out value) && value != null)
{
return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase);
}
return false;
}
}
The following code shows how to register the constraint:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var constraintsResolver = new DefaultInlineConstraintResolver();
constraintsResolver.ConstraintMap.Add("values", typeof(ValuesConstraint));
routes.MapMvcAttributeRoutes(constraintsResolver);
}
}
Now you can apply the constraint in your routes:
public class TemperatureController : Controller
{
// eg: temp/celsius and /temp/fahrenheit but not /temp/kelvin
[Route("temp/{scale:values(celsius|fahrenheit)}")]
public ActionResult Show(string scale)
{
return Content("scale is " + scale);
}
}
In my opinion, this isn't great design. There are no judgments about what URL you intended and no specificity rules when matching unless you explicitly set them yourself. But at least you can get your URLs looking the way you want. Hopefully your constraint list isn't too long. If it is, or you don't want to hard-code the route string parameter and its constraints, you could build it programmatically outside the action method and feed it to the Route attribute as a variable.
I am having liferay portlet and I need to heavily depend upon the AJAX calls. So I need to make multiple calls to serveResource method. One way to do the same is that I can pass a parameter with the URL and then differentiate the request according to that parameter.
But in my case I have to call serveResource so many times due to which the method will be difficult to maintain.
Is there any framework to do so? Using which the code becomes maintainable.
Use Spring MVC framework and call different method based on your business logic/user action in controller,
Try below code
in jsp
<portlet:resourceURL var="loadContents" id="loadContents"></portlet:resourceURL>
<portlet:resourceURL var="loadCategories" id="loadCategories"></portlet:resourceURL>
ajax call in jsp
AUI().ready(
function(A) {
A.use('aui-io-request',
function(aui) {
A.io.request("<%=loadContents%>", {
autoLoad : false,
cache : false,
dataType : 'json',
data:{},
method:'POST',
on : {
success : function(event, id, xhr) {
var response = this.get('responseData');
// add logic here after response
}
}
}).start();
});
});
in controller/ java class
#ResourceMapping("loadCategories")
public void loadCategories(final ResourceRequest resourceRequest, final ResourceResponse resourceResponse)
{
// your business logic goes here
}
#ResourceMapping("loadContents")
public void loadContents(final ResourceRequest resourceRequest, final ResourceResponse resourceResponse)
{
// your business logic goes here
}
hope above code snippets will help you and you get what you were looking for!!!
Adding more in this.We can not use the serveResource method like processAction.There can be multiple processAction in single liferay portlet(Which is not spring mvc portlet) ,while in case of serveReource it will be single.
serveResource is mainly used for ajax call.We can handle multiple ajax request in serveResource method by differentiating the call using resource Id.
resourceRequest.getResourceID() will return the Id, which we have defined in jsp using below code.
<portlet:resourceURL var="demoUrl" id="demoUrl"></portlet:resourceURL>