I'm working with a ASP.NET Core Web Api project that uses OData for the exposed endpoints and are consumed with a Simple.OData.Client.
Some of my endpoints are:
http://{baseUrl}/odata/Vehicle --> this works perfectly
But I'm having issues with these two:
http://{baseUrl}/odata/Vehicle/Brand
http://{baseUrl}/odata/Vehicle/Type
Basicly, I can't modify my EDM Models for modifying the URL property that is exposed in the metadata of OData. My EDM looks like this:
private IEdmModel GetEdmModel()
{
var odataBuilder = new ODataConventionModelBuilder();
odataBuilder.EntitySet<Vehicle>("Vehicle");
odataBuilder.EntitySet<VehicleType>("VehicleType");
odataBuilder.EntitySet<VehicleBrand>("VehicleBrand");
return odataBuilder.GetEdmModel();
}
And the metadata that I get when I navigate through http://{baseUrl}/odata/ is the following:
{
"#odata.context": "https://localhost:44332/odata/$metadata",
"value": [
{
"name": "Vehicle",
"kind": "EntitySet",
"url": "Vehicle"
},
{
"name": "VehicleType",
"kind": "EntitySet",
"url": "VehicleType"
},
{
"name": "VehicleBrand",
"kind": "EntitySet",
"url": "VehicleBrand"
}
]
}
I couldn't find a way to maintain the name as it is, but modify the "url" property shown on the JSON to point to my proper endpoint. I want this result:
{
"name": "VehicleBrand",
"kind": "EntitySet",
"url": "Vehicle/Brand"
}
Any of the methods exposed on EntitySetConfiguration or ODataConventionModelBuilder seems to have a way to specify a different URI for a registered entity type.
Someone has faced this issue? I'm sure that might be some way of solving this.
Odata Route or Navigation Property?
Please have a look at that documentation here
Long story short - an OData URI consists of:
The service root
The OData path
Query options
For example. This is a Path that goes to the EntitySet "Products", takes the first, and then Navigates (see Navigation Properties) to its Supplier.
https://example.com/odata/Products(1)/Supplier?$top=2
------------base---------|-----Path-----------?---options---
So, everything you make accessible at root level should have its own path, and the / telling Odata to navigate onward from there.
So, now for OData, it would freak the hell out of most clients and surely be bad style if you would define an entitysets path as something that can be confused with another entititysets navigation property.
But if you REALLY need to do it, maybe you can achieve it by defining a custom routing convention.
But dont! It will only make trouble
Do you want a navigation property?
If you want the set that "Type" returns to be dependent on the Vehicle, you should define a navigation property on Vehicle instead.
Greetings, Mike
I have a .NET Core 2.0 application, using Swashbuckle/Swagger to generate API documentation. When we were on 2.1.0-preview, Swagger was working fine. Then we did the big upgrade to 2.1.0 release and SDK 2.1.300. We didn't notice exactly when things broke, but now our Swagger docs won't load. Here's what we see:
Project has a reference to Swashbuckle.AspNetCore version 2.5.0. The relevant code in Startup.cs is below. In ConfigureServices():
services.AddSwaggerGen(swaggerOptions =>
{
// Register a swagger doc
swaggerOptions.SwaggerDoc("v1", new Info
{
// Optional descriptive info that will be included in the Swagger output
Contact = new Contact
{
Name = "LightSail",
Url = "https://myurl.com/"
},
Description = "A description of the API can go here",
Title = "My API",
Version = "v1"
});
// Xml file to get comment information from
swaggerOptions.IncludeXmlComments("App_Data/Api.xml");
});
And in Configure():
app.UseSwagger();
app.UseSwaggerUI(swaggerUiOptions => swaggerUiOptions.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1"));
I found lots of other similar questions, one of which suggested that there might be duplicate endpoints; I tried adding a call to .ResolveConflictingEndpoints() but that made no difference. I have searched through my project folders and there is no file called swagger.json, so I'm guessing that's the problem.
Any ideas why this is not working, or how to fix?
This is usually indicative of controllers/actions that Swashbuckle doesn't support for one reason or another.
It's expected that you don't have a swagger.json file in your project. Swashbuckle creates and serves that dynamically using ASP.NET Core's ApiExplorer APIs. What's probably happening here is that Swashbuckle is unable to generate Swagger.json and, therefore, the UI is failing to display.
As HelderSepu said, it's hard to know exactly what caused the failure, so the best way to debug is probably just to remove half your controllers (just move the files to a temporary location) and check whether the issues persists. Then you'll know which half of your controllers contains the troublesome action. You can 'binary search' removing controllers (and then actions) until you figure out which action method is causing Swashbuckle to not be able to generate Swagger.json. Once you know that, it should be obvious whether this is some issue in your code or an issue that should be filed in the Swashbuckle repo.
For example, Swashbuckle appears to not support open generics, so having a response type attribute like [ResponseType(typeof(IEnumerable<>))] could cause this sort of behavior. It could also be an issue with ambiguous routes or something like that tripping Swashbuckle up. Once you've narrowed down the cause of failure to something more specific like that, it can either be fixed or filed, as appropriate.
Today I found out that I could just go to the json url in the browser and get some error information
for example
myapiurl/api/vi/swagger.json
I was able to solve this error by explicitly adding the http verb attribute to my asp.net core 2.x controller method. The convention of prefixing the method name with the http verb is not enough for Swashbuckle apparently.
[HttpPost]
public async Task<IActionResult> AddNewData([FromBody] MyType myType) { … }
In my case I can reproduce your error by omitting "." from the end point as you have done.
I don't get the error if I include "." at the start of the path.
Here is more of my code in case it is relevant.
In ConfigureServices I have
services.AddSwaggerGen(c =>
{
c.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "My API",
Description = "ASP.NET Core Web API",
TermsOfService = "None",
Contact = new Contact
{
Name = "my name",
Email = "me#myemail.com"
}
});
});
In configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRewriter(new RewriteOptions()
.AddRedirectToHttpsPermanent());
app.UseSwagger(c =>
{
c.RouteTemplate =
"api-docs/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
//Include virtual directory if site is configured so
c.RoutePrefix = "api-docs";
c.SwaggerEndpoint("./v1/swagger.json", "Api v1");
});
app.UseMvc(routes =>
{
routes.MapRoute(
"default",
"{controller=Home}/{action=Index}/{id?}");
});
Also there is
public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);
if (isAuthorized && !allowAnonymous)
{
if (operation.Parameters == null)
operation.Parameters = new List<IParameter>();
operation.Parameters.Add(new NonBodyParameter
{
Name = "Authorization",
In = "header",
Description = "access token",
Required = true,
Type = "string"
});
}
}
My dependencies are
Microsoft.AspNetCore.App (2.1.0)
Swashbuckle.AspNetCore (2.5.0)
Microsoft.NETCore.App (2.1.0)
Personally I was a bit quick and forgot to add this line to the method ConfigureServices in Startup.cs.
services.AddSwaggerDocument();
In my case, I missed the 'HttpAttribute':
public async Task<IEnumerable<ClientesListDto>> GetAll()
{
return await _service.GetAllAsync();
}
Then I put it and swagger likes it:
[HttpGet]
public async Task<IEnumerable<ClientesListDto>> GetAll()
{
return await _service.GetAllAsync();
}
In my case, I had this:
[HttpGet("CleanUpSnoozedLeads")]
public async Task<ActionResult<bool>> CleanUpSnoozedLeads()
[HttpGet("CleanUpSnoozedLeads")]
public async Task<ActionResult<bool>> DoSomethingElse()
Notice the HttpGet() had the same name. That causes the undefined error as well.
A very common case is ambiguity. Just use the same signature for two PUT or POST operations for example and you will get the error.
Others answers did not worked for me.
I was able to fix and understand my issue when I tried to go to the swagger.json URL location:
https://localhost:XXXXX/swagger/v1/swagger.json
The page will show the error and reason why it is not found.
In my case, I saw that there was a misconfigured XML definition of one of my methods based on the error it returned:
NotSupportedException: HTTP method "GET" & path "api/Values/{id}" overloaded by actions - ...
...
...
In my case, i just forgot to add the HttpPostAttribute annotation to the method.
[HttpPost]
public ActionResult Post()
{
return Ok();
}
In my case there was a conflict in the schemaId. Apparently every class in the swagger JSON must have a unique schemaId. If you have two classes in different namespaces with the same name this will not work. We have to configure "UseFullTypeNameInSchemaIds" in the startup class.
Add "options.CustomSchemaIds(x => x.FullName);" in "services.AddSwaggerGen"
I found the trace by enabling Output window in VS, selecting the main project from Show output from dropdown list then visit http://{yourapiendpoint}/swagger/v1/swagger.json
If your api have same two or more [HttpGet] its not working swagger.
You should be specify [HttpGet] , [HttpGet ("{id}")]
simple solution
In my repository class, I have my Config object and looks like my connection string is under:
Config > Providers > Microsoft.Configuration.Json.JsonConfigurationProvider > Data > ConnectionStrings.myConnectionString
This is what my appsettings.json looks like:
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": {
"myConnectionString": details..."
}
}
I'm trying to read myConnectionString as follows which is not working:
var cs = _config.GetSection("ConnectionStrings.myConnectionString").value;
What am I doing wrong?
UPDATE:
For some reason, I'm not seeing GetValue() method. I'm using ASP.NET Core 2.0.
Configuration API provides extension method for IConfiguration to simplify reading ConnectionStrings section:
// using Microsoft.Extensions.Configuration;
string connectionString = _config.GetConnectionString("myConnectionString");
what it does is return configuration?.GetSection("ConnectionStrings")?[name];
The issue seems to lie in the string path you are passing to the GetSection() method. According to the ASP.NET Core Configuration documentation you should use "ConnectionStrings:myConnectionString" instead of "ConnectionStrings.myConnectionString".
Plus, if you wish to retrieve the value directly you may prefer to use the GetValue() method instead:
var cs = _config.GetValue("ConnectionStrings:myConnectionString", "");
If you prefer, you can also use the index notation as:
var cs = _config["ConnectionStrings:myConnectionString"];
But I honestly find the first approach more clean and elegant as the GetValue() method allows you to specify a default value in case the property is not found in the configuration.
Does ASP.NET Core 1.0 support the use of APIExplorer? I'm unable to find any docs on it or how to use it, has anyone used it and can share some insight?
Itay's response helped me a bit getting the answer I wanted.
To anyone else that needs to use the ApiExplorer, Dr Rob Lang wrote an answer to How to get a list of all routes in ASP.NET Core?.
In brief, to get the routes you can have the IApiDescriptionGroupCollectionProvider injected into your controller using constructor injection. You then receive the routes in ApiDescriptionGroupCollectionProvider.ApiDescriptionGroups.Items. The routes will only be visible if you mark them as visible to ApiExplorer. This can be done per controller or by using a convention. Since I want to use it on all of my controllers, I used an IApplicationModelConvention:
public class ApiExplorerVisibilityEnabledConvention : IApplicationModelConvention
{
public void Apply(ApplicationModel application)
{
foreach (var controller in application.Controllers)
{
if (controller.ApiExplorer.IsVisible == null)
{
controller.ApiExplorer.IsVisible = true;
controller.ApiExplorer.GroupName = controller.ControllerName;
}
}
}
}
Then in Startup.cs, you add the convention:
public void ConfigureServices(IServiceCollection services)
{
// other calls omitted for brevity
services.AddMvc(opt =>
{
opt.Conventions.Add(new ApiExplorerVisibilityEnabledConvention());
});
}
Code from How to get a list of all routes in ASP.NET Core? - Dr Rob Lang, Mar 2 '16 at 14:40
There's a downloadable NuGet of the ApiExplorer for ASP.NET Core: Microsoft.AspNetCore.Mvc.ApiExplorer 1.0.0
So this means that it's supported (used by Swagger/Swashbackle which are also supported AFAIK).
I'm trying to get an example of EF 7 with Azure Table Storage to work in VS 14 CTP3, but I am having no luck with the dependency injection stuff. I was able to get an example with SQL done fairly easily, but I am seeing an issue that doesn't make sense: The referenced package is there and being pulled in, and if I look at it, it contains the correct namespaces, methods, clases etc., but the compile doesn't like it.
Here is my project.json:
{
"dependencies": {
"Microsoft.AspNet.Server.IIS": "1.0.0-alpha3",
"EntityFramework.AzureTableStorage": "7.0.0-alpha3",
"Microsoft.AspNet.RequestContainer": "1.0.0-alpha3"
},
"frameworks" : {
"net451" : { },
"k10" : { }
}
}
using System;
using Microsoft.AspNet.Builder;
using Microsoft.Data.Entity; /* <- 'missing reference' unless I add EntityFramework to project.json */
using Microsoft.Data.Entity.AzureTableStorage; /* <- ALWAYS errors w/ 'missing reference' */
using Microsoft.Framework.DependencyInjection;
namespace WebApplication2
{
public class Startup
{
public void Configure(IBuilder app)
{
app.UseServices(services =>
{
services.AddEntityFramework() /* <-- this errors too */
.AddAzureTableStorage();
services.SetupOptions<DbContextOptions> //,- says it can't find this
(config => config.UseAzureTableStorage("UseDevelopmentStorage=true"));
});
}
}
}
The strange thing is, if I right click and 'go to definition' on any of the 'missing' classes or methods, it brings them up, and I can see that I'm using them as defined. Am I missing something terribly obvious? Or is this stuff just not fully cooked yet?
Your project.json has both frameworks mentioned so VS builds both of them. If your intention is to just build for net451, you should remove the following from your project.json -
"k10" : { }