how to apply odataqueryoptions against odata endpoint? - c#

CRM 2016 provides an odata endpoint such as:
https://mycrmorg.com/api/data/v8.1/
And it allows you to apply odata filters on it such as:
https://mycrmorg.com/api/data/v8.1/accounts(8308AD1C-1B1A-E711-941B-00155DC0D345)
If I have a controller such as :
class AccountsController
{
public IHttpActionResult Get(ODataQueryOptions options)
{
var endPoint = #"https://mycrmorg.com/api/data/v8.1/";
//how do we apply the odata query options here??
}
}
how do we apply the odata query options against this endpoint??

I understand that you simply want to get all the ODATA query options from your ODataQueryOptions object. You can get it from RequestUri:
class AccountsController
{
public IHttpActionResult Get(ODataQueryOptions options)
{
var stringOptions = options.Request.RequestUri.PathAndQuery;
var endPoint = #"https://mycrmorg.com/api/data/v8.1";
var endPointAndQuery = endPoint + stringOptions;
//call Odata
}
}
stringOptions will contain for example something like this: "/accounts?$filter=accountnumber eq '1234'" which you can simply append to your endpoint and call other odata api.

Related

Hide action parameters with [BindNever] attribute from swagger

I want to hide parameters with the BindNever attribute in actions from SwaggerUI.
My action:
[HttpGet("{id:int}")]
[TryGetUserByIdValidation(GetAsUserReadDto = true, UserArgumentName = "userDto")]
public virtual ActionResult<ApiResult<User>> Get(int id,
[BindNever] UserReadDto userDto)
{
var res = new ApiResult<UserReadDto>()
.WithData(userDto);
return Ok(res);
}
In SwaggerUI I can pass the [BindNever] UserReadDto userDto as JSON in the request body, but the method is GET so I get an error in swaggerUI.
Why do I have a BindNever parameter in my action? I'm filling it with TryGetUserByIdValidation ActionFilter which is above my action. It handles all the mappings and errors. I'm preventing binding for that parameter with BindNever but it is not fully compatible with API so sometimes I have to use it with FromForm or any From... attribute except FromBody.
I have tried this OperationFilter.
public class SwaggerExcludeBindNeverFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var paramsToHide = context.MethodInfo.GetParameters()
.Where(para => para.CustomAttributes.Any(att => att.AttributeType == typeof(BindNeverAttribute)))
.ToList();
if (paramsToHide.Any())
{
// What to do now?
operation.RequestBody.Content.Clear();
}
}
}
And add to swagger:
options.OperationFilter<SwaggerExcludeBindNeverFilter>();
But this code removes all RequestBody.Content and as I said it can be in Form or anywhere else.
Swagger should not send or even show this.
You can tell me which attribute should I use to prevent Binding and show it in swagger.
I'm using Asp.Net Core 5.
In the case that I can't add a service to HttpContext.RequestServices after a request was made and get it in action parameter with [FromServices] attribute (error: this service is not registered).
So I pass the data with the HttpContext.Items[] dictionary.
In TryGetUserByIdValidation ActionFilter:
// pervious code
// context.ActionArguments[UserArgumentName] = user;
context.HttpContext.Items[ItemKey] = user; // < set the item
Action:
[HttpGet("{id:int}")]
[TryGetUserByIdValidation(GetAsUserReadDto = true, ItemKey = "userDto")]
public virtual ActionResult<ApiResult<User>> Get(int id)
{
UserReadDto userDto = HttpContext.Items[nameof(userDto)] as UserReadDto; // < get the item
var res = new ApiResult<UserReadDto>()
.WithData(userDto);
return Ok(res);
}
This is just a whitewash answer, it works for me. It does not have anything to do with Swagger so please post an answer to hide [BindNever] action parameters. To accept that.

Pre-filtering OData using an URL param (

I'm using odata with web API on service fabric.
I would like to do some per-filtering using a parameter in the URL before to return the IQuerable. Probably that means that I'm mixing the API routing with the odata routing. A not-working example, just to give an idea of what I'm trying to achieve:
[ResponseType(typeof(IQuerable<Something>))]
[HttpGet, Route("Something/{id:guid}/SomeFiltrableList")]
public async Task<IHttpActionResult> GetBySomethingId(Guid id)
{
var someFiltrableList= await _repository.GetSomething(id);
return Ok(someFiltrableList.AsQueryable());
}
Would it be possible to do something like this? How?
Thanks
Add System.Web.OData.Query.ODataQueryOptions parameter and apply it to the data source:
[ResponseType(typeof(IQueryable<Something>))]
[System.Web.Http.HttpGet, System.Web.Http.Route("Something/{id:guid}/SomeFiltrableList")]
public async Task<IQueryable<Something>> Get(Guid id, ODataQueryOptions options)
{
var someFiltrableList = await _repository.GetSomething(id);
return options
.ApplyTo(someFiltrableList)
.Cast<Something>()
.AsQueryable();
}

AngularJS $http and WebApi 2

I have an issues which has just arisen.
I have a Web API that has this method:
[HttpGet]
[Route("")]
public IHttpActionResult Get(OrderRequestViewModel model)
{
var order = new orderHeader()
{
sordNo = model.OrderNumber,
process = orderHeader.orderProcesses.Read
};
order.processInfos.read_CoreHistory = model.ReadCoreHistory;
var response = this.Connection.webServicesAdvanced.ordersHubAction_S(this.SecurityToken, order);
switch (response.state)
{
case processorResult.resultEnum.failed:
case processorResult.resultEnum.validationfailed:
throw new HttpException(500, response.info);
default:
return Ok(new OrderResponseViewModel(response.obj));
}
}
The OrderRequestViewModel only has 2 properties: OrderNumber (string) and ReadCoreHistory (boolean).
In my Angular JS application I have a service which looks like this:
.factory('OrderService', ['$http', '$filter', 'apiUrl', function ($http, $filter, api) {
var _get = function (orderNumber, readCoreHistory) {
return $http.get(api + 'orders?orderNumber=' + orderNumber + '&readCoreHistory=' + readCoreHistory);
}
//-- removed for brevity --//
var service = {};
service.get = _get;
//-- removed for brevity --//
return service;
}])
When I call the get() method from my controller, if I have fiddler open it states:
No MediaTypeFormatter is available to read an object of type 'OrderRequestViewModel' from content with media type 'application/octet-stream'.
Apparently this is a content type header issue, but I have no access to the ajax call options that I can see. Does anyone know how I can solve this?
GET method does not have a body, complex types are filled by data from request body in ASP.NET WebAPI, you should add [FromUri] attribute before your complex type in method signature.
public IHttpActionResult Get([FromUri] OrderRequestViewModel model)
{ ...

Where does WebAPI 2.2 OData v4 [EnableQuery] apply?

Where is it correct/incorrect to apply the EnableQueryAttribute as of Jan 2015?
The document linked below:
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/create-an-odata-v4-endpoint
Says:
The [EnableQuery] attribute enables clients to modify the query, by using query options such as $filter, $sort, and $page. For more information, see Supporting OData Query Options.
The following linked document:
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options
Says:
The EnableQuerySupport method enables query options globally for any controller action that returns an IQueryable type.
But this document for OData 4 on WebApi 2.2 has put it on actions returning IHttpActionResult:
http://blogs.msdn.com/b/webdev/archive/2014/03/13/getting-started-with-asp-net-web-api-2-2-for-odata-v4-0.aspx
[ODataRoutePrefix("Teams")]
public class TeamsEntitySetController : ODataController
{
private readonly LeageContext _leage = new LeageContext();
[EnableQuery]
[ODataRoute]
public IHttpActionResult GetFeed()
{
return Ok(_leage.Teams);
}
[ODataRoute("({id})")]
[EnableQuery]
public IHttpActionResult GetEntity(int id)
{
return Ok(SingleResult.Create<Team>(_leage.Teams.Where(t => t.Id == id)));
}
}
I'm going crazy trying to find up-to-date, accurate and consistent documentation on OData v4 / WebApi 2.2.
Which is correct today?
Use global configuration (instance of an HttpConfiguration object) and call
config.Filters.Add(new EnableQueryAttribute()
{
PageSize = 2
// .. other settings
});
this works

$Metadata with WebAPi OData Attribute Routing Not Working

I'm using OData Attribute Routing for an OData endpoint. Here is an example of what I have:
[ODataRoutePrefix("Profile")]
public class ProfileODataController : ODataController
{
[ODataRoute]
[EnableQuery]
public IHttpActionResult Get()
{
var repo = new Repositories.ProfileRepository();
return Ok(repo.GetProfiles());
}
[ODataRoute("({key})")]
[EnableQuery]
public IHttpActionResult Get([FromODataUri] string key)
{
var repo = new Repositories.ProfileRepository();
var result = repo.GetProfiles().SingleOrDefault(x => x.Id== key);
if (result == null) return NotFound();
return Ok(result);
}
}
Here is my set up:
config.MapODataServiceRoute("odata", "odata", ModelGenerator.GetEdmModel());
Here is my EdmModel Generation:
public static IEdmModel GenerateEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Profile>("Profile").EntityType.HasKey(x => x.Id);
return builder.GetEdmModel();
}
The urls /odata/Profile and /odata/Profile('someid') both work, but when I try to access the $metadata endpoint (/odata/$metadata#Profile), I get the following error:
{"Message":"No HTTP resource was found that matches the request URI 'http://****/odata/$metadata'.","MessageDetail":"No type was found that matches the controller named 'Metadata'."}
Do I need to create a controller/action for serving the metadata? If so, how is that action implemented?
Turns out it had to do with my replacement of the IAssembliesResolver.
I had implemented a custom version to provide only the component assemblies that I had implemented controllers in. However, as the error said, it couldn't find a controller named MetadataController. Turns out, OData implements one: System.Web.OData.MetadataController, which provides for the $metadata keyword.
Since I had implemented my own IAssembliesResolver, the System.Web.OData assembly wasn't being included, and $metadata failed. Once I discovered this, and updated my assembly resolver to explicitly include the OData assembly, it now works as it should.

Categories