Web Api Help Page, multiple Get same parameter break help generator - odata

I have created an OData Controller that exposes three different GET methods.
// http://odata/Companies
IQueryable<Company> Get()
// http://odata/Companies(1)
SingleResult<Company> Get([FromODataUri] key)
// http://odata/Companies(1)/Persons
IQueryable<Persons> GetPersons([FromODataUri] key)
Using this approach the OData Links convention works perfectly but the Help Generator creates only 1 method, the Get(), while it skips Get{key} and GetPersons{key}
If I change the methods in this way:
// http://odata/Companies
IQueryable<Company> Get()
// http://odata/Companies(1)
SingleResult<Company> Get([FromODataUri] key)
// http://odata/Companies(1)/Persons
IQueryable<Persons> GetPersons([FromODataUri] companyKey)
the help is generated but Web Api OData doesn't work anymore cause the framework is looking for a "key" parameter.

If you are using OData v4(System.Web.OData.dll instead of System.Web.Http.OData.dll), you can resolve this issue through attribute routing:
[ODataRoute("Companies({companyKey})/Persons")]
IQueryable<Persons> GetPersons([FromODataUri] companyKey)
For more info about attribute routing, you can reference https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/ODataAttributeRoutingSample/.

Related

How can I find my dotnet mvc Controller's ApiVersion programmatically

I'm using dotnet core 1.1. I have a web api with several controllers, which look like this:
[ApiVersion("1.0")]
[Route("v{version:apiVersion}/mycontroller")]
public class MyController : Controller
{
[HttpGet("action1")]
public string Get()
{ /* actual logic */ }
// More actions ...
}
I'd like to have a special controller which iterates through my controllers and print out the version and the route to them, like this:
MyController -- 1.0 -- /v1.0/mycontroller
MyOtherController -- 1.1 -- /v1.1/myothercontroller
But I can't figure out how to access the data at runtime. Thoughts?
Apologies for being late to the party, but the way to achieve this is to use the complimentary API Explorer package for API Versioning. This package is most commonly used for Swagger generation, but there is no direct coupling or usage of Swagger. You can use the API Explorer to enumerate all your controllers as well as their corresponding versions and routes.
In your scenario, you would integrate the required services with:
services.AddVersionedApiExplorer(
options =>
{
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
}
The controller you want to output the information should declare the IApiDescriptionGroupCollectionProvider and IApiVersionDescriptionProvider services in its constructor. The APIs will be grouped according to the GroupName property in the format specified by the API Explorer.
That should get you started. Let me know if you have more questions.

POST to a related collection in WEB API 2 with OData 4

I want to have a route like:
/Accounts(id)/Orders
where I can execute a POST to create an order. I can't find a way to add this route using OData in WebApi. For GET there is a convention to follow to get related collections, but I am not able to find any convention for posting new entities to a related collection.
Is there a standard way to handle this POST request with Web API 2 and OData 4 ?
Added the following attributes to the method and it worked:
[HttpPost]
[ODataRoute("Accounts({key})/Orders")]
public IHttpActionResult Orders([FromODataUri] string key, OrderDto orderDto)
{
}

OData Web API routing

I have a web API exposing ODATA from a SQL stored proc. I want to use a url like /odata/firmhierarchy(225) to pass 225 into a param for the stored proc. It just tells me that it can't find a matching resource. It hits the controller, just skips the method. Thoughts?
In webapiconfig
private static IEdmModel GenerateEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Employee>("Employees");
builder.EntitySet<Employee>("FirmHierarchy");
return builder.GetEdmModel();
}
Context:
public virtual ObjectResult<Employee> sp_EmployeeHierarchy(Nullable<int> managerEmpID)
{
var managerEmpIDParameter = managerEmpID.HasValue ?
new SqlParameter("ManagerEmpID", managerEmpID) :
new SqlParameter("ManagerEmpID", 0);
return ((IObjectContextAdapter)this).ObjectContext.ExecuteStoreQuery<Employee>("sp_EmployeeHierarchy #ManagerEmpID", managerEmpIDParameter);
}
Only method in controller:
[Queryable]
public IQueryable<Employee> GetFirmHierarchy()
{
return db.sp_EmployeeHierarchy(225).AsQueryable();
//return SingleResult.Create(db.Employees.Where(employee => employee.EmpId == key));
}
This should work:
1.Write another method in your controller:
[EnableQuery]
public IQueryable<Employee> Get([FromODataUri] int key)
{
return db.sp_EmployeeHierarchy(key).AsQueryable();
}
Please note that [EnableQuery] is an attribute introduced in Web API for OData V4. If you are still using Web API for OData V1-3, use [Queryable] still.
2.Then you can send the request
GET /odata/firmhierarchy(225)
and get the employees.
I was able to make ODATA work for a table, when auto-generated from entity framework. However, that generation process didn't want to work for a complex type returned by a Table Valued Function (similar scenario to a SP), because it didn't seem to understand where the key was.
What I found was that I could however make it work. First, I check out this article. He sets things up a bit more manually, where his Get on a companyProcessingController ends up routing for id 3 as "http://localhost:10020/odata/companyProcessing(3)" .
This surprised me. My other generated classes set up the pattern that SomeEntity became SomeEntityController, with methods like GetSomeEntities, and a routing that seemed to me to match the method but dropping the word get. Therefore, dropping the entity name from the Get method name seemed different, but it worked. Proving that the path is actually matching the controller name, not the method name.
In this Case you configure the routing using the data type you're querying for, and the beginning of the controller name. Then the actual path utilizes the beginning of the controller name as well.
And then all of this just brings us essentially to the other posted solution, assuming your controller name is firmhierarchyController
So, now, making sense of this... Try going to http://localhost:55063/odata/$metadata , where your port may differ. You'll notice that ODATA exposes a DataType, which is accessed via a DataSet. When a client tries to query into ODATA, they are trying to query against the DataSet, getting items of the DataType.
The DataSet matching the controller name (less Controller), and the Get methods can indeed just be Get without further extension of the name - and otherwise in this scenario was giving me problems.

EF Reference gets lost when Web API returns IQueryable

I have been looking for quite some time now on this problem.
Here's the deal.
I'm building a website that calls to a Web API to get its data. My Web API uses a library, working with repository pattern. My database model (EF Model-first) was build in the library. In that model I have a base class Pass. Then I have two derived classes, CustomerCard : Pass and Voucher : Pass. My model from EF Designer
I have a method to get all the CustomerCards.
public IQueryable<CustomerCard> GetAllPasses() {
IList<CustomerCard> allCards = new List<CustomerCard>();
var c_cards = context.Passes;
foreach (var c_card in c_cards) {
if (c_card is CustomerCard) {
allCards.Add((CustomerCard)c_card);
}
}
return allCards.AsQueryable<CustomerCard>();
}
In my ApiController, I use this method to get the passes and return them to the website, like this:
[HttpGet]
[Queryable]
public IQueryable<CustomerCard> GetAllPasses(string version) {
return passRepo.GetAllPasses().AsQueryable();
}
My Web API returns JSON format. This is my config to preserve referencing and stuff:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.Objects;
json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.Remove(config.Formatters.XmlFormatter);
I'm using IQueryable because I want to be able to page the data on my website. The api method is available at '/api/v1/passes/all'.
Here's the strange part. To test my paging, I call 1 pass per page.
For my first Pass, it works fine. But when I go to my second page, he also gets the correct pass, but the reference to User is gone.
As you can see in my model, the CustomerCard class has a property User. This indicates who owns the customer card.
So this call loads the user from the pass: 'api/v1/passes/all?$top=1'
but when I call to this one, the user instance is NULL: 'api/v1/passes/all?$top=1&$skip=1'.
However, when I call to 'api/v1/passes/all?$top=2', the User for the second pass IS loaded.
So this is where my mind get's blown! I don't get it? Why doesn't the user-reference comes along with the second one? Could it have something to do with the Lazy loading feature of the EF?
EDIT
When I use the extension method Include on context.passes, an error is thrown:
A specified Include path is not valid. The EntityType 'LCS_Model.Pass'
does not declare a navigation property with the name 'User'.
This is because Passes as a dbset, contains CustomerCard as well as Voucher. Is there a way I can tell my context to expect or convert it to a CustomerCard?
Can someone please help me. If you don't understand my question, ask away!
Thanks allready!
EDIT 2
The method on my API controller is now
[HttpGet]
[Queryable]
public IQueryable<CustomerCard> GetAllPasses(string version) {
return context.Passes.Include("User").OfType<CustomerCard>();
}
This gives me my correct items. I have 2 customer cards in my db. Both are from the same user. My API has the user still loaded. The moment my website receives the response, the User property becomes null. My guess is that it's because it is still referencing to the same user from the first element of the array. Is that possible? And if yes, how can I prevent that from happening?
Yes, you need to make sure any related records are included when you do your query. See this for some examples. Secondly... I fail to understand why you are doing all that work with the for loop... That's a lot of absolutely needless and wasted work for the server to do if you want to do any paging. I'm thinking, besides any other filters you might want to apply, your GetAllPasses should look something like this.
public IQueryable<CustomerCard> GetAllPasses() {
return context.Passes.Include(r => r.User);
}
Edit (2): I need to read better. I have to confess, I'm not familiar with type inheritance in EF. I found some things that might work here: table per hierarchy, table per concrete type, and see also also MSDN Queryable.OfType<TResult>. This is a guess, but let's try:
public IQueryable<CustomerCard> GetAllPasses() {
return context.Passes.OfType<CustomerCard>().Include(r => r.User);
}

Getting Query String parameters in struts2 Action

I have came across an issue where i am unable to find a solution.I am working on a web-application and have to impliment Oauth, things are working fine for me except one issue,in my redirect back URL from Yahoo i am getting several parametersand i need to access few of them in my action class.
Now i can easily create a property in my action class with its getter and setter methods but the name of the property is
openid.response_nonce
and my Eclipse editor will not allow me to name a variable like this.Though one solution is add RequestAware interceptor in my action class and access the parameter.
my Question is can i access it without using RequestAware inteceptor?
There isn't a RequestAware interceptor... There is a Servlet-Config interceptor which will check if your action has one of the following interfaces: ServletContextAware, ServletRequestAware, ServletResponseAware, ParameterAware, RequestAware, SessionAware, ApplicationAware, PrincipalAware.
The Servlet-Config interceptor is part of the default-stack, which you are probably already using. So there is no additional cost or configuration required to use one of the aware interfaces.
That aside, if you have a parameter called "openid.response_nonce" which contains a string, you should be able to refer to it with:
//following not tested, and not checked for syntax errors
private Map openid = new HashMap();
//In Constructor{
oauth.put("response_nonce","");
}
//create BOTH a getter and setter for openid
public getOpenid(){
return openid;
}
public setOpenid(Map openid){
this.openid = openid;
}
Now struts2 should be able to figure out how to set the value... I think, sorry didn't test it. You could always create a class called Openid with a response_nonce property(along with the appropriate getters and setters for that Class)... but I think in this case it might be best to just use RequestAware if you only need that single property.
I think that you maybe looking for the Alias interceptor. http://struts.apache.org/2.0.14/docs/alias-interceptor.html
Regards

Resources