Problem generating Swagger definitions using custom naming strategy - c#

I have an OData Web API and I'm using Swashbuckle and Swagger to generate and display Web API documentation. This was working and producing nice definition names, like so:
User
Role
Permission
ODataResponse[List[Permission]]
CoordinateSystem
Etc...
I ran into an issue with "Conflicting schemaIds" after adding some new objects and controllers and started getting this error:
System.InvalidOperationException: 'Conflicting schemaIds: Duplicate schemaIds detected for types Microsoft.Spatial.CoordinateSystem and MyProject.CoordinateSystem. See the config setting - "UseFullTypeNameInSchemaIds" for a potential workaround'
As the error states, I added the following line in SwaggerConfig.cs
c.UseFullTypeNameInSchemaIds();
This fixed the error but caused some issues with my definition names. Now they show up like this:
MyProject.User
MyProject.Role
MyProject.Permission
Swashbuckle.OData.ODataResponse[System.Collections.Generic.List[MyProject.Permission]]
MyProject.CoordinateSystem
Microsoft.Spatial.CoordinateSystem
The third last one is problematic now as it is too long for the app that reads this information.
Next, I tried using the custom strategy example provided in SwaggerConfig.cs to rename one of the conflicting definition names:
c.SchemaId(t => t.FullName.Contains("Microsoft.Spatial.CoordinateSystem")
? t.FullName : t.Name);
This works for the conflicting definitions as it uses the full name for "Microsoft.Spatial.CoordinateSystem" and I get this now:
User
Role
Permission
ODataResponse`1
CoordinateSystem
Microsoft.Spatial.CoordinateSystem
The problem now is "ODataResponse`1". I'm expecting "ODataResponse[List[Permission]]" but it is displaying the name of the Swashbuckle.OData definition only. There are other options - I tried t.FullName, t.Name, t.AssemblyQualifiedName, t.Namespace but none produce the desired results.
I did come up with a solution that works using the custom naming strategy but it looks horrible and is super hacky:
c.SchemaId(t => t.FullName.Contains("System.Collections.Generic.List") ?
"ODataResponse[List[" +
t.FullName.Substring(t.FullName.IndexOf("MyProject.") + 10, (t.FullName.IndexOf(",") - 10 - t.FullName.IndexOf("MyProject.")))
+ "]]"
: (t.FullName.Contains("Microsoft.Spatial.CoordinateSystem") ? t.FullName : t.Name));
Does this seem to be a bug within Swagger/Swashbuckle or am I doing something wrong with the c.SchemaId code?

Related

Fluxor Blazor error on assembly scanning and registration

I've followed the steps of setting up a Fluxor library.
Added
var currentAssembly = typeof(Program).Assembly; builder.Services.AddFluxor(options => options.ScanAssemblies(currentAssembly));
into the Program.cs. Then added <Fluxor.Blazor.Web.StoreInitializer/> into App.Razor
Stater is decorated with [FeatureState] attribute. Everything according to documention. But the application fails to start with the message :
ArgumentException: An item with the same key has already been added. Key: Store.Account.AccountTopUpState
Seems like the problem exists in Fluxor assembly scanning ...
Are you also creating a class deriving from Feature<AccountTopUpState>? If so, remove the [FeatureState] attribute. I believe the attribute was added as an alternative to the Feature base class approach, and you should choose one approach or the other, but not both.

Swagger showing error section on API UI Screen

I have an error on the Swagger API documentation/List page. All the calls work properly its just that the image detailing error is not a comfortable look. When I drill into the error I receive the following json response:
{"schemaValidationMessages":[{"level":"error","domain":"validation","keyword":"minItems","message":"array is too short: must have at least 1 elements but instance has 0 elements","schema":{"loadingURI":"http://json-schema.org/draft-04/schema#","pointer":"/properties/enum"},"instance":{"pointer":"/definitions/Permissions/properties/permissionType/enum"}}]}
Has anyone come across this issue?
Swagger will always validate your schema against their online validator. In your case it has failed validation because of the permissionType enum having no elements, as per the JSON returned. If you really want to and fixing the issue is not viable then you can turn the validation off using Swagger's validatorUrl parameter - see this link. If you wish to do this via Swashbuckle you could use:
.EnableSwaggerUi(c =>
{
// Your other config...
c.DisableValidator();
});
as per the Swashbuckle documentation

How can I make url path in Swashbuckle/Swaggerwork when api is served from inside another project?

all. I am trying to document a WebApi 2 using Swashbuckle package.
All works great if the API is running by itself i.e. localhost/api/swagger brings me to ui and localhost/api/swagger/docs/v1 to json.
However the producation app initializes this same Webapi project by running webapiconfig method of this project from global.asax.cs in another - now web project (the main application one). So the api url looks like localhost/web/api instead of localhost/api.
Now swashbuckle doesn't work like that at all.
localhost/api/swagger generates error cannot load
'API.WebApiApplication', well of course
localhost/web/swagger = 404
localhost/web/api/swagger = 404
I tried to look everywhere, but all I found is workaround.
c.RootUrl(req => req.RequestUri.GetLeftPart(UriPartial.Authority) + VirtualPathUtility.ToAbsolute("~/").TrimEnd('/'));
Unfortunately it doesn't work, now maybe it should and I just need to change something but I don't even know what exactly this property expects and what it should be set to.
May be it's not even applicable - maybe setup we have requires something else or some swashbuckle code changes.
I will appreciate any help you can provide. I really starting to like swagger (and swashbuckle) for rest documentation.
For Swashbuckle 5.x:
This appears to be set by an extension method of httpConfiguration called EnableSwagger. Swashbuckle 5.x migration readme notes that this replaces SwaggerSpecConfig. SwaggerDocConfig RootUrl() specifically replaces ResolveBasePathUsing() from 4.x.
This practically works the same as it did before, looks like the biggest change was that it was renamed and moved into SwaggerDocConfig:
public void RootUrl(Func<HttpRequestMessage, string> rootUrlResolver)
An example from the readme, tweaked for brevity:
string myCustomBasePath = #"http://mycustombasepath.com";
httpConfiguration
.EnableSwagger(c =>
{
c.RootUrl(req => myCustomBasePath);
// The rest of your additional metadata goes here
});
For Swashbuckle 4.x:
Use SwaggerSpecConfig ResolveBasePathUsing and have your lambda read your known endpoint.
ResolveBasePathUsing:
public SwaggerSpecConfig ResolveBasePathUsing(Func<HttpRequestMessage, string> basePathResolver);
My API is behind a load balancer and this was a helpful workaround to providing a base address. Here's a dumb example to use ResolveBasePathUsing to resolve the path with a known base path.
string myCustomBasePath = #"http://mycustombasepath.com";
SwaggerSpecConfig.Customize(c =>
{
c.ResolveBasePathUsing((req) => myCustomBasePath);
}
I hardcoded the endpoint for clarity, but you can define it anywhere. You can even use the request object to attempt to cleanup your request uri to point to /web/api instead of /api.
The developer commented on this workaround on GitHub last year:
The lambda takes the current HttpRequest (i.e. the request for a given
Swagger ApiDeclaration) and should return a string to be used as the
baseUrl for your Api. For load-balanced apps, this should return the load-balancer path.
The default implementation is as follows:
(req) => req.RequestUri.GetLeftPart(UriPartial.Authority) + req.GetConfiguration().VirtualPathRoot.TrimEnd('/');
...
Re relative paths, the Swagger spec requires absolute paths because
the URL at which the Swagger is being served need not be the URL of
the actual API.
...
The lambda is passed a HttpRequestMessage instance ... you should be able to use this to get at the RequestUri etc. Another option, you could just place the host name in your web.config and have the lambda just read it from there.

ODataClient MaxProtocolVersion V3

I am trying to consume OData from a windows forms. So, what i have done to now is create a new project, i added a web service reference to the OData service and try to consume it.
My code is:
var VistaEntities = new VrExternalEntities("serviceURI");
var query = VistaEntities.VRtblCinemaType
.Where(
x =>
x.VRtblCinema_Operators
.Any
(
z =>
z.VRtblSessions
.Any
(
y =>
y.Session_dtmDate_Time > DateTime.Now
)
)
)
.Select
(
x =>
new
{
x.CinType_strCode,
x.CinType_strDescription
}
);
If i remove the Where clause it works. If i do it says that Any is not supported. I know i have to set MaxProtocolVersion to V3 but i do not know how to do it. I don't have an entity context or anything else. I only have what i have stated above.
Please provide steps on how to accomplish that.
Thanks in advance.
Giannis
You must retrieve the configuration of your DataService and set the MaxProtocolVersion of its behavior to V3.
The best place to do this is certainly in the InitializeService static method you can define in your service class, which will be given the proper configuration object as its config parameter by the environment. It will only be invoked once, typically at the first request.
Note #1: You need WCF Data Services 5.0 or greater. The best way to get it is probably via the Server NuGet package.
Note #2: Oddly enough, the DataServiceProtocolVersion type, although in the Common namespace, is included in the Client assembly (Microsoft.Data.Services.Client, provided by the Client NuGet package). I suggested a better organization here.
public class Vista : DataService<VistaContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule(...);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
...
}
}
Update:
The client may indeed specify the desired version in the requests by using the DataServiceVersion HTTP header. It's currently recommended that you specify and support a range of versions using the MinDataServiceVersion and MaxDataServiceVersion headers if you can, for obvious reasons. Note however that the MinDataServiceVersion will be removed in OData 4.0 (see appendix E.1 of part 1 and "What's new" documents drafts).
The relevant documentation for the WCF Data Services 5.x implementation is available here. The documentation specific to the client seems pretty scarce, but looking at the reference you can see that you must use this constructor for the DataServiceContext to specify the maximum protocol version, and it looks like you cannot change it at any one point for subsequent requests without rebuilding a new context. You may attempt to fiddle with the headers directly, but I wouldn't expect it to work reliably (or at all).
So, to answer your question, you really need control over how you create the context for the client.

Where clause in oData

Is it possible to use this code in OData?
IQueryable<CallLogInfo> CallLogInfos = _callCenterServiceAccessor.CallLogInfos.Where(x => x.LogId == logid);
var log = CallLogInfos.ToList();
return log.Any();
I checked my request that it generated and I saw this:
http://services/CallCenter/CallCenterDataService.svc/CallLogInfos(1364974501.4)
so get this error:
<m:message xml:lang="en-US">Resource not found for the segment 'CallLogInfo'.</m:message>
but when I manually make a request to this request url:
http://services/CallCenter/CallCenterDataService.svc/CallLogInfos
Its ok.
As far as I know a request like your code should be possible.
Assuming that LogId is the Key column of you OData Service, your code
CallLogInfos.Where(x => x.LogId == logid);
will be transformed internally by the OData Service to
http://services/CallCenter/CallCenterDataService.svc/CallLogInfos(logid)
which is the standard syntax to get an element with a specific Id.
The error message you showed is thrown, if a query for an Id does not find an entry in the list, have you checked if the Id you provide is correct?
(If that is the problem, you can turn off this behavior by setting the IgnoreResourceNotFoundException Property of the service context (see MSDN))
In a test that I made, a query like yours does work, maybe your Odata service implementation contains errors?
You can try your code and your service with a tool like LinqPad which is helpful to try out things like that.

Categories

Resources