A malicious or naive client may construct a query that consumes excessive resources. Such a query can disrupt access to your service. Review Security Guidance for ASP.NET Core Web API OData before starting this tutorial.
Enable OData
Update Startup.cs with the following highlighted code:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<SchoolContext>(options =>
options.UseInMemoryDatabase("OData-expand"));
services.AddMvc(option => option.EnableEndpointRouting = false);
services.AddOData();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc(routeBuilder =>
{
routeBuilder.EnableDependencyInjection();
routeBuilder.Expand().Select();
});
}
}
The preceding code:
Calls services.AddOData(); to enable OData middleware.
Calls routeBuilder.Expand().Select() to enable querying related entities with OData.
Add a controller
Create new Controller named EnrollmentController and with the following action:
[HttpGet, EnableQuery]
public IQueryable<Enrollment> Get([FromServices]SchoolContext context)
=> context.Enrollment;
The preceding code enables OData queries and returns enrollment entities SchoolContext.
$expand
OData expand functionality can be used to query related data. For example, to get the Course data for each Enrollment entity, include ?$expand=course at the end of the request path:
Set the request URL to https://localhost:5001/api/Enrollment/?$expand=course($expand=Department). Change the port as necessary.
Select Send.
The Course data for each Enrollment entity is included in the response.
Expand depth
Expand can be applied to more than one level of navigation property. For example, to get the Department data of each Course for each Enrollment entity, include ?$expand=course($expand=Department) at the end of the request path. The following JSON shows a portion of the output:
By default, Web API allows the maximum expansion depth of two. To override the default, set the MaxExpansionDepth property on the [EnableQuery] attribute.
Security concerns
Consider disallowing expand:
On sensitive data for security reasons.
On non-trivial data sets for performance reasons.
In this section, code is added to prevent querying CourseAssignments related data.
Override SelectExpandQueryValidator to prevent $expand=CourseAssignments. Create a new class named MyExpandValidator with the following code:
public class MyExpandValidator : SelectExpandQueryValidator
{
public MyExpandValidator(DefaultQuerySettings defaultQuerySettings)
: base(defaultQuerySettings)
{
}
public override void Validate(SelectExpandQueryOption selectExpandQueryOption,
ODataValidationSettings validationSettings)
{
if (selectExpandQueryOption.RawExpand.Contains(nameof(Course.CourseAssignments)))
{
throw new ODataException(
$"Query on {nameof(Course.CourseAssignments)} not allowed");
}
base.Validate(selectExpandQueryOption, validationSettings);
}
}
The preceding code throws an exception if $expand is used with CourseAssignments.
Create a new class named MyEnableQueryAttribute with the following code:
public class MyEnableQueryAttribute : EnableQueryAttribute
{
private readonly DefaultQuerySettings defaultQuerySettings;
public MyEnableQueryAttribute()
{
this.defaultQuerySettings = new DefaultQuerySettings();
this.defaultQuerySettings.EnableExpand = true;
this.defaultQuerySettings.EnableSelect = true;
}
public override void ValidateQuery(HttpRequest request, ODataQueryOptions queryOpts)
{
queryOpts.SelectExpand.Validator =
new MyExpandValidator(this.defaultQuerySettings);
base.ValidateQuery(request, queryOpts);
}
}
The preceding code creates the MyEnableQuery attribute. The MyEnableQuery attribute adds the MyExpandValidator, which prevents $expand=CourseAssignments
Replace the EnableQuery attribute with MyEnableQuery attribute in the EnrollmentController:
[HttpGet, MyEnableQuery]
public IQueryable<Enrollment> Get([FromServices]SchoolContext context)
=> context.Enrollment;
In the endpoint testing tool:
Send the previous Get request https://localhost:5001/api/Enrollment/?$expand=course($expand=Department). The request returns data because ($expand=Department) is not prohibited.
Send a Get request for with ($expand=CourseAssignments). For example, https://localhost:5001/api/Enrollment/?$expand=course($expand=CourseAssignments)
Connect to your finance and operations apps data by using data entities, where data is accessed outside of the application and with different endpoint and external applications.