When I post a json to the API the body received is always null. I'm using .net core 2.0
[HttpPost]
public IActionResult UpdateProfile([FromBody] UserProfileView userProfile)
{
return Ok();
}
public class UserProfileView
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
public string UserName { get; set; }
public string DisplayName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public byte? YearOfBirth { get; set; }
public List<UserAttribute> Attributes { get; set; }
}
Json I'm posting
{
"id": "3e9f50c8-358a-4509-bbe4-4f4c6cc00676",
"email": "joesamraj#outlook.com",
"userName": "619795228409681",
"displayName": "Joe Samraj",
"firstName": "Joe Samraj",
"lastName": "Muthuraj",
"gender": "Male",
"yearOfBirth": "1989",
"attributes": []
}
Header: Content-Type:application/json
what is wrong here?
I think there is a problem in datatype of your fields. You have yearofbirth field with data type bit and bit will accept values between 0 to 255 so you can change the type of yearofbirth to datetime .
for reference see this byte datatype documentation
Related
May someone show an example with a partial update entity? All examples into git/docs/stack have only update method, which replaces all fields, even I sending null.
It looks like I need to use HttpPatch and json-patch. But it's so much extra code...
UserEntity
public class User: AbpUser<User>
{
[Required(AllowEmptyStrings = true)]
[StringLength(MaxNameLength)]
public override string Name { get; set; }
[Required(AllowEmptyStrings = true)]
[StringLength(MaxNameLength)]
public override string Surname { get; set; }
public string FatherName { get; set; }
public DateTime BirthDate { get; set; }
public long? DepartmentId { get; set; }
public long? ManagerId { get; set; }
public long? PositionId { get; set; }
public override string FullName => $"{Surname} {Name} {FatherName}";
}
CreateUserDto
{
"name": "Test",
"surname": "Test",
"emailAddress": "test#test.test"
}
UpdateUserDto
{
"id": 1,
"name": "Василий",
"surname": "Пупкин",
"fatherName": "Иванович",
"birthDate": "1993-02-21",
"emailAddress": "vasiliyp#test.test",
"phonenumber": "+79378889911",
"departmentId": 1,
"positionId" : 1
}
Second UpdateUserDto
{
"id": 1,
"name": "Василий",
"surname": "Пупкин",
"fatherName": "Иванович"
}
After the second update I want to get a partial update, but it updates all fields include those I do not send. For example, PositionId will be null, instead of 1.
UserAppService
public override async Task<UserDto> UpdateAsync(UpdateUserDto input)
{
var user = await _userManager.GetUserByIdAsync(input.Id);
MapToEntity(input, user);
return await GetAsync(input);
}
protected override async void MapToEntity(UpdateUserDto input, User user)
{
ObjectMapper.Map(input, user);
user.SetNormalizedNames();
if (!user.DepartmentId.HasValue || user.DepartmentId == 0) return;
var department = await _departmentRepository.GetAsync((long)user.DepartmentId);
user.ManagerId = department.ManagerId;
}
Update
Yesterday I find a way: it needs to customize automapper that to merge models.
Configuration.Modules.AbpAutoMapper().Configurators.Add(
cfg => cfg.ForAllMaps((obj, cnfg) => cnfg.ForAllMembers(
opts => opts.Condition((src, dest, srcMember) => srcMember != null))));
But there are problems:
All value types into DTO must be nullable because the automapper will get default values.
Even though I have defined DateTime as nullable, the automatic conversion converts it to the default (DateTime). I have not yet found a way to fix this without a crutch.
if(input.BirthDate == null) input.BirthDate = user.BirthDate;
UPDATE 2
[AutoMapTo(typeof(User))]
public class UpdateUserDto : EntityDto<long>
{
[StringLength(AbpUserBase.MaxNameLength)]
public string Name { get; set; }
[StringLength(AbpUserBase.MaxSurnameLength)]
public string Surname { get; set; }
public string FatherName { get; set; }
public DateTime? BirthDate { get; set; }
[EmailAddress]
[StringLength(AbpUserBase.MaxEmailAddressLength)]
public string EmailAddress { get; set; }
public string PhoneNumber { get; set; }
public long? DepartmentId { get; set; }
public long? PositionId { get; set; }
}
you can create new Dto with specific fields and create new Update function for that.
As you have pointed out, all values in the update DTO must be declared nullable, or it will be assigned default values.
Then you also need to convert nullable type to using destination value instead.
// example for int, double and DateTime
CreateMap<int?, int>().ConvertUsing((src, dest) => src ?? dest);
CreateMap<double?, double>().ConvertUsing((src, dest) => src ?? dest);
CreateMap<DateTime?, DateTime>().ConvertUsing((src, dest) => src ?? dest);
// ignore if null
CreateMap<UpdateDTO, UpdateEntity>()
.ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
I have created an web api using the below code.
[HttpPost]
[Route("api/Agents/SetAgentSettings")]
public HttpResponseMessage SetAgentSetting(string agentIp, string agentMac, Guid orgId,SettingModel settingData)
{
}
From postman I am trying to call this api using the following request.
http://localhost:50194/api/Agents/SetAgentSettings?agentIp=10.0.1.33&agentMac=E442A6273481&orgId=C1F62D47-FBDF-468E-A4E6-418BFD8EB525
And in body I am sending the following body:
{
"agentIp":"10.0.2.10",
"agentMac":"Computer1",
"orgId":"c1f62d47-fbdf-468e-a4e6-418bfd8eb525",
"settingData":"{\"IsAutoSyncActive\":false,\"AutoSyncInterval\":0,\"AutosyncTime\":\"00:00\",\"IsRealtimeSyncActive\":false,\"RealTimeSyncUrl\":null,\"LogTargets\":6,\"LogSeveritys\":15,\"Exports\":0,\"LogInDetail\":true,\"LogInDatabase\":true,\"NotifyEmailId\":null,\"DiagonisticsMode\":false,\"ResyncRule\":null,\"ResyncBatchCount\":\"10\",\"IsResyncScheduled\":false,\"ExecuteFor\":1,\"Batch\":0,\"SaveSyncInfoToDb\":false,\"RealTimePort\":null,\"NotificationRule\":null,\"IsNotificationMailEnabled\":false,\"FileDeleteTime\":null,\"AgentType\":null,\"Frequency\":\"DAILY\",\"PartitionKey\":\"c1f62d47-fbdf-468e-a4e6-418bfd8eb525\",\"RowKey\":\"fbc6b368-9251-4165-a36b-fc1bd3912925\",\"Timestamp\":\"0001-01-01T00:00:00+00:00\",\"ETag\":null}"
}
In controller I am getting every data but setting model is not serializing. How to send the setting model data.
You're passing settingData in JSON string and another side expecting to be bind it with the model, which wouldn't work anyway. You have to pass the model in JSON format. Additionally, as you're passing agentIp, agentMac, orgId in URL, don't need to pass in the body again.
Here I assume your SettingModel as follows,
public class SettingModel
{
public bool IsAutoSyncActive { get; set; }
public int AutoSyncInterval { get; set; }
public string AutosyncTime { get; set; }
public bool IsRealtimeSyncActive { get; set; }
public object RealTimeSyncUrl { get; set; }
public int LogTargets { get; set; }
public int LogSeveritys { get; set; }
public int Exports { get; set; }
public bool LogInDetail { get; set; }
public bool LogInDatabase { get; set; }
public object NotifyEmailId { get; set; }
public bool DiagonisticsMode { get; set; }
public object ResyncRule { get; set; }
public string ResyncBatchCount { get; set; }
public bool IsResyncScheduled { get; set; }
public int ExecuteFor { get; set; }
public int Batch { get; set; }
public bool SaveSyncInfoToDb { get; set; }
public object RealTimePort { get; set; }
public object NotificationRule { get; set; }
public bool IsNotificationMailEnabled { get; set; }
public object FileDeleteTime { get; set; }
public object AgentType { get; set; }
public string Frequency { get; set; }
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string Timestamp { get; set; }
public object ETag { get; set; }
}
Your request should be:
URL: http://localhost:50194/api/Agents/SetAgentSettings?agentIp=10.0.1.33&agentMac=E442A6273481&orgId=C1F62D47-FBDF-468E-A4E6-418BFD8EB525
Body:
{
"IsAutoSyncActive": false,
"AutoSyncInterval": 0,
"AutosyncTime": "00:00",
"IsRealtimeSyncActive": false,
"RealTimeSyncUrl": null,
"LogTargets": 6,
"LogSeveritys": 15,
"Exports": 0,
"LogInDetail": true,
"LogInDatabase": true,
"NotifyEmailId": null,
"DiagonisticsMode": false,
"ResyncRule": null,
"ResyncBatchCount": "10 ",
"IsResyncScheduled": false,
"ExecuteFor": 1,
"Batch": 0,
"SaveSyncInfoToDb": false,
"RealTimePort": null,
"NotificationRule": null,
"IsNotificationMailEnabled": false,
"FileDeleteTime": null,
"AgentType": null,
"Frequency": "DAILY ",
"PartitionKey": "c1f62d47-fbdf-468e-a4e6-418bfd8eb525",
"RowKey": "fbc6b368-9251-4165-a36b-fc1bd3912925",
"Timestamp": "0001-01-01T00:00:00+00:00",
"ETag": null
}
Method:
[HttpPost]
[Route("api/Agents/SetAgentSettings")]
public void SetAgentSetting(string agentIp, string agentMac, Guid orgId, [FromBody]SettingModel settingData)
{
}
I have a Product class, which looks like this:
Public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Barcode { get; set; }
public string InnerCode { get; set; }
public virtual ProductUnit ProductUnit { get; set; }
public int? ProductUnitID { get; set; }
public virtual ProductType ProductType { get; set; }
public int? ProductTypeID { get; set; }
}
In ASP.NET Core Web API Service I have a put method which returns OK(product).
The response in postman looks like this:
{
"result": {
"id": 22,
"name": "Bread",
"productType": {
"id": 4,
"name": "Food",
"remarks": null,
"products": []
},
"productTypeID": 4,
"code": "566",
"barcode": "855",
"innerCode": "145522",
"productUnit": {
"id": 4,
"name": "Box",
"remarks": null,
"products": []
},
"productUnitID": 4
},
"id": 592, ---> //probably this
"exception": null,
"status": 5,
"isCanceled": false,
"isCompleted": true,
"isCompletedSuccessfully": true,
"creationOptions": 0,
"asyncState": null,
"isFaulted": false
}
I am trying to get Product object as shown below:
var data = await httpResponseMessage.Content.ReadAsAsync<Product>();
But As a result, I get the product object with null properties, except the ID, which is random number and which as I think is the id above the exception in the json response.
What mistake do I have?
So, I believe you are trying to parse result which is the inner object in your case.
In order to parse the whole result you have to create a type for mentioned json, which you can create using https://app.quicktype.io/#l=cs&r=json2csharp.
Classes will be as follows :
public partial class ProductResult
{
[JsonProperty("result")]
public Result Result { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("exception")]
public object Exception { get; set; }
[JsonProperty("status")]
public long Status { get; set; }
[JsonProperty("isCanceled")]
public bool IsCanceled { get; set; }
[JsonProperty("isCompleted")]
public bool IsCompleted { get; set; }
[JsonProperty("isCompletedSuccessfully")]
public bool IsCompletedSuccessfully { get; set; }
[JsonProperty("creationOptions")]
public long CreationOptions { get; set; }
[JsonProperty("asyncState")]
public object AsyncState { get; set; }
[JsonProperty("isFaulted")]
public bool IsFaulted { get; set; }
}
public partial class Result
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("productType")]
public Product ProductType { get; set; }
[JsonProperty("productTypeID")]
public long ProductTypeId { get; set; }
[JsonProperty("code")]
[JsonConverter(typeof(ParseStringConverter))]
public long Code { get; set; }
[JsonProperty("barcode")]
[JsonConverter(typeof(ParseStringConverter))]
public long Barcode { get; set; }
[JsonProperty("innerCode")]
[JsonConverter(typeof(ParseStringConverter))]
public long InnerCode { get; set; }
[JsonProperty("productUnit")]
public Product ProductUnit { get; set; }
[JsonProperty("productUnitID")]
public long ProductUnitId { get; set; }
}
public partial class Product
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("remarks")]
public object Remarks { get; set; }
[JsonProperty("products")]
public List<object> Products { get; set; }
}
now you can use ProductResult as
var data = await httpResponseMessage.Content.ReadAsAsync<ProductResult>();
Update
Another solution is, instead of creating type (class) for full JSON data you can use JObject class and using jsonpath you can select any property or object as follows :
string result = await httpResponseMessage.Content.ReadAsStringAsync();
Product product = JObject.Parse(result).SelectToken("$.result").ToObject<Product>()
I want to search on content field and return content and file name. The query below is taken from NEST github page
Connection string:
var node = new Uri("http://localhost:9200");
var settings = new ConnectionSettings(node);
var client = new ElasticClient(settings);
My class:
The class of search type is below (I feel problem might be here):
public class myclass
{
public string Content { get; set; }
public string filename { get; set; }
}
So I need only content and filename which is in file.filename, but in my search it return null for file.filename but content do return in same query.
NEST API CALL:
var request = new SearchRequest
{
From = 0,
Size = 10,
Query = new TermQuery { Name="Web", Field = "content", Value = "findthis" }
};
var response = client.Search<myclass>(request);
var twet = response.Documents.Select(t=>t.Content).ToList();
As I am new to elastic search so can't understand it. I even don't know why I am using term query to search a document while in kibana I user different queries and quite understandable match and match_phrase queries. So please help me get file.filename.
Edit: I have tried to include this too (later removed):
Source = new SourceFilter { Includes = ("file.filename") }
KIBANA Call:
This is the call from kibana console:
GET /extract/_search
{
"from" : 0, "size" : 1
, "query": {
"match": {
"content": "findthis"
}
}
}
The call returns following result I have used 1 result to show here:
Document in Elastic Search Index:
{
"took": 322,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3330,
"max_score": 4.693223,
"hits": [
{
"_index": "extract",
"_type": "doc",
"_id": "29ebfd23bd7b276d7f3afc2bfad146d",
"_score": 4.693223,
"_source": {
"content": """
my title
A looooong document text to findthis.
""",
"meta": {
"title": "my title",
"raw": {
"X-Parsed-By": "org.apache.tika.parser.DefaultParser",
"Originator": "Microsoft Word 11",
"dc:title": "my title",
"Content-Encoding": "windows-1252",
"Content-Type-Hint": "text/html; charset=windows-1252",
"resourceName": "filename.htm",
"ProgId": "Word.Document",
"title": "my title",
"Content-Type": "text/html; charset=windows-1252",
"Generator": "Microsoft Word 11"
}
},
"file": {
"extension": "htm",
"content_type": "text/html; charset=windows-1252",
"last_modified": "2015-10-27T15:44:07.093+0000",
"indexing_date": "2018-02-10T08:16:23.329+0000",
"filesize": 32048,
"filename": "filename.htm",
"url": """file://D:\tmp\path\to\filename.htm"""
},
"path": {
"root": "e1a38f7da342f641e3eefad1ed1ca0f2",
"virtual": "/path/to/filename.htm",
"real": """D:\tmp\path\to\filename.htm"""
}
}
}
]
}
}
I am using NEST Api to get document file.filename from elastic search 6 on same server.
ISSUE:
Even though I have mentioned above too. Problem is filename is returned null in NEST API while content does return.
SOLUTION 1:
Using settings.DisableDirectStreaming(); I retrieved JSON result and created Following Class:
New Class:
public class Rootobject
{
public int took { get; set; }
public bool timed_out { get; set; }
public _Shards _shards { get; set; }
public Hits hits { get; set; }
}
public class _Shards
{
public int total { get; set; }
public int successful { get; set; }
public int skipped { get; set; }
public int failed { get; set; }
}
public class Hits
{
public int total { get; set; }
public float max_score { get; set; }
public Hit[] hits { get; set; }
}
public class Hit
{
public string _index { get; set; }
public string _type { get; set; }
public string _id { get; set; }
public float _score { get; set; }
public _Source _source { get; set; }
}
public class _Source
{
public string content { get; set; }
public Meta meta { get; set; }
public File file { get; set; }
public Path path { get; set; }
}
public class Meta
{
public string title { get; set; }
public Raw raw { get; set; }
}
public class Raw
{
public string XParsedBy { get; set; }
public string Originator { get; set; }
public string dctitle { get; set; }
public string ContentEncoding { get; set; }
public string ContentTypeHint { get; set; }
public string resourceName { get; set; }
public string ProgId { get; set; }
public string title { get; set; }
public string ContentType { get; set; }
public string Generator { get; set; }
}
public class File
{
public string extension { get; set; }
public string content_type { get; set; }
public DateTime last_modified { get; set; }
public DateTime indexing_date { get; set; }
public int filesize { get; set; }
public string filename { get; set; }
public string url { get; set; }
}
public class Path
{
public string root { get; set; }
public string _virtual { get; set; }
public string real { get; set; }
}
Query:
Instead of TermQuery I used MatchQuery here is my query, connection string is as above:
var request = new SearchRequest
{
From = 0,
Size = 1,
Query = new MatchQuery { Field = "content", Query = txtsearch.Text }
};
New Problem:
I tried much, though response does contain whole JSON result, but it is not being mapped properly.
I tried using Rootobject, Hits and Hit class but results only returned for _source as:
var response = client.Search<_Source>(request);
var twet = response.Documents.Select(t => t.file.filename).ToList();
Now I can retrieve content and file name but if I try using previous classes. The Hits and hits.total are returned as null.
I tried following queries:
var twet = response.Documents.SelectMany(t => t.hits.hits.Select(k => k._source.content)).ToList();
and
var twet1 = response.Hits.SelectMany(t => t.Source.hits.hits.Select(k => k._source.content)).ToList();
and
var twet1 = response.Documents.Select(t => t.Filename.fileName).ToList();
and
var twet = response.HitsMetadata.Hits.Select(t => t.Source.filename).ToList();
using Rootobject , Hits, Hit classes. While response does contain it.
So how can I use Rootobject class instead so that I can get whatever I want.
The elastic search server returns response as a JSON string and then Nest deserializes it into your required class.
In your case filename is a nested property inside the file property. To deserialize nested JSON properties check this link How to access nested object from JSON with Json.NET in C#
public class MyClass
{
public string Content { get; set; }
public FileClass File { get; set; }
}
public class Fileclass
{
public string Filename { get; set; }
}
And then you can read filename like response.Documents.Select(t=>t.File.Filename).ToList();
I have the following end point on my asp.net web api that I use to post new users and receive user object from body.
// POST: api/Users
[HttpPost]
public async Task<IActionResult> PostUser([FromBody] User user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
user.DateCreated = DateTime.Now;
//user.LastLogin = DateTime.Now;
var hashedPassword = BCrypt.Net.BCrypt.HashPassword(user.Password);
user.Password = hashedPassword;
_context.User.Add(user);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
if (UserExists(user.Id))
{
return new StatusCodeResult(StatusCodes.Status409Conflict);
}
else
{
Console.WriteLine(ex.Message);
}
}
return CreatedAtAction("GetUser", new { id = user.Id }, user);
}
One property of user is a Role object defined by the following class:
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? LastUpdate { get; set; }
}
public class User
{
public int Id { get; set; }
public Role role { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public DateTime? DateCreated { get; set; }
public DateTime? LastLogin { get; set; }
}
The question is: How to I create a new user using the post endpoint? How to I pass the Role into user object? Note that I am able to create new user passing null on the role property. Now I want to pass an existing role.
This is what I am passing via Postman and angular2 app, but not getting through. return 201 status but not saving...
{
"role": {
"id": 1,
"name": "Admin",
"description": "Admin",
"dateCreated": "2016-01-01T00:00:00",
"lastUpdate": null
},
"firstName": "gggg",
"lastName": "gggg",
"emailAddress": "gggg",
"userName": "ggg",
"password": "$2a$10$z5dKRLqrus7i0fIluMynluJ8EOI2ko5vNGjBrBbfRaP738zHmc866",
"dateCreated": "2016-08-16T21:24:18.144517+02:00",
"lastLogin": null
}
I have already solved this issue. Data that i was passing via body was correct (model state was valid) but there was an internal error caused my entity framework core. It was trying to recreate a role attached to the user although the role already existed.