Swagger showing error section on API UI Screen - c#

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

Related

Failed to load resource: the server responded with a status of 404 ()

I am writing a REST API in .net core. I am trying to test the API using Postman and I am getting an error saying
Failed to load resource: the server responded with a status of 404 ()
I know this error occurs when the route does not match. Not sure, what am I doing wrong with the route. Below is my code with the Route at the top:
namespace RecLoad.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class RecLoadPrimeController : ControllerBase
{
[Route("RecLoadPrime/insertRecLoadData/{RecStartDate}/{RecEndDate}")]
[HttpPost]
public void insertRecLoadData(string RecStartDate, string RecEndDate)
{
RecLoadDataProvider dataProvider = new RecLoadDataProvider();
dataProvider.InsertCardsData(RecStartDate, RecEndDate);
}
}
}
The URL that I am trying to test in Postman is below:
https://localhost:44360/api/RecLoadPrime/insertRecLoadData/?RecStartDate=01/01/2020&RecEndDate=01/02/2020
I am very new to API, this is the first API that I am writing. Below is the image for application structure. Its extremely simple:
Any help will be greatly appreciated.
A 404 error means not found. This means Postman cant find the end point you are trying to hit.
Your [Route] attribute needs to be updated. The root of this endpoint (controller) it's RecLoadPrime. So get rid of that part. If you are just trying to test, update it to [Route("insert")].
Using ? in your URL means you are passing query parameters. Which are usually used on GET requests not on POST requests.
Web API expects you to use Model Binding for passing in parameters. Meaning map the post parameters to a strongly typed .NET object, not to single parameters. Alternatively, you can also accept a FormDataCollection parameter on your API method to get a name value collection of all POSTed values.
For example: Create a small class called Card, with the properties startDate, and endDate. Make them DateTime. Now use that in the method signature public void insertRecLoadData([FromBody]Card card)
In Postman, you are now going to use the Body option and create a JSON representation of this new class we created.
For example: { "startDate": "2020-03-23", "endDate": "2020-03-27" }
In the route, you are going to use: POST | https://localhost:44360/api/insertRecLoadData/insert
Make sure you set breakpoints in your controller. Not sure how you have setup your project but I'd suggest reading up more on how to setup a Web API using ASP.NET Core. Look into RESTful design to also get an idea on how to best setup these end points.
Good luck!
The current route configuration on your controller and on your action will result in duplicated section in your route. Specifically, the route the action will be associated with will be "api/RecLoadPrime/RecLoadPrime/insertRecLoadData/{RecStartDate}/{RecEndDate}".
Consider removing the RecLoadPrim/ prefix from your action route attribute as follows:
[Route("insertRecLoadData/{RecStartDate}/{RecEndDate}")]

Problem generating Swagger definitions using custom naming strategy

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?

MVC Ajax.error render Partial View

While working with the Kendo UI Grid for ASP.NET MVC I am finding some frustrating behavior that I can't seem to understand or find any information on. I have a grid which makes a kendoGrid.saveChanges() request to the server which has some server side validation. If the server-side validation fails, it returns the following result:
Response.StatusCode = HTTP_BAD_CLIENT_REQUEST; // const int 400
return PartialView("Error/BadRequest");
The partial view just has some basic HTML for displaying the error information to the user, and the grid's datasource error callback does the following:
function errorCallback(e) {
// Handles explicit Ajax and Kendo DataSource error callbacks
var content = e.responseText || e.xhr.responseText;
// Wrapper around a kendo.ui.Window to display content
Core.Alert.raiseAlert(content);
}
When I run my project in debug mode, or publish the Release version of the project to my local machine the e.xhr.responseText value is populated correctly i.e. it contains the partial view's HTML message. However, as soon as I move this to the production server the e.xhr.responseText simply contains the value "Bad Request" which is also the HTTP status code I am currently using. I've tried using other status codes but the result is the same (error name is used as the responseText).
The reason I find this to be so odd is that I am doing something similar in another project for an internal application at our company and this works perfectly fine. They are running on the same versions and tools of both Kendo and ASP.NET.
Can anyone point me in the direct of tools or documentation, Kendo or AJAX, which explain why the response text is not using my partial view result or how I can map the partial view result I am sending to the xhr.responseText property?
Edit: With trying different error codes, I found that certain status codes such 405 (Not Allowed) resulted in the IIS server error html being returned. So now I'm really stumped, why is it that some status codes simply return a name of the request, while others return templated HTML for that error code when I am specifying the return value and view to return?
Credit for findings and solutions goes to this post.
After doing some digging, I figured out that the issue came down to IIS overriding the content (and therefore partial view) being sent when I used the error HTTP status codes.
The solution is to add an <httpErrors> tag to the system.webServer in the web.config. I found that the following was sufficient to get my partial views to reach the client.
<system.webServer>
<httpErrors existingResponse="PassThrough" />
</system.webServer>

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.

ajax submit to WebAPI controller

I'm not sure of the best way to accomplish my goal. Looking for insight. I'm familiar with WebAPI services consumed through WPF and Silverlight but this is my first run at ASP and MVC.
I am building a site to verify contents of a shipment against an electronic manifest (EDI 856). I have a page that displays the shipping data and I need the users to scan each item barcode in the container. I would then like to pass that barcode to a service, verify the item belongs in that shipment and then update the page to show as much.
My plan was to have a single text box into which the user could scan/type the barcode and then submit that data to a WebAPI service which would verify the information and then probably use SignalR to send a message back to the page and update a grid with the item data.
If this is a decent way to go, I'm just not quite sure how to use ajax to call the WebAPI endpoint and provide the data I need.
I would advise against using SignalR in this situtation. What you need, judging from your description, is the most basic use case of submitting an ajax request and receiving a response.
You are not designing a system where you need the server to initiate communication with the browser or anything like that, where sockets (and SignalR as an abstraction over sockets with fallbacks to less suitable protocols) is a huge overkill.
Don't worry, your use case is rather simple.
It's a little out of scope to describe how to setup a WebApi project, how to configure routing, action names, etc. Simple google searches will surely provide ample quality tutorials on getting started.
I'll just try to explain what the general idea is, with some code samples, to get you thinking in the right direction.
You need to create an ApiController.
The simplest version of that Controller will probably look something like this:
public class ShipmentVerificationController : ApiController
{
//this is the response object you will be sending back to the client website
public class VerificationResult
{
public bool Valid;
}
public VerificationResult GetIsItemValid(string BarCode)
{
bool itemIsValid;
// Implement checks against the BarCode string here
itemIsValid = true;
return new VerificationResult { Valid = itemIsValid };
}
}
Note that the inner class represents the response you will be sending back. It should be properly filled out with additional info if needed and probably put into a separate .cs file in the "Models" folder or where ever you see fit.
I have declared it inside the controller for demonstration purposes only
Once you have a WebApi service deployed, it's really easy to send it data from your website and receive the feedback.
To simplify Ajax requests, jQuery is often used.
Once the user inputs the barcode into a textbox, you can hook up an event to check for return key being pressed (most barcode scanners send the return key command after they input the barcode data) and then write something along the lines of:
var barcode = $("#input-field").val();
$.getJSON( "<url_to_your_webapi_service>/api/ShipmentVerification/GetIsItemValid/" + barcode, function( data ) {
if (data.Valid) {
// great, highlight the item as valid
}
else {
//better indicate an error with the scanned item
}
});
Please note that for simplicity I have not included any error handling, url parameter encoding, and most importantly, zero authorization.
Authorization is very important if you deploy the web service to the open web but still do not want anyone to be able to call it.
You will have to research these topics yourself, but I hope I have presented you the core concepts and logic behind a simple service such as this, so you have a base to start with.
If you come up with specific problems and questions post a new question.
I actually found a more simple way to do this. I nixed the idea of using a WebAPI endpoint and just went with a normal controller. I used ajax to prevent the page from refreshing with the new view, since that view is actually just json data with my return values in it.

Categories

Resources