Paging in .NET Web API - c#

I have to do paging for an odata endpoint built using Entity Framework . I know I can do it using
private ODataQuerySettings settings = new ODataQuerySettings();
settings.PageSize = myPageSize; // I keep this value in web.config of solution
and
options.ApplyTo(IQueryable, settings);
But I am constrained not to use ApplyTo (i.e. I don't want to use the settings above) and take the page size from the web.config of my solution without modifying the url presented by the web api i.e. no client size paging.
So, far I haven't found a way to do this. I can't put page size in [ Enable Query ] as that is not dynamically lifting page size parameter from web.config.
I wonder if what I want could be done or am I trying to do something too tricky.

You can extend the default behavior of the EnableQuery attribute to use web.config's value as you want. Maybe something like this:
public class EnablePagedQueryAttribute : EnableQueryAttribute
{
public EnablePagedQueryAttribute()
{
int myPageSizeFromWebConfig = 0;
// Get value from web.config as you want:
if (int.TryParse(ConfigurationManager.AppSettings["myPageSize"], out myPageSizeFromWebConfig))
{
this.PageSize = myPageSizeFromWebConfig;
}
}
}

Related

Where does custom IRouteConstraint get discovered by ASP.NET Core

I'm digging the source code to see how asp.net core discovery custom IRouteConstraint.
We know that when we define a custom IRouteConstraint, we add it to RouteOptions as
public void ConfigureServices(IServiceCollection services)
{
services.Configure<RouteOptions>(opts => {
opts.ConstraintMap.Add("countryName", typeof(CountryRouteConstraint));
});
}
public class CountryRouteConstraint: IRouteConstraint
{
public bool Match(...) { ... }
}
where IOptions<RouteOptions> is registered.
So I check the source code of
EndpointRoutingMiddleware (https://source.dot.net/#Microsoft.AspNetCore.Routing/EndpointRoutingMiddleware.cs,e91e5febd7b6da29)
DfaMatcher
(https://source.dot.net/#Microsoft.AspNetCore.Routing/Matching/DfaMatcher.cs,0b08e610bec2cfbc)
and so on, I didn't find any part of the source code that tries to read from RouteOptions to discovery custom IRouteConstraint.
This is the place I think most likely https://source.dot.net/#Microsoft.AspNetCore.Routing/Matching/DfaMatcher.cs,197
but still doesn't find anything.
Can anybody show me the section of the source code that ASP.NET Core read from RouteOptions to discovery custom IRouteConstraint?
Navigate to that ConstraintMap property of RouteOptions, to which you are adding your CountryRouteConstraint constraint.
Look for its references.
The ones of most interest for you are
DefaultInlineConstraintResolver
DefaultParameterPolicyFactory
Both make use of the ParameterPolicyActivator for the instantation of the constraints, passing in that ConstraintMap.
E.g. DefaultParameterPolicyFactory shows below
var parameterPolicy = ParameterPolicyActivator.ResolveParameterPolicy<IParameterPolicy>(
_options.ConstraintMap,
_serviceProvider,
inlineText,
out var parameterPolicyKey);

JSON configuration properties containing dashes/hyphens in .NET Core

I have a .NET Core 2.0 project using a JSON configuration file, via the Microsoft.Extensions.Configuration.Json provider.
This application is a utility application to support another application that I do not control.
To keep consistency with that other application's configuration file format/style, multi-word setting keys use dashes between words.
Example JSON:
{
"multi-word-setting": "setting value"
}
Example settings class:
public class AppSettings
{
// Pascal casing, as is typical in C#
public string MultiWordSetting { get; set; }
}
Example application code:
class Program
{
private static void Main ( string[ ] args )
{
IConfigurationBuilder configBuilder = new ConfigurationBuilder( ).SetBasePath( Environment.CurrentDirectory ).AddJsonFile( "my-settings.json", true, true );
IConfigurationRoot configRoot = configBuilder.Build( );
AppSettings config = new AppSettings( );
configRoot.Bind( config );
Console.WriteLine( config.MultiWordSetting );
}
}
However, given that hyphens are illegal in identifiers in C#, how can I follow typical C# naming conventions while also following the defined style of the application I am supporting?
I know I can use Newtonsoft and its JsonPropertyAttribute to just manually deal with the json data, but I'd prefer to make this work without "external" libraries, if possible. Besides, JSON.Net is over 20x larger, and the configuration libraries handle other stuff, such as automatic reloading, binding, merging, and optional files.
I've tried adding DataMember attributes to the properties of my class, to indicate the name of the json property, but that doesn't do the trick.
Is there a way to do this, with the .NET Core Microsoft.Extensions.Configuration.Json provider?
While I was writing the question, I decided to dive into the .net core source and found that no, it is not possible to do what I wanted to do.
So, I decided to fix it and, pending the outcome of ASP.Net Configuration Pull Request 775, it may be possible in a future version of .net core. Please feel free to review/scrutinize that pull request.
Until then, a relatively simple workaround is to bind the configuration as normal and then manually access any unsupported settings via the ConfigurationRoot object's indexer.
Note that, if you're using the automatic reload feature, you'd have to manually handle setting the property on reload, too.

Read patched attribute values from config files?

I want to read the patched attributes that are in Sitecore.ItemWebApi.config to determine whether the Sitecore Item API is enable in my site. This value I'm looking for is itemwebapi.mode, and I want to see whether it's set to 'Off' or not.
<site name="mysite">
<patch:attribute name="itemwebapi.mode">StandardSecurity</patch:attribute>
<patch:attribute name="itemwebapi.access">ReadOnly</patch:attribute>
<patch:attribute name="itemwebapi.allowanonymousaccess">false</patch:attribute>
</site>
I tried getting ConfigurationManager.AppSettings["itemwebapi.mode"] but it returns null. How do I get this value?
I think you need to use sitecore configuration factory to read the configuration stuff.
(http://sitecore-community.github.io/docs/documentation/Sitecore%20Fundamentals/Sitecore%20Configuration%20Factory/#config_factory)
Try as below. If not working experiment in similar way.
var refObj = Sitecore.Configuration.Factory.CreateObject("site/patch:attribute", true) as itemwebapi.mode;
If not useful, put a comment. I will delete it.
You can access the attributes on the site node using the Properties accessor,
if the attribute is not set then the value will be empty. For the current Context site:
string mode = Sitecore.Context.Site.Properties["itemwebapi.mode"];
string access = Sitecore.Context.Site.Properties["itemwebapi.access"];
string anon = Sitecore.Context.Site.Properties["itemwebapi.allowanonymousaccess"];
If you are checking within an ItemWebApi request (beginning with -/item/) then you can check the ItemWebApi.Context:
if (Sitecore.ItemWebApi.Context.Current != null)
{
Mode mode = Sitecore.ItemWebApi.Context.Current.Settings.Mode;
AccessType access = Sitecore.ItemWebApi.Context.Current.Settings.Access;
bool anon = Sitecore.ItemWebApi.Context.Current.Settings.AnonymousAcessAllowed;
}
This will give you strongly typed access to the settings using enums. Unfortunately the overloaded constructor for RuntimeSettings() is marked as internal so it is not possible to new this up yourself in a normal web request but could check the code and do something similar if you need.

How to disable c# ODataController client side query

According to this tutorial: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/using-select-expand-and-value
"Web API 2 adds support for the $expand, $select, and $value options in OData. These options allow a client to control the representation that it gets back from the server"
My question it, how can I disable the representation manipulation done at the client side. In other words, my server makes sure that filtering/selecting etc. are done properly, and thus I do not want the client side to do it again. It is more of an overhead.
I think you misunderstand the purpose of query options like $expand, $select, etc. They do not cause data to be manipulated on the client. Rather, they are instructions to the service. In the Web API OData implementation, query options are typically handled by the EnableQuery attribute or the Queryable attribute. If you don't use these attributes, then you are responsible for writing the code that handles query options. Or you are free to not support them.
In your controller action, like get method, add attribute [EnableQuery] (this is for OData v4)
IN your client, send out request like ~/EntitySet?$filter=... & $select = ...
Then the response will only contain the filtered and select content.
Refer to https://github.com/OData/ODataSamples/tree/master/WebApi/v4/ODataQueryableSample to see the example.
You can create a custom attribute which would inherit from EnableQueryAttribute and then override the ValidateQuery method to limit the allowed query options as well as allowed functions and page size.
using System.Net.Http;
using System.Web.OData;
using System.Web.OData.Query;
public class SecureApiQueryAttribute : EnableQueryAttribute
{
public override void ValidateQuery(HttpRequestMessage request, ODataQueryOptions queryOptions)
{
base.AllowedQueryOptions = AllowedQueryOptions.None;
base.PageSize = 30;
base.AllowedFunctions = AllowedFunctions.AllFunctions;
base.ValidateQuery(request, queryOptions);
}
}
Then you can use this custom attribute like this
[SecureApiQuery]
public IHttpActionResult Get([FromODataUri] int? key = null)
{
}

CDN for Images in ASP.NET

I am in the process of moving all of the images in my web application over to a CDN but I want to easily be able to switch the CDN on or off without having to hard code the path to the images.
My first thought was to add an HttpHandler for image extensions that depending whether a variable in the web.config (something like ) will serve the image from the server or from the CDN. But after giving this a little though I think I've essentially ruled this out as it will cause ASP.NET to handle the request for every single image, thus adding overhead, and it might actually completely mitigate the benefits of using a CDN.
An alternative approach is, since all of my pages inherit from a base page class, I could create a function in the base class that determines what path to serve the files from based off the web.config variable. I would then do something like this in the markup:
<img src='<%= GetImagePath()/image.png' />
I think this is probably what I'll have to end up doing, but it seems a little clunky to me. I also envision problems with the old .NET error of not being able to modify the control collection because of the "<%=" though the "<%#" solution will probably work.
Any thoughts or ideas on how to implement this?
You've dismissed writing an HttpHandler based on an assumption of pre-optimization. I would revisit this and definitely write a simple HttpHandler and test it out. You might find that your Page method solution might even be slower, especially if you get the ASP preprocessor involved.
HttpHandlers are pretty close to the metal - it's a miniscule amount of overhead for IIS to hand the request to ASP.Net. It would be a more elegant solution than what you're proposing, and probably more scalable and I'm willing to bet - faster.
Have you considered a slightly simpler approach?
If your pages all inherit from a base class, you could expose a property on that which contains the prepend URL to your CDN (or, to your local server if you want to switch the CDN off). It is then a trivial matter of storing the prepend URL in the web.config:
public string PrependURLPath() {
get { return ConfigurationManager.AppSettings["ImagePrependURL"].ToString(); }
}
In your <appSettings/> element, you can simply choose what the prepend URL would be, eg:
http://my.cdn.com/user/
or:
http://my.own.server.com/images/
Pretty simple!
You would then be able to code your image refernces as per your example, but calling your base page property to expose the desired path:
<img src='<%= this.BasePage.PrependURLPath() + [YourImagePath.png] %>'/>
I agree that setting the image source through the inline call is messy, but you could probably do as someone else has suggested and then iterate through the image controls on your page, changing the prepend URL as you go.
Even if your pages currently only inherit from System.Web.UI.Page, it's a simple matter to create your own base class which inherits System.Web.Page, then do a find/replace in your solution on all remaining pages.
Hope this helps.
weighing in pretty late here, but i've been looking for a similar solution myself. searched google to sanity check what i had done. didn't consider the HttpHandler approach, what i did was simply extend the ASP.net Image control:
public class Img : Image
{
public Img()
{
RelativePath = false;
}
public bool RelativePath { get; set; }
public override string ImageUrl
{
get
{
if (RelativePath)
return base.ImageUrl;
return "http://some.configurable-value.com" + base.ImageUrl;
}
set { base.ImageUrl = value; }
}
}
it's rough and ready, but it works :) obviously it should rely on some configurable value rather than a string literal, but that's not a big change
If you display your images using tags you could create a control adapter, these allow you to alter the way .net controls render or universally alter them something like this should do the trick:
using System.Web.UI.WebControls.Adapters;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace ExampleCode
{
public class ImageAdapter : WebControlAdapter
{
private bool UseCdn
{
get { return true; } // Get value from config or anywhere else
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
Image image = (Image)Control;
if (UseCdn)
{
// If using relative urls for images may need to handle ~
image.ImageUrl = String.Format("{0}/{1}", "CDN URL", image.ImageUrl);
}
}
}
}
Then add a browser file to the App_Browsers folder in your web project like below:
<browsers>
<browser refID="Default">
<controlAdapters>
<adapter
controlType="System.Web.UI.WebControls.Image"
adapterType="ExampleCode.ImageAdapter"
/>
</controlAdapters>
</browser>
</browsers>
you could loop all controls and change the images url in the prerender event on your base class...
The good thing about the HTTP Handler approach is that it's quite re-usable and configurable: you can identify img paths to handle based on location - assuming the structure they're in helps this.
The possible drawback is that image file extensions (.jpg, .png, etc) aren't automatically passed on to the asp.net pipe-line; you can easily config IIS to do so - but you need to have a certain level of contriol over IIS - so it might not be an option if you're on a shared hosting environment.
I will go for #Rhys approach for image control.
Most of the time, I try to use background image css than using image control.
After that I upload both css and images together to the cloud and working fine for relative path.
Doesn't look like there has been an accepted answer yet so here is my suggestion. I had similar problems dealing with modifying URL's transparently (to a different end, but I thought about using it for CDN support as well).
This is an old filter / module but it worked well for my needs with a little tuning: http://www.paraesthesia.com/archive/2007/12/14/urlabsolutifiermodule---convert-urls-in-asp.net-output-to-absolute.aspx
What you can do is make a response filter and hook it with an httpmodule (as this absolutifier does). If you use this module + response filter you could probably achieve what you need by modifying the source for it to replace the hostname / prefix all urls to use the CDN.
I had to solve your problem and another one, that is I do not want to take resources from the CDN during development but only when the website is deployed on the production server.
To solve this I developed an ExpressionBuilder that prepends the CDN URL only in production.
<asp:Image ImageUrl="<%$ CdnUrl:/images/myimage.png %>" runat="server" />
In previous code the CDN URL will be prepended only in production.
namespace IdeaR.Web.Compilation
{
[ExpressionPrefix("CdnUrl")]
public class CdnUrlExpressionBuilder : ExpressionBuilder
{
public static object GetCdnUrl(string expression, Type target, string entry)
{
var retvalue = expression;
var productionUri = new Uri("http://www.myproductionurl.com",
UriKind.Absolute);
var currentUri = HttpContext.Current.Request.Url;
var cdnUrl = "http://cdn.mycdn.com";
// If this is a production website URL
if (currentUri.Scheme == productionUri.Scheme &&
currentUri.Host == productionUri.Host)
retvalue = cdnUrl + expression;
return retvalue;
}
public override CodeExpression GetCodeExpression(BoundPropertyEntry entry,
object parsedData, ExpressionBuilderContext context)
{
var componentType = entry.DeclaringType;
var expressionArray = new CodeExpression[3]
{
new CodePrimitiveExpression(entry.Expression.Trim()),
new CodeTypeOfExpression(componentType),
new CodePrimitiveExpression(entry.Name)
};
var descriptor = TypeDescriptor.GetProperties(componentType)
[entry.PropertyInfo.Name];
return new CodeCastExpression(descriptor.PropertyType,
new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression(GetType()),
"GetCdnUrl", expressionArray));
}
}
}
For more information I wrote an article on this
How to use a CDN in production but not during development

Categories