Unexpected parameters definition using Swashbuckle - c#

I have such controller
[HttpGet]
public Task<IActionResult> GetCsvAudit([FromQuery] ReportDto request)
{
return Task.FromResult<IActionResult>(Ok());
}
public class ReportDto
{
public DateTimeRange ActionDateRange { get; set; }
public PhiResourceType OrderBy { get; set; }
public string PhiResourceTypeIdentifier { get; set; }
}
DateTimeRange is configured for Swagger as follow:
c.MapType<DateTimeRange>(() => new OpenApiSchema()
{
Description = "Date range",
Type = "string",
Title = "Date range",
Example = new OpenApiString("2020-11-15T00:42:13+00:00 - 2020-11-16T00:42:13+00:00"),
});
The problem is the Swagger still wants me to provide every DateTimeRange property in query while I'm using the [FromQuery] attribute, when I want it to expects me to provide the string 2020-11-15T00:42:13+00:00,2020-11-16T00:42:13+00:00 for it.

If you mark field/property as internal or protected or private, it will be ignored automatically by swashbuckle in swagger documentation:
public class DateTimeRange
{
internal string Start { get; set; }
internal string End { get; set; }
}
Result:

Related

Swagger is throwing exception when ProducesResponseType is used

I am using .Net 7 and Swagger for Api and documentation. I am facing a problem when using "ProducesResponseType". It is throwing exception which is below.
Here is what I am using to decorate my controller.
This is the problem
[ProducesResponseType(typeof(PaginationResponse<GetBackEndUsers.Result>), 200)]
This is where it is conflicting with. Both have different classes and properties.
[ProducesResponseType(typeof(Login.Result), 200)]
Classes I am using are below
public class PaginationResponse<T> where T : class
{
public int PageNumber { get; set; } = 1;
public int ItemCount { get; set; } = 10;
public int TotalRecords { get; set; }
public IList<T> Data { get; set; }
public PaginationResponse()
{
Data = new List<T>();
}
}
public static class GetBackEndUsers
{
public class Result
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string? Email { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
}
}
The exception I am getting is
InvalidOperationException: Can't use schemaId "$Result" for type "$OctuFit.Application.Features.GetBackEndUsers+Result". The same schemaId is already used for type "$OctuFit.Application.Features.Login+Result"
Solution I have tried
options.CustomSchemaIds(type => type.ToString());
options.CustomSchemaIds( type => type.FullName );
Both removes my schema from request and response.
Any help will be appreciated.
Cheers
Edit 1
Here is the example of the controller method
[HttpGet]
[ProducesResponseType(typeof(PaginationResponse<GetBackEndUsers.Result>), 200)]
public async Task<IActionResult> GetBackendUsers([FromQuery] PaginationRequest pagination)
{
var query = new GetBackEndUsers.Query
{
PageNumber = pagination.PageNumber,
ItemCount = pagination.ItemCount
};
var response = await mediator.Send(query);
return HandleResponse(response);
}
options.CustomSchemaIds(s => s.FullName?.Replace("+", "."));
Solved my problem.
You should update [FromQuery] attribute to [FromBody].

Why the controller response are setting model field names into lower case?

In my .NET Core project, in the response of all controllers, the object fields are coming in lower case in the first one or two letters of the field name:
{
"iD_PARAM": "foo",
"cD_PROM": "bar",
"txT_OFFICER": "lorem",
"cN_NEW_PARAM": "fubá",
"iD_SITUATION": "XX",
"iD_NEW_USER": "ipsun",
}
It's strange, because the model has all fields in UPPER case:
public partial class MyModel {
public long ID_PARAM { get; set; }
public long CD_PROM { get; set; }
public string TXT_OFFICER { get; set; }
public int CN_NEW_PARAM { get; set; }
public int ID_SITUATION { get; set; }
public int ID_NEW_USER { get; set; }
}
For more detail, this is the controller where I set the values and the response:
[HttpPost("receive")]
public async Task<IActionResult> Get()
{
try
{
MyModel newParam = new MyModel ();
newParam.ID_PARAM = "foo";
newParam.CD_PROM = "foo";
newParam.TXT_OFFICER = "lorem";
newParam.CN_NEW_PARAM = "fubá";
newParam.ID_SITUATION = "XX";
newParam.ID_NEW_USER = "ipsun";
return Ok(newParam);
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
Assuming you are using Newtonsoft Json, if you want your Json properties to be uppercase, try decorating your Model with JsonProperty like this to prevent the Serializer try to infer the property name :
public partial class MyModel {
[JsonProperty("ID_PARAM")]
public long ID_PARAM { get; set; }
[JsonProperty("CD_PROM")]
public long CD_PROM { get; set; }
[JsonProperty("TXT_OFFICER")]
public string TXT_OFFICER { get; set; }
[JsonProperty("CN_NEW_PARAM")]
public int CN_NEW_PARAM { get; set; }
[JsonProperty("ID_SITUATION")]
public int ID_SITUATION { get; set; }
[JsonProperty("ID_NEW_USER")]
public int ID_NEW_USER { get; set; }
}
You should change the ContractResolver, just add below code in startup ConfigurSservices
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
Refer to Lowercase property names from Json() in .Net core

Variable cannot be non-input type - GraphQL .Net Conventions

I have graphql.net implementation using conventions
I have my model defined as below.
public partial class Project
{
public Project()
{
ProjectGroup = new HashSet<ProjectGroup>();
ProjectUser = new HashSet<ProjectUser>();
Datasource = new HashSet<Datasource>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProjectGroup> ProjectGroup { get; set; }
public virtual ICollection<ProjectUser> ProjectUser { get; set; }
public virtual ICollection<Datasource> Datasource { get; set; }
}
I am trying to update only name of above class.
using above class (which is basically kind of entity framework class, but that is irrelevant of this question)
So I have defined mutation as below.
public sealed class Mutation
{
public async Task<Project> SaveProject([Inject] IProjectRepository projectRepository, projectModels.Master.Project project)
{
return Mapper.Map<Project>(await projectRepository.SaveProject(project));
}
}
and I am calling this mutation as below.
axios
.post('https://localhost:44375/api/Graph', {
query: `mutation ($project: Project) {
saveProject(project: $project) {
name
}
}`,
variables: {
'project': { 'name' : data.label },
},
})
In response I am getting below error.
{"errors":[{"message":"Variable \"project\" cannot be non-input type \"Project\".","locations":[{"line":1,"column":11}],"extensions":{"code":"VALIDATION_ERROR"}}]}
what am I doing wrong?
From graphql.net convention's official repo, I found one example and there was one attribute used for input type. After use of that it is working.
https://github.com/graphql-dotnet/conventions/blob/master/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/GraphApi/Schema/InputTypes/UpdateMovieTitleParams.cs
So it requires attribute something in a following way.
[InputType]
public class UpdateMovieTitleParams
{
public Guid Id { get; set; }
public string NewTitle { get; set; }
}

Deserialize nested JSON Response with RestSharp Client

I'd like to consume a REST Api and deserialize the nested JSON Response. For that purpose I tried to create some POCO classes which represent the JSON Response [1].
The response looks like this:
{
"success": true,
"message": "OK",
"types":
[
{
"name": "A5EF3-ASR",
"title": "ITIL Foundation Plus Cloud Introduction",
"classroomDeliveryMethod": "Self-paced Virtual Class",
"descriptions": {
"EN": {
"description": "some Text null",
"overview": null,
"abstract": "Some other text",
"prerequisits": null,
"objective": null,
"topic": null
}
},
"lastModified": "2014-10-08T08:37:43Z",
"created": "2014-04-28T11:23:12Z"
},
{
"name": "A4DT3-ASR",
"title": "ITIL Foundation eLearning Course + Exam",
"classroomDeliveryMethod": "Self-paced Virtual Class",
"descriptions": {
"EN": {
"description": "some Text"
(...)
So I created the following POCO classes:
public class Course
{
public bool success { get; set; }
public string Message { get; set; }
public List<CourseTypeContainer> Type { get; set; }
}
/* each Course has n CourseTypes */
public class CourseType
{
public string Name { get; set; }
public string Title { get; set; }
public List<CourseTypeDescriptionContainer> Descriptions { get; set; }
public DateTime LastModified { get; set; }
public DateTime Created { get; set; }
}
public class CourseTypeContainer
{
public CourseType CourseType { get; set; }
}
/* each CourseType has n CourseTypeDescriptions */
public class CourseTypeDescription
{
public string Description { get; set; }
public string Overview { get; set; }
public string Abstract { get; set; }
public string Prerequisits { get; set; }
public string Objective { get; set; }
public string Topic { get; set; }
}
public class CourseTypeDescriptionContainer
{
public CourseTypeDescription CourseTypeDescription { get; set; }
}
And this is the API Code:
var client = new RestClient("https://www.someurl.com");
client.Authenticator = new HttpBasicAuthenticator("user", "password");
var request = new RestRequest();
request.Resource = "api/v1.0/types";
request.Method = Method.GET;
request.RequestFormat = DataFormat.Json;
var response = client.Execute<Course>(request);
EDIT 1: I found a Typo, the Type property in AvnetCourse should be named Types:
public List<AvnetCourseTypeContainer> Type { get; set; } // wrong
public List<AvnetCourseTypeContainer> Types { get; set; } // correct
Now the return values look like:
response.Data.success = true // CORRECT
repsonse.Data.Message = "OK" // CORRECT
response.Data.Types = (Count: 1234); // CORRECT
response.Data.Types[0].AvnetCourseType = null; // NOT CORRECT
EDIT 2: I implemented the Course.Types Property using a List<CourseType> instead of a List<CourseTypeContainer>, as proposed by Jaanus. The same goes for the CourseTypeDescriptionContainer:
public List<CourseTypeContainer> Type { get; set; } // OLD
public List<CourseTypeDescriptionContainer> Descriptions { get; set; } // OLD
public List<CourseType> Type { get; set; } // NEW
public List<CourseTypeDescription> Descriptions { get; set; } // NEW
Now the response.Data.Types finally are properly filled. However, the response.Data.Types.Descriptions are still not properly filled, since there is an additional language layer (e.g. "EN"). How can I solve this, without creating a PACO for each language?
EDIT 3: I had to add an additional CourseTypeDescriptionDetails class, where I would store the descriptive Data. In my CourseTypeDescription I added a property of the Type List for each language. Code Snippet:
public class AvnetCourseType
{
public List<CourseTypeDescription> Descriptions { get; set; }
// other properties
}
public class CourseTypeDescription
{
public List<CourseTypeDescriptionDetails> EN { get; set; } // English
public List<CourseTypeDescriptionDetails> NL { get; set; } // Dutch
}
public class CourseTypeDescriptionDetails
{
public string Description { get; set; }
public string Overview { get; set; }
public string Abstract { get; set; }
public string Prerequisits { get; set; }
public string Objective { get; set; }
public string Topic { get; set; }
}
It works now, but I need to add another property to CourseTypeDescription for each language.
OLD: The return values are
response.Data.success = true // CORRECT
repsonse.Data.Message = "OK" // CORRECT
response.Data.Type = null; // WHY?
So why does my response.Type equal null? What am I doing wrong?
Thank you
Resources:
[1] RestSharp Deserialization with JSON Array
Try using this as POCO:
public class Course
{
public bool success { get; set; }
public string message { get; set; }
public List<CourseTypeContainer> Types { get; set; }
}
Now you have list of CourseTypeContainer.
And CourseTypeContainer is
public class CourseTypeContainer
{
public CourseType CourseType { get; set; }
}
So when you are trying to get response.Data.Types[0].AvnetCourseType , then you need to have field AvnetCourseType inside CourseTypeContainer
Or I think what you want is actually this public List<CourseType> Types { get; set; }, you don't need a container there.
Just in case this helps someone else, I tried everything here and it still didn't work on the current version of RestSharp (106.6.2). RestSharp was completely ignoring the RootElement property as far as I could tell, even though it was at the top level. My workaround was to manually tell it to pull the nested JSON and then convert that. I used JSON.Net to accomplish this.
var response = restClient.Execute<T>(restRequest);
response.Content = JObject.Parse(response.Content)[restRequest.RootElement].ToString();
return new JsonDeserializer().Deserialize<T>(response);
I used http://json2csharp.com/ to create C# classes from JSON.
Then, renamed RootObject to the ClassName of the model file I'm creating
All the data in the nested json was accessible after RestSharp Deserializitaion similar to responseBody.data.Subject.Alias
where data, Subject and Alias are nested nodes inside the response JSON received.

ServiceStack Operation are not showing in metadata when using with Repository pattern

This is the DTO
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public string EmployeeeAddress { get; set; }
}
This is the Response
public class EmployeeResponse
{
public List<Employee> listofemp { get; set; }
}
This is the Service stack Service
public class EmployeeServices:Service
{
public dbRepo<Employee> objEmploye; //Repository (which is working fine)
public EmployeeServices()
{
objEmploye = new dbRepo<Employee>();
}
public object getAll(Employee obj)
{
var objlist = new EmployeeResponse {listofemp = objEmploye.GetAll().ToList()};
return objlist.listofemp;
}
}
this is the AppHostBase class
public class ServiceHostApp:AppHostBase
{
public ServiceHostApp()
: base("ServiceStack WebSerivces", typeof(EmployeeServices).Assembly)
{
}
public override void Configure(Funq.Container container)
{
}
}
My Question is why EmployeeServices Metod are not showing in the Metadata ?
is there any additional thing do i need to do ??
ServiceStack operates on the method names matching the Http Verb used, so instead of getAll you should really be using Get (or All if any verb can be used to do that).
No need to return an object...
public List<Employee> Get(Employee obj)
{
var objlist = new EmployeeResponse {listofemp = objEmploye.GetAll().ToList()};
return objlist.listofemp;
}
Lastly, you can always adorn Employee with:
[Route("/Employee")]
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public string EmployeeeAddress { get; set; }
}
That attribute is define in the namespace ServiceStack.ServiceHost.
You don't seem to have configured any routes and also haven't respected the naming convention of the service operation.
So you should decorate your request DTO with the Route attribute:
[Route("/employees")]
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public string EmployeeeAddress { get; set; }
}
and have your service operation be named with the HTTP verb you want it to be accessible with (GET in your case):
public object Get(Employee obj)
{
var objlist = new EmployeeResponse {listofemp = objEmploye.GetAll().ToList()};
return objlist.listofemp;
}
Now when you navigate to GET /employees the Get operation will be executed.
Bear in mind that you can also configure your routes using the Fluent API instead of using the Route attribute:
Routes.Add<Employee>("/employees", "GET");

Categories