Is it possible something like this in web api :
[Route("api/bot1/messages")]
[BotAuthentication(id = ..., pass = '']
[Route("api/bot2/messages")]
[BotAuthentication(id = 2..., pass = 2...]
public class MessagesController : ApiController
PS : I'm noob.
Take a look at the source code for the [BotAuthentication] attribute, located here.
namespace Microsoft.Bot.Connector
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class BotAuthentication : ActionFilterAttribute {
...
}
}
In the [AttributeUsage()] attribute (documentation here), we see that there is a property inside of the class:
AllowMultiple = false
From the documentation for the AllowMultiple property:
Gets or sets a Boolean value indicating whether more than one instance
of the indicated attribute can be specified for a single program
element.
true if more than one instance is allowed to be specified; otherwise, false. The default is false.
This means that more than one [BotAuthentication] instance can not be specified for the Messages Controller.
So no, something like that is not possible.
(Also, as a note for next time, try to relate your question to the code you have posted. In no way do you say how you are using an "if-else" control statement in your code, and some people might not be able to infer what you mean.)
Related
I have a DbSet<Items> collection.
The primary key is a Guid. I don't want to order by this primary key. I want to order by an editable decimal property named "Order".
The code I have is very simple, and it works great until the user puts a "$top" parameter into the request:
public class ItemsController : ApiController
{
protected DbContext ctx = // ...
// GET api/documents
[EnableQuery()]
public IQueryable<Item> Get()
{
return ctx.Items.OrderBy(o => o.Order).AsQueryable();
}
When the user puts "$top" into the query string, the order gets all messed up (it presumably forces the ordering to be done by the primary key, for consistent paging results -- however, in my situation, this is having the opposite effect, it's preventing me from having consistent paging results).
I've tried moving .AsQueryable() to be earlier in the query (before the .OrderBy(...) clause), I've tried it without the .AsQueryable(), I've tried it with two AsQueryables, etc.
There are going to be a lot of items in this table, so it needs to be done via an IQueryable (enumerating all of the items on the web server via IEnumerable is not an option here).
The only thing that has worked so far is passing in "$orderby=Order" from the client, but I don't want to force that (seems like it will get forgotten easily).
1.) Is there anything I can do to make ordering by my Order property the default behavior here?
2.) Or failing that, is there anyway to trick WebApi / OData into thinking that a custom "$orderby=Order" clause was specified?
To override default sort order, you need to set property EnsureStableOrdering of EnableQueryAttribute to false, like describe here:
A true value indicates the original query should be modified when
necessary to guarantee a stable sort order. A false value indicates
the sort order can be considered stable without modifying the query.
Query providers that ensure a stable sort order should set this value
to false. The default value is true.
So in your code, changes the action attribute like this:
// GET api/documents
[EnableQuery(EnsureStableOrdering = false)]
public IQueryable<Item> Get()
{
return ctx.Items.OrderBy(o => o.Order).AsQueryable();
}
You can manually invoke the odata in your controller. This should create the proper sorted IQueryable and then apply the $top and any other odata like $filter and $skip. Now you don't have to return an IQueryable which was causing the problem because the actual query was being executed later in the pipeline.
public class ItemsController : ApiController
{
protected DbContext ctx = // ...
public IEnumerable<Item> Get(ODataQueryOptions<Item> odata)
{
var collection = ctx.Items.OrderBy(o => o.Order);
if (odata == null)
{
//return a default max size of 100
return collection.Take(100).ToList();
}
var results = odata.ApplyTo(collection.AsQueryable()) as List<Item>;
//still provide a max incase the $top wasn't specified.
//you could check the odata to see if $top is there or not.
return results.Take(100);
}
}
More information can be found in the WebApi documentation.
In my edmx model are 2 related tables: Challenge and ChallengeNote (has FK back to ChallengeID)
I can do this in breeze all day long
var qry = dataservice.getQuery("Challenges");
However, this fails every time:
var qry = dataservice.getQuery("Challenges").expand("ChallengeNotes");
The searchFailed is called and is the only error information in the console.
return dataservice.execute(qry.inlineCount(true))
.then(seachSucceeded)
.fail(searchFailed);
Does Breeze support relational data like this?
Does one need to write some custom code to support?
What am I missing?
Here's related answered question, but I was already following (unless I missed something) the answer's solution (and why I have the 2 context.Configuration settings in my ContextProvider).
breezejs-error-when-loading-an-entity-with-related-data
Here's another similar question that's been unanswered breeze-expand-query-fails-with-object-object-has-no-method-getproperty
Here's my provider code (want to use the BeforeSaveEntity override further on in the project):
public class ModelProvider : EFContextProvider<ModelEntities>
{
public ModelProvider()
: base()
{
this.Context.Configuration.LazyLoadingEnabled = false;
this.Context.Configuration.ProxyCreationEnabled = false;
}
}
Here's my controller code:
[BreezeController]
public class DataController : ApiController
{
readonly ModelProvider _contextProvider = new ModelProvider();
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
[HttpGet]
public IQueryable<Challenge> Challenges()
{
return _contextProvider.Context.Challenges.Include(x => x.ChallengeNotes);
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
[HttpGet]
public IQueryable<ChallengeNote> ChallengeNotes()
{
return _contextProvider.Context.ChallengeNotes;
}
}
When I browse to the URL, it's including the related entity:
http://localhost:53644/breeze/data/Challenges?$filter=Active%20eq%20true&$top=10&$expand=ChallengeNotes&$inlinecount=allpages
Here is the data coming from the Controller
At this point all things, imo, are pointing to Breeze configuration on either the Server or Client.
TIA
Breeze absolutely does support this, but you do need to make sure that your Entity Framework model is set up correctly. Take a look at the DocCode sample in the Breeze zip for a number of examples of using both expand (client side) or EF include (server side) clauses.
One guess about your problem is that you are using the Breeze camelCasing naming convention and therefore your "expand" clause should be
var qry = dataservice.getQuery("Challenges").expand("challengeNotes");
i.e. "challengeNotes" (note the casing) is the name of the client side property that corresponds to a server side property of "ChallengeNotes". To clarify, "expand" clauses take the names of client side "properties" as parameters and property names are what are transformed as a result of the Breeze.NamingConvention.
In contrast, a query resource name i.e. "Challenges" in your example is the name of the server side resource ( as a result of marking your "Challenges" method with the [HttpGet] annotation. This name is NOT affected by the NamingConvention.
Side notes: Your example has both an expand and an Include clause. Either of these is sufficient all by itself. You do not need both. In general you can either include an "expand" clause in your client side query OR have an Entity Framework "Include" clause on the server. The advantage of the first is that you can control the expand on the client, the advantage of the second is that you can insure that every query for a specified resource always fetches some related entities.
Hope this helps!
i am new in mvc .here scott shows how to Creating a Custom [Email] Validation Attribute in mvc. here is the picture.
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
1) now see how they did it. first create a class give a name and extend regular expression attribute class and in its ctor they use regex to validate email address
my question is when they use [Email(Errormessage="blah blah")]
then how MVC can understand this email attribute is pointing to email attribute class which extend regularexpression attribute class. how relation will be extanlish. the class name is email attribute but when they use then they use attribite name email. this is not clear to me please explain.
2) if i validate the email the above way they where validation will occur means at server side or client side ?
if not client side then how can i make it client and required js will be render for that.
please explain me with sample code example. thanks
The first question is best answered with a principle widely used in MVC: convention over configuration. That basically means: do the less config possible, use the most default functionalities. Several examples in ASP.NET MVC
Folder Controllers contain controllers by default.
The name of a view corresponds to the name of a Action in a Controller.
The folder name where a view is located corresponds to the Controller name without 'Controller' ending.
The class name of the controller ends with 'Controller' which is omitted when calling the controller.
The same with Attributes; the class name ends with 'Attribute' which is omitted in usage
etc, etc, etc,
There are many more like this and it is not configured. It is convention.
The second question is already partially answered in the question itself: you cannot inherit from EmailAddressAttribute as it's a sealed class. But you can use
RegularExpressionAttribute the way it's described in your question, or create a new attribute, like I will do it below.
However this way the validation will take place only on server side. To make it on client side you need to do the following:
public class EmailAttribute : ValidationAttribute, IClientValidatable
{
private const string VALIDATION_TYPE = "customEmail";
private const string EMAIL_REGEX = #"put your regex here";
public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule { ValidationType = VALIDATION_TYPE, ErrorMessage = ErrorMessageString };
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var sValue = value as string;
if (!string.IsNullOrEmpty(sValue) && Regex.Match(sValue, EMAIL_REGEX).Success)
{
return ValidationResult.Success;
}
return new ValidationResult(string.Format(ErrorMessageString, validationContext.MemberName));
}
}
Then in Javascript (I suppose you've included jQuery, jQuery.validate and jQuery.validate.unobtrusive) use the following:
$.validator.addMethod('customEmail', function (value, element) {
let regex = /put your regex here/;
return regex.test($(element).val());
});
$.validator.unobtrusive.adapters.add('customEmail', [], function (options) {
options.messages['customEmail'] = options.message;
options.rules['customEmail'] = options.params;
});
I am implementing ACL security using the spring-security-acl plugin. I have the following domain classes:
package test
class Subitem {
String name
static belongsTo = [employer: Employer]
static constraints = {
name blank: false
}
}
package test
class Employer {
String name
static hasMany = [users: User, items: Subitem]
static belongsTo = User
static constraints = {
name blank: false, unique: true
}
String toString() {
name
}
}
In the create.gsp file which is used to create a Subitem, there is the following statement:
<g:select id="employer" name="employer.id" from="${test.Employer.list()}" optionKey="id" required="" value="${subitemInstance?.employer?.id}" class="many-to-one"/>
From the EmployerController:
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[employerInstanceList: employerService.list(params),
employerInstanceTotal: employerService.count()]
}
Following the tutorial given here, I have moved some of the functionality with dealing with Employer to a service called EmployerService:
#PreAuthorize("hasRole('ROLE_USER')")
#PostFilter("hasPermission(filterObject, read)")
List<Employer> list(Map params) {
Employer.list params
}
int count() {
Employer.count()
}
Access to information in any given Employer class instance is restricted using ACL. At present, I can see ALL instances of Employer in the database in the drop down, and I assume that is because I am using the controller list(), not the service list() - however, I only want to see the filtered list of Employer domain classes. However, if I replace the g:select with:
<g:select id="employer" name="employer.id" from="${test.EmployerService.list()}" optionKey="id" required="" value="${subitemInstance?.employer?.id}" class="many-to-one"/>
then I get an internal server error because I haven't passed a Map parameter to the service list() function (and I don't know how to do this within the tag):
URI /security/subitem/create
Class groovy.lang.MissingMethodException
Message No signature of method: static test.EmployerService.list() is applicable for argument types: () values: [] Possible solutions: list(java.util.Map), is(java.lang.Object), wait(), find(), wait(long), get(long)
I only want to see the information that comes from the EmployerService list() function - how do I do this please? How do I reference the correct function from within the gap?
Edit 16 Mar 0835: Thanks #OverZealous, that's really helpful, I hadn't realised that. However, I've tried that and still get the same problem. I've put a println() statement in both the Employer and EmployerService list() functions, and can see that neither actually seems to get called when the g:select tag is parsed (even if I leave the g:select to refer to Employer). Is there another version of the list() function that is being called perhaps? Or how else to get the g:select to take account of the ACL?
Just change your method signature in the Service to look like this:
List<Employer> list(Map params = [:]) {
Employer.list params
}
The change is adding this: = [:]. This provides a default value for params, in this case, an empty map.
(This is a Groovy feature, BTW. You can use it on any method or closure where the arguments are optional, and you want to provide a default.)
OK, I worked it out, and here is the solution to anyone else who comes up against the same problem.
The create Subitem page is rendered by means of the Subitem's create.gsp file and the SubitemController. The trick is to amend the SubitemController create() closure:
class SubitemController {
def employerService
def create() {
// this line was the default supplied method:
// [subitemInstance: new Subitem(params)]
// so replace with the following:
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[subitemInstance: new Subitem(params), employerInstanceList: employerService.list(params),
employerInstanceTotal: employerService.count()]
}
}
So now when the SubitemController is asked by the g:select within the Subitem view for the list of Employers, it calls the EmployerService, which supplies the correct answer. We have simply added 2 further variables that are returned to the view, and which can be referenced anywhere within the view (such as by the g:select tag).
The lesson for me is that the View interacts with the Controller, which can refer to a Service: the Service doesn't play nicely with a View, it seems.
I have an action method that takes in a string as its only parameter. The action method transforms it, and returns the result back to the client (this is done on an ajax call). I need to allow markup in the string value. In the past, I've done this by decorating the property on my model with [AllowHtml], but that attribute cannot be used on a parameter and the AllowHtmlAttribute class is sealed, so I cannot inherit from it. I currently have a work around where I've created a model with just one property and decorated it with the aforementioned attribute, and this is working.
I don't think I should have to jump through that hoop. Is there something I'm missing, or should I make a request to the MVC team to allow this attribute to be used on method parameters?
If you need to allow html input for a particular parameter (opposed to "model property") there's no built-in way to do that because [AllowHtml] only works with models. But you can easily achieve this using a custom model binder:
public ActionResult AddBlogPost(int id, [ModelBinder(typeof(AllowHtmlBinder))] string html)
{
//...
}
The AllowHtmlBinder code:
public class AllowHtmlBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
var name = bindingContext.ModelName;
return request.Unvalidated[name]; //magic happens here
}
}
Find the complete source code and the explanation in my blog post: https://www.jitbit.com/alexblog/273-aspnet-mvc-allowing-html-for-particular-action-parameters/
Have you looked at ValidateInputAttribute? More info here: http://blogs.msdn.com/b/marcinon/archive/2010/11/09/mvc3-granular-request-validation-update.aspx.