kendo ui grid not working with .net core - postgresql

I am trying to get Kendo UI grid MVC working in a .net core, reading data from PostgreSQL database.
I set up a project, connected it to a database, scaffolded controller with appropriate views and it's working ok (meaning CRUD operations are working ok).
Now I want to hook it up with Kendo.
I followed guide from kendo website and installed successfully (although it didn't offer pro version in nugget so I had to install trial??). I added all those CSS/js files in _Layout.cshtml
this is my code in controller:
public ActionResult kendo()
{
return View();
}
// GET for Kendo
public ActionResult Categories_Read([DataSourceRequest] DataSourceRequest request)
{
return Json(GetCategories().ToDataSourceResult(request));
}
public static IEnumerable<Category> GetCategories()
{
var result = from c in _context.Categories
select new Category()
{
Id = c.Id,
Name = c.Name
};
return result;
}
this is my code in kendo.cshtml
#(Html.Kendo().Grid<Category>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.Id).Title("id");
columns.Bound(c => c.Name);
})
.HtmlAttributes(new { style = "height: 550px;" })
.Scrollable()
.Groupable()
.Sortable()
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.ButtonCount(5))
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("Categories_Read", "Categories"))
.PageSize(20)
)
Model is simple
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
The problem is that there is no data displayed in kendo grid. I can see in debugger that there is request going forward and data coming backwards but nothing is shown in grid.
?

The problem is most probably caused by the new ASP.NET Core serialization mechanism. Follow the guidelines in the "second" Step 4 here:
http://docs.telerik.com/kendo-ui/aspnet-mvc/mvc-core/getting-started
Step 4 Open Startup.cs, using a text editor (IDE) and update it as described below.
Locate the ConfigureServices method and add a call to services.AddKendo at the end.
public void ConfigureServices(IServiceCollection services)
{
...
// Maintain property names during serialization. See:
// https://github.com/aspnet/Announcements/issues/194
services
.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
// Add Kendo UI services to the services container
services.AddKendo();
}
Locate the Configure method and add a call to app.UseKendo at the end.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
// Configure Kendo UI
app.UseKendo(env);
}
UPDATE
In order to verify that the above configuration is applied, check the server response and see if it has the following structure and letter casing for Data and Total:
{
"Data": [
{ "Id": "1", "Name": "Name 1" },
{ "Id": "2", "Name": "Name 2" }
],
"Total": "2"
}

I had the same problem in a .net core 3.1 project, the solution here is to add the following lines in Startup.cs:
services
.AddMvc()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
services.AddKendo();

Related

EF Core 5.x + OData 7.5.6 - $Top doesn't insert TOP inside of EF Query

I'm thinking I missed something very simply but here is what I'm trying to todo. I have a .NET Core 5 project with EF Core 5 + OData 7.5.6. Everything appears to be working except for INSERT a TOP command in the generated SQL Query. Here is my controller. Very simple.
[EnableQuery]
[ApiController]
[Route("odata/[controller]")]
public class ConferenceHistoryController : ControllerBase
{
private readonly cdr_database_2Context _db;
public ConferenceHistoryController(cdr_database_2Context db)
{
_db = db;
}
[HttpGet]
public IEnumerable<_000701CallDataRecord> GetConferenceList()
{
// Return Full List
var query = _db._000701CallDataRecords;
var qs = query.ToQueryString();
return query.ToList();
}
}
When I send in my request to:
https://localhost:44355/odata/ConferenceHistory/?$select=RecordId,version&$top=5
The resulting SQL query is:
SELECT [0].[endDateTime], [0].[id], [0].[organizer], [0].[ParticipantCount], [0].[participants], [0].[PoorCall], [0].[type], [0].[version]
FROM [000701_CallDataRecord] AS [0]
As you can see, it's missing both the TOP and the SELECT commands. I have the following in my Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add CORS to Project
services.AddCors(options =>
{
options.AddDefaultPolicy(builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
});
// Add OData
services.AddOData();
services.AddControllers();
services.AddDbContext<cdr_database_2Context>(options =>
options.UseSqlServer(Configuration.GetConnectionString("CDRConnection"))
);
// Add Swagger Support
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ODataAPI", Version = "v1" });
});
SetOutputFormatters(services);
}
And
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Use HTTPS & Routing
app.UseHttpsRedirection();
app.UseRouting();
// Auth???
app.UseAuthorization();
// Swagger Configure
app.UseSwagger();
app.UseSwaggerUI((config) =>
{
config.SwaggerEndpoint("/swagger/v1/swagger.json", "Swagger Odata Demo Api");
});
// Setup Endpoint for EDM
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.EnableDependencyInjection();
endpoints.Select().Expand().Filter().OrderBy().MaxTop(50).Count();
endpoints.MapODataRoute("odata", "odata", GetEdmModel());
});
}
And
private IEdmModel GetEdmModel()
{
// Add OData - EDM Definitions Below
var odataBuilder = new ODataConventionModelBuilder();
odataBuilder.EntitySet<WeatherForecast>("WeatherForecast");
odataBuilder.EntitySet<_000701CallDataRecord>("ConferenceHistory");
return odataBuilder.GetEdmModel();
}
Just looking for some direction on where I could have gone wrong. Everything else seems to be working really well.
Your controller needs to derive from ODataController not ControllerBase
You need to decorate GetConferenceList() with [Queryable]
OK, I figured out where the issue was in my above. It was with using the .ToList and the IEumerable. So if I changed this:
[HttpGet]
public IEnumerable<_000701CallDataRecord> GetConferenceList()
{
// Return Full List
var query = _db._000701CallDataRecords;
var qs = query.ToQueryString();
return query.ToList();
}
To:
[HttpGet]
public IEnumerable<_000701CallDataRecord> GetConferenceList()
{
// Return Full List
var query = _db._000701CallDataRecords;
var qs = query.ToQueryString();
return query;
}
It works. It's also worth noting that the .ToQueryString() doesn't show the OData/EF insertion of the TOP command. But if I enable .EnableSensitiveDataLogging() in the Startup.cs file, then I clearly see it being inserted into it.
Apparently, calling .ToList() will cause it to process and ignore any of the fancy OData commands. That was the only change and all was good then.

In Swagger UI, how can I remove the padlock icon from "anonymous" methods?

I'm creating an API with .Net Core 2.1 and using JSON Web Token (JWT) for authentication.
I have 2 controllers: AuthenticationController and UserController.
I have decorated AuthenticationController with [AllowAnonymous] and UserController with [Authorize].
Swagger is working correctly: it allows me to hit the endpoints in AuthenticationController (SignUp/SignIn) without requesting authorization, and it does request JWT to hit the endpoints in UserController.
However, in Swagger UI, every endpoint of every controller shows a padlock icon as if all of them required authorization. Everything works correctly and as expected but it just bothers me that the endpoints that don't require authorization still show that padlock icon.
Is there a way to remove the padlock icon from those endpoints?
I believe that something can be done with the OperationFilter but I couldn't find a way.
Absolutly, you need to use an IOperationFilter to remove the padlock icon for the anonymous endpoints.
// AuthResponsesOperationFilter.cs
public class AuthResponsesOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AuthorizeAttribute>();
if (authAttributes.Any())
{
var securityRequirement = new OpenApiSecurityRequirement()
{
{
// Put here you own security scheme, this one is an example
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
};
operation.Security = new List<OpenApiSecurityRequirement> { securityRequirement };
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
}
}
}
// Startup.cs
services.AddSwaggerGen(c =>
{
...
c.OperationFilter<AuthResponsesOperationFilter>();
};
Do not forget to remove any call to AddSecurityRequirement in your Startup.cs, otherwise the padlock icon would still be added to all endpoints.
this solution works for SwashBuckle 5.0.0-rc5 and .Net Core 3.1.1 Web API.
You need to :
implement an IOperationFilter interface,
add c.OperationFilter(); in your Startup.cs file
finally remove any call of AddSecurityRequirement
public class AuthResponsesOperationFilter: IOperationFilter {
public void Apply(OpenApiOperation operation, OperationFilterContext context) {
if (!context.MethodInfo.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) &&
!context.MethodInfo.DeclaringType.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute)) {
operation.Security = new List < OpenApiSecurityRequirement > {
new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme {
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "bearer"
}
}, new string[] {}
}
}
};
}
}
}
Install Package
Swashbuckle.AspNetCore.Filters
And then when you document your swagger you need to add the below line
options.OperationFilter<SecurityRequirementsOperationFilter >();
Here's an example from .NET 6
builder.Services.AddSwaggerGen(options => {
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "API",
Version = "v1",
Description = "API using .NET 6"
});
options.OperationFilter<SecurityRequirementsOperationFilter>();
});
In startup.cs -> services.AddSwaggerGen , you need to add c.OperationFilter<ApplyOAuth2Security>(); and add below method in stratup.cs which will enable lock/authorize icon in Swagger UI for the action methods which are marked as Authorize only.
private class ApplyOAuth2Security : IOperationFilter
{
/// <inheritdoc/>
public void Apply(Operation operation, OperationFilterContext context)
{
var filterDescriptor = context.ApiDescription.ActionDescriptor.FilterDescriptors;
var isAuthorized = filterDescriptor.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
var authorizationRequired = context.MethodInfo.CustomAttributes.Any(a => a.AttributeType.Name == "AuthorizeAttribute");
if (isAuthorized && authorizationRequired)
{
operation.Security = new List<IDictionary<string, IEnumerable<string>>>
{
new Dictionary<string, IEnumerable<string>>
{
{ "oauth2", new string[] { "openid" } },
},
};
}
}
}

How to override route in a plugin nopcommerce

I've a route like Admin/Vendor in my MVC application . Without changing this route I need to point this same route to another method say CustomAdmin/CustomVendor.
I tried attribute routing but no luck . Is there any way to do this. My current code is given below
Original Method:
public class AdminController
{
public ActionResult Vendor()
{
return View();
}
}
Custom Method:
public class CustomAdminController
{
[Route("Admin/Vendor")]
public ActionResult CustomVendor()
{
return View();
}
}
As you're developing a plugin. You have to add your custom route to the RouteProvider.
In default nopCommerce AdminController and Vendor doesn't exists, so I assume that you're trying to override vendor list method of admin.
Which looks like:
public partial class RouteProvider : IRouteProvider
{
public void RegisterRoutes(RouteCollection routes)
{
var route = routes.MapRoute("Plugin.GroupName.PluginName.CustomVendor",
"Admin/Vendor/List",
new { controller = "CustomAdminController", action = "CustomVendor", orderIds = UrlParameter.Optional, area = "Admin" },
new[] { "Nop.Plugin.GroupName.PluginName.Controllers" });
route.DataTokens.Add("area", "admin");
routes.Remove(route);
routes.Insert(0, route);
}
public int Priority
{
get
{
return 100; // route priority
}
}
}
Side Note: GroupName and PluginName should be your plugin group name and plugin name.
Hope this helps !
On your plugin which class implements the interface IRouteProvider, you can easily override the route there.
Likewise I have a class named RouteProvider in my plugin, So I have Implemented the abstract function RegisterRoutes and simply it can be overrided by
routes.MapRoute("Plugin.Promotion.Combo.SaveGeneralSettings",
"Admin/Vendor",
new { controller = "CustomAdmin", action = "CustomVendor", },
new[] { "Nop.Plugin.Promotion.Combo.Controllers" }
);
Here Plugin.Promotion.Combo must be replaced by your plugin directory.And using SaveGeneralSettings or any things you want to use that will be your route url

HippoCMS Rest service does not return image links

I'm a new bee to Hippo CMS. I'm working on version 10 and I'm using angularJS service to consume the rest url for "banners" I created through HippoCms.
This is my Rest URL for Banners created via Hippo's Rest Api Manager:
http://localhost:8080/site/stbetrest/Banner?_type=json
and the reponse I'm getting when consumed the link is:
{
"pageSize": 10,
"total": 3,
"items": [
{
"content": "<![CDATA[\r\n\r\n <p>Banner description</p>\r\n\r\n \r\n]]>",
"title": "Sample banner"
},
{
"content": "<![CDATA[<p>10 Simple Steps to Green Your Office:</p>\n\n<p> </p>\n\n<p>
<img src=\"/site/pagenotfound\" /></p>]]>",
"title": "10 Simple Steps to Green Your Office"
},
{
"content": "<![CDATA[<p>How to Green Your Dorm Room</p>]]>",
"title": "How to Green Your Dorm Room"
}
],
"currentPage": 1,
"totalPages": 1
}
The Problem here is I can't see the Images I used inside the banner documents in Hippo. I want to get those Images/links to load them into a carousel created in AngularJs. Please guide me how to generate the images also into the above banner response.
UPDATE:
All though via the 'localhost:8080/cms' it shows uploaded images, but can not access the image via response:
#XmlRootElement(name = "banner")
#XmlAccessorType(XmlAccessType.NONE)
#HippoEssentialsGenerated(internalName = "gogreen:bannerdocument")
#Node(jcrType = "gogreen:bannerdocument")
public class Banner extends BaseDocument {
#XmlElement
#HippoEssentialsGenerated(internalName = "gogreen:title")
public String getTitle() {
return getProperty("gogreen:title");
}
#XmlJavaTypeAdapter(HippoHtmlAdapter.class)
#XmlElement
#HippoEssentialsGenerated(internalName = "gogreen:content")
public HippoHtml getContent() {
return getHippoHtml("gogreen:content");
}
#HippoEssentialsGenerated(internalName = "gogreen:link")
public HippoBean getLink() {
return getLinkedBean("gogreen:link", HippoBean.class);
}
#XmlJavaTypeAdapter(KerkRestAdapter.class)
#XmlElement
#HippoEssentialsGenerated(internalName = "gogreen:image")
public Kerk getImage() {
return getLinkedBean("gogreen:image", Kerk.class);
}
}
and my Content rewriter is :
public class RestContentRewriter extends SimpleContentRewriter {
#Override
protected String rewriteBinaryLink(String binaryLinkSrc, Node node, HstRequestContext requestContext, Mount targetMount) {
return super.rewriteBinaryLink(binaryLinkSrc, node, requestContext, requestContext.getMount("site"));
}
And My Adapter is:
public class KerkRestAdapter extends XmlAdapter<String, HippoHtml> {
#Override
public HippoHtml unmarshal(String representation) throws Exception {
throw new UnsupportedOperationException("Unmarshalling not implemented.");
}
#Override
public String marshal(HippoHtml html) throws Exception {
if (html == null) {
return null;
}
final HstRequestContext context = RequestContextProvider.get();
//final RestContentRewriter contentRewriter = new RestContentRewriter();
final ContentReWriter<String> contentRewriter = new RestContentRewriter();
final String rewrite = contentRewriter.rewrite(html.getContent(), html.getNode(), context, context.getMount("api"));
return "<![CDATA[" + rewrite + "]]>";
}
}
additional Question: what is the mount point to be used in rewriter? ( Rest mount name or gogreen??)
Please help !
You have used the Essentials Rest setup tool. That generates a example or demo Rest implementation. After generating the setup a developer shall always want to extend and refine the result.
The links inside rich text in this setup are by default generated for the current mount, which is the rest mount. Since the images are not available through the rest mount, HST generated the /pagenotfound URL.
If you inspect the bean for the Banner you see that for the HippoHtml field an HippoHtmlAdapter is used. Check the code of it: it uses the SimpleContentRewriter. Create your own version of the Adapter and use an extended version of the ContentRewriter. Override the #rewriteBinaryLink method. In it call the super method, but specify the site mount as the target mount.
I think adding an hst:alias to the site mount is a good idea so you can reference the site with the alias.

Validate checkbox - frontend MVC3

I have created the following custom attribute to assist me with validating a required checkbox field:
public class CheckboxRequired : ValidationAttribute, IClientValidatable
{
public CheckboxRequired()
: base("required") { }
public override bool IsValid(object value)
{
return (bool)value == true;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationType = "mandatory";
yield return rule;
}
}
However, I am trying to get it to trigger client side, and not when I call my ActionResult (if (ModelState.IsValid))
The validation does work when I call my ActionResult, but I'd prefer it to validate before getting that far.
What modifications do I need to make to make the validation kick in client side?
Thanks
In order to implement the client side you can add for example a jQuery validator method and an unobtrusive adapter (simple example):
// Checkbox Validation
jQuery.validator.addMethod("checkrequired", function (value, element)
{
var checked = false;
checked = $(element).is(':checked');
return checked;
}, '');
jQuery.validator.unobtrusive.adapters.addBool("mandatory", "checkrequired");
I hope it helps.
How about the old good Regex?
[RegularExpression("^(true|True)$", ErrorMessage="Required...")]
public bool AgreeWithTos { get; set; }
Accepts both "true", and 'True' as javascript and .NET format booleans differently.