I was depeloping a WebApi based on a DDD Pattern in .Net6 and when i run it in Swagger UI
Response of Swagger when i try to create a person with post method
Responses
Curl
curl -X 'POST' \
'https://localhost:7233/api/Person' \
-H 'accept: */*' \
-H 'Content-Type: application/json' \
-d '"personId": "892130432",
"name": "Kathe",
"phone": 78213901,
"address": "Alemania",
"maritalStatus": "single"'
Request URL
https://localhost:7233/api/Person
Server response
Code Details
400
Undocumented
Error: response status is 400
Response body
Download
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-3bfbf63df0e77aefa938d1c052d1d14f-d83e38df87273120-00",
"errors": {
"$": [
"The JSON value could not be converted to API.Commands.CreatePersonCommand. Path: $ | LineNumber: 0 | BytePositionInLine: 10."
],
"createPersonCommand": [
"The createPersonCommand field is required."
]
}
}
Response headers
content-type: application/problem+json; charset=utf-8
date: Tue,31 Jan 2023 16:15:30 GMT
server: Kestrel
This is my class CreatePersonCommand
It is a record to create a new Person
public record CreatePersonCommand(Guid personId, string Name, int Phone, string Address, string MaritalStatus);
This is my class APIServices where i expose the post method
With the handel command i create the person
public class APIServices
{
private readonly PersonRepository repository;
private readonly PersonQueries personQueries;
public APIServices(PersonRepository repository, PersonQueries personQueries)
{
this.repository = repository;
this.personQueries = personQueries;
}
public async Task HandleCommand(CreatePersonCommand createPerson)
{
var person = new Person(PersonId.create(createPerson.personId));
person.SetName(PersonName.Create(createPerson.Name));
person.SetPhone(PersonPhone.Create(createPerson.Phone));
person.SetAddress(PersonAddress.Create(createPerson.Address));
person.SetMaritalStatus(PersonMaritalStatus.Create(createPerson.MaritalStatus));
await repository.AddPerson(person);
}
public async Task<Person> GetPerson(Guid id)
{
return await personQueries.GetPersonIdAsync(id);
}
public async Task<List<Person>> GetAllPerson()
{
return await personQueries.GetAllPerson();
}
}
Method Create
This method is used in APIServices to create a new Id and the operator help to make the conversion of PersonId, this method "Create" is on all of the entities
public record PersonId
{
public Guid value { get; init; }
//Constructor
internal PersonId(Guid value_)
{
value = value_;
}
//Create the value type PersonId
public static PersonId create(Guid value)
{
return new PersonId(value);
}
//Make the conversion from PersonId to Guid
public static implicit operator Guid(PersonId personId)
{
return personId.value;
}
}
Interface Repository
public interface PersonRepository
{
//Operations to Person
Task<Person> GetPersonById(PersonId Id);
Task AddPerson(Person person);
Task<List<Person>> GetAllPerson();
}
I don't know if the problem is for the Create Command or it is in the operator on PersonID
Related
I have searched until my eyes are bleeding but alas no answers.
I have a class called Booking.
public class Booking
{
[Key]
public int Id { get; set; }
[Required]
public int RoomNumber { get; set; }
[Required]
public string? ClientName { get; set; }
}
The API method is
[HttpGet]
public async Task<JsonResult> Get(int id)
{
Booking? booking = await _bookingRepository.FindByIdAsync(id);
if (booking == null)
{
return new JsonResult(NotFound());
}
return new JsonResult(Ok(booking));
}
The Client is
protected static async Task<Booking?> GetAsync<Booking>(HttpClient httpClient)
{
HttpResponseMessage response = await httpClient.GetAsync("https://localhost:7139/api/HotelBooking?id=5");
if (response.StatusCode == HttpStatusCode.NoContent)
{
string msg = await response.Content.ReadAsStringAsync();
return default(Booking);
}
else if (response.IsSuccessStatusCode)
{
string msg = await response.Content.ReadAsStringAsync();
Booking? booking = JsonConvert.DeserializeObject<Booking>(msg);
//Booking? booking = await response.Content.ReadFromJsonAsync<Booking>();
return booking;
}
else
{
string msg = await response.Content.ReadAsStringAsync();
Console.WriteLine(msg);
throw new Exception(msg);
}
}
The value in msg is
{
"Value": {
"Id": 5,
"RoomNumber": 100,
"ClientName": "Nelms, Anthony"
},
"Formatters": [],
"ContentTypes": [],
"DeclaredType": null,
"StatusCode": 200
}
The result: I have a Booking object that is instantiated by the JsonConvert.DeserializeObject line of code, but all three values are missing. The string is null and the two ints are zero.
I believe the problem is that the section "Value" is "hiding" my data. I am not sure how to read the data from response.Content.
This is my first attempt at Http. Any help would be appreciated.
Can you try replacing msg with msg.Result in below line?
JsonConvert.DeserializeObject<Booking>(msg.Result);
I tried the suggestion from #TopSail in the comments, and it worked:
Well, I would try return Ok(booking) instead of JsonResult(Ok(booking)) - see if that works. Seems like wrapping the return in a JsonResult is also creating a new type of OK object here. –
Before I read your suggestion I simply returned the object as follows:
return booking;
That worked.
However, I like your suggestion better. I implemented your suggestion as follows:
return Ok(booking);
This also worked.
We have a swagger generated OpenAPI definition which has an endpoint that defines it's responses as the following:
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"type": "file"
}
},
"application/json": {
"schema": {
"type": "file"
}
},
"text/json": {
"schema": {
"type": "file"
}
}
}
}
}
When using NSwagStudio to generate a C# Client from swagger.json, the response type translates into a class which gets defined into the generated file like so...
NSwagStudio Generated Endpoint example
public System.Threading.Tasks.Task<FileResponse> FileAsync(LayeredVideo body)
{
return FileAsync(body, System.Threading.CancellationToken.None);
}
NSwagStudio Geneated FileResponse class
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.15.10.0 (NJsonSchema v10.6.10.0 (Newtonsoft.Json v12.0.0.0))")]
public partial class FileResponse : System.IDisposable
{
private System.IDisposable _client;
private System.IDisposable _response;
public int StatusCode { get; private set; }
public System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> Headers { get; private set; }
public System.IO.Stream Stream { get; private set; }
public bool IsPartial
{
get { return StatusCode == 206; }
}
public FileResponse(int statusCode, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.IO.Stream stream, System.IDisposable client, System.IDisposable response)
{
StatusCode = statusCode;
Headers = headers;
Stream = stream;
_client = client;
_response = response;
}
public void Dispose()
{
Stream.Dispose();
if (_response != null)
_response.Dispose();
if (_client != null)
_client.Dispose();
}
}
however if I attempt to generate the same C# client within VS2019 via the Connected Services interface while the endpoint itself generates the same, it does not define the actual FileResponse class
VS2019 Generated Endpoint example
public System.Threading.Tasks.Task<FileResponse> FileAsync(LayeredVideo body)
{
return FileAsync(body, System.Threading.CancellationToken.None);
}
This leads to the following error in the generated code.
By my understanding, NSwagStudio and Connected Services use basically the same tech in the background to generate code, so I am confused as to why one solution generates working code and the other doesn't.
Any ideas?
I've built a Web API application and found an issue (which currently treated badly in my code), the issue summarized in wrapping all Json objects which returned from All API actions with custom nodes(roots).
i.e: I have this json (array) response:
[
{
"Category": "Pages",
"Users": [
{
"ID": "1",
"Fname": "Foo",
"Lname": "Bar"
}
]
}
]
And Need this response:
{
"Object": {
"Body": [
{
"Category": "Pages",
"Users": [
{
"ID": "1",
"Fname": "Foo",
"Lname": "Bar"
}
]
}
]
}
}
So here I just wrapped the response inside {"Object":{"Body": <Response Here>}}
And this I need it to be applied on all API Json responses of type Array.
And for simple Json object response, I need it just to be wrapped like {"Object": <Response Here>}
I wrapped the Json response currently in each controller action by this code:
public JsonResult Categories()
{
return Json(new { Object= new { Body= GetCategoriesList() } }, JsonRequestBehavior.AllowGet);
}
Sure this achievement is so bad because I have to repeat this wrapping in each action.
My Question Is:
How to create ActionFilterAttribute to be called after each action execution to wrap the response as per the above Json sample?
i.e. for creating the filter:
public class JsonWrapper: System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
i.e. for calling the filter:
[JsonWrapper]
public class APIController : Controller
And also to set the response content type in the same filter "application/json"
If suppose here if what you looking for:
public class JsonWrapperAttribute : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
//Check it's JsonResult that we're dealing with
JsonResult jsonRes = context.Result as JsonResult;
if (jsonRes == null)
return;
jsonRes.Data = new { Object = new { Body = jsonRes.Data } }
}
}
Here is how you can use it:
[JsonWrapper]
public JsonResult Index()
{
var data = new
{
a = 1,
b = 2
};
return Json(data, JsonRequestBehavior.AllowGet);
}
Result will be:
{"Object":{"Body":{"a":1,"b":2}}}
To prevent yourself having to repeat wrapping in each action you could either write an extension method which would do the wrapping for you
public static class ControllerExtensions
{
public static JsonResult WrappedJson(this Controller controller, object data, JsonRequestBehavior behavior)
{
return new JsonResult
{
Data = new { Object = new { Body = data } },
JsonRequestBehavior = behavior
};
}
}
or create a new ActionResult class (and add extension methods to return that)
public class WrappedJsonResult : JsonResult
{
public new object Data
{
get
{
if (base.Data == null)
{
return null;
}
return (object) ((dynamic) base.Data).Object.Body;
}
set { base.Data = new {Object = new {Body = value}}; }
}
}
I am trying to create one WEB API controller (Service method) which accept an array of a class object.
And then also a .NET client which makes a call to this API method and pass JSON string (array of class object). Issue is I am not able to receive json array contents on server side. Seems some serialization/de-serialization error but I am not able to spot. Please see sample code as below:
C# class as below:
public class UserData
{
public int ID { get; set; }
public DateTime DATETIME { get; set; }
public int SEQUENCE { get; set; }
}
And then WEB API method (API Controller as below)
[HttpPost()]
public HttpResponseMessage Post([FromBody()]
IEnumerable<#UserData> RequestBody)
{
}
Json array as below
[
{
"ID": 1,
"DATE": "2014-01-01",
"SEQUENCE": 533
},
{
"ID": 2,
"DATE": "2015-01-01",
"SEQUENCE": 3233
},
{
"ID": 3,
"DATE": "2015-01-01",
"SEQUENCE": 233
}
]
And the .NET Client as below:
public void CallService(string jsonString)
{
try {
var client = new RestClient(GetBaseURLService());
var requestRest = new RestRequest("event ", Method.POST);
var RequestBody = TextBoxCreateEventJson.Text;
requestRest.AddBody(jsonString);
requestRest.RequestFormat = DataFormat.Json;
var res = client.Execute(requestRest);
} catch (Exception ex) {
}
}
And then I get null/nothing in the RequestBody.
I know something I need to do before a call to requestRest.AddBody(jsonString);
But not sure what?
In System.Web.Helpers namespace, there is a Json class which you can use in order to encode or decode. For your case,
string jsonString = Json.Encode(your array as an argument)
Use jsonString in the body of your request.
I have ASP.NET application, with some MVC and WebAPI controllers in there. MVC 5.1 and WebAPI 2.1. There is no tricky configuration at all, the only thing is getting configured is removed XML formatter:
public static class WebApi
{
public static void Configure(HttpConfiguration config)
{
var formatters = config.Formatters;
formatters.Remove(formatters.XmlFormatter);
}
}
There is simple DTO to put data returned from controller action together:
public class TokenResponse
{
public string token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public string error { get; set; }
}
And the following code:
[NoHttpResponseCaching]
public class TokenController : ApiController
{
public async Task<HttpResponseMessage> PostAsync(HttpRequestMessage request, TokenRequest tokenRequest)
{
try
{
// do some stuff
var response = new TokenResponse { ... };
return request.CreateResponse(HttpStatusCode.OK, response);
}
catch (TokenRequestValidationException ex)
{
_logger.WriteError(ex);
var response = new TokenResponse { error = ex.ErrorToDisplay };
return request.CreateResponse(HttpStatusCode.BadRequest, response);
}
}
}
The problem is that if everything fine, I have my TokenResponse serialized to JSON, as expected, but if there is exception occurring, execution flow is coming into catch block and response body is equal to "Bad Request", this is the RAW dump of response from Fiddler:
HTTP/1.1 400 Bad Request
Cache-Control: no-store
Pragma: no-cache
Content-Type: text/html
Server: Microsoft-IIS/8.5
X-Powered-By: ASP.NET
Date: Wed, 18 Jun 2014 08:25:53 GMT
Content-Length: 11
Bad Request
Tried to return anonymous object having some random named properties instead of using TokenResponse, getting same response:
return request.CreateResponse(HttpStatusCode.BadRequest, new {klhjaoiubf = "kjhaflkjh"});
And I'm stuck at this point trying to understand why I'm not getting my object serialized in response body and how to change it. Anyone, any ideas, why my object is getting ignored when response code is BadRequest?