How to send a collection of files + JSON data to the client side - asp.net

In my ASP.NET Core Web API I have an entity Idea:
public class Idea
{
public int Id { get; set; }
public string Name { get; set; }
public string OwnerId { get; set; }
public User Owner { get; set; }
public string Description { get; set; }
public int? MainPhotoId { get; set; }
public Photo MainPhoto { get; set; }
public int? MainVideoId { get; set; }
public Video MainVideo { get; set; }
public ICollection<Photo> Photos { get; set; }
public ICollection<Video> Videos { get; set; }
public ICollection<Audio> Audios { get; set; }
public ICollection<Document> DocumentsAboutTheIdea { get; set; }
public DateTime DateOfPublishing { get; set; }
}
public class Photo
{
public int Id { get; set; }
public string Url { get; set; }
}
(the different media-type classes are equivalent)
When the client makes a Post request for creating the Idea he sends the information about it along with all the media-files (I am using IFormFile and IFormFileCollection) and while saving them on the server I set the Url property to match their location. But in the Get request I want to send the files (not Urls).
Here is the Get action which now returns only one file (mainPhoto) without any JSON and without any other media-files:
[HttpGet("{id}", Name = "Get")]
public async Task<IActionResult> Get(int id)
{
var query = await unitOfWork.IdeaRepository.GetByIdAsync(id, includeProperties: "Owner,MainPhoto,MainVideo,Photos,Videos,Audios,DocumentsAboutTheIdea");
if (query != null)
{
string webRootPath = hostingEnvironment.WebRootPath;
var path = string.Concat(webRootPath, query.MainPhoto.Url);
var memory = new MemoryStream();
using (var stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, GetContentType(path), Path.GetFileName(path));
}
return NotFound();
}
So in a Get request I want to send to the client side (an Angular app) some of the information about the Idea in a JSON-format ALONG WITH different (NOT ONE) media-files associated with it. My goal is to make the client side get it all so that it can then show them on the page of the info about the Idea. But I cannot figure out the approach for this. Is there a way to achieve this? Or there is another (better) way of interacting with the client side to transfer all the required data? I tried to find information on this topic but couldn't find any.

Related

Handle POST requests with many-to-many relationships ASP.NET EFCore

I have these classes
public class Project
{
public Guid Id { get; set; }
public string DeployUrl { get; set; }
public List<Technology> Techs { get; set; } = new();
}
public class Technology
{
public Guid Id { get; set; }
public string Name { get; set; }
public string TechImg { get; set; }
[JsonIgnore]
public List<Project> Projects { get; set; } = new();
[JsonIgnore]
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
Which creates a many-to-many relationship. I want to add an endpoint to create a Project. The main issue here is the approach with which i'll do it. Since I don't send the full Technology object to the frontend, it would be impossible to send back a full Project as the request body for the endpoint since its Technology property would be missing fields, which posses an incompatibility with the actual entity in my server. Of course that the Technology instance already exists. So, what is the best approach here? Currently I'm sending a Project DTO that doesn't contain a List<Technology> but a List<Guid> of technologies, and in my repository I fetch from the DB all those technologies and add them manually. Is this correct? Thanks in advance.
public class ProjectPostDto
{
public string DeployUrl { get; set; }
public List<Guid> TechsIds { get; set; } = new();
}
public async Task<ActionResult> PostProject(ProjectPostDto projectDto)
{
try
{
Project newProject = FromPostDto(projectDto);
newProject.Techs = await Service.GetTechs(projectDto.TechsIds);
await Service.Create(newProject);
ProjectPostDto newProjectDto = ToPostDto(newProject);
return CreatedAtAction(nameof(GetProject), new { id = newProject.Id }, newProjectDto);
} catch (Exception)
{
return StatusCode(500, "Server error");
}
}

Xamarin Forms SQLLite add OneToOne and OneToMany subobjects to db

Im trying to create a persistent sqllite db (creation tables once you install the app, deletion of db when you uninstall app)
I have a issue that I cant save my sub objects for example
public class ObjectInstanceResponseModel : GenericResponseModel
{
public ObservableCollection<ObjectInstanceData> ObjectInstances { get; set; }
}
public class ObjectInstanceData : GenericResponseModel
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[JsonProperty("idObjectinstance")]
public int IdObjectInstance { get; set; }
[JsonProperty("objectclass_idObjectclass")]
public int ObjectClassIdObjectClass { get; set; }
[JsonProperty("objectclassname")]
public string ObjectClassName { get; set; }
[JsonProperty("visibilitylevel")]
public int VisibilityLevel { get; set; }
[JsonProperty("showname")]
public bool ShowName { get; set; }
[JsonProperty("showicon")]
public bool ShowIcon { get; set; }
[JsonProperty("creationtime")]
public DateTimeOffset CreationTime { get; set; }
[JsonProperty("users_idUsers")]
public int UsersIdUsers { get; set; }
[JsonProperty("isfavorite")]
public bool? IsFavorite { get; set; }
[OneToMany("ObjectInstanceDataStrings")]
[JsonProperty("strings")]
public List<String> Strings { get; set; }
}
public class String
{
[PrimaryKey, AutoIncrement]
[JsonProperty("idObjectparameterstring")]
public int? IdObjectParameterString { get; set; }
[ForeignKey(typeof(ObjectInstanceData))]
public int ObjectInstanceDataStrings { get; set; }
[JsonProperty("stringvalue")]
public string StringValue { get; set; }
[JsonProperty("objectparameterset_idObjectparameterset")]
public int? ObjectParameterSetIdObjectParameterSet { get; set; }
[JsonProperty("showinballon")]
public bool? ShowInBallon { get; set; }
[JsonProperty("idClassparameter")]
public int IdClassParameter { get; set; }
[JsonProperty("classparametername")]
public string ClassParameterName { get; set; }
}
So, my class String is always empty, although there are some rows in the table that I created Strings..
Am I need a lazy loading for this?
I implemented sqllite through depedency service in my app.cs like this:
public partial class App : Application
{
public static SQLiteConnection DatabaseConnection { get; set; }
public App()
{
InitializeComponent();
DatabaseConnection = DependencyService.Get<IConnection>().GetConnection();
CreateTables();
}
private void CreateTables()
{
DatabaseConnection.CreateTable<ObjectInstanceData>();
DatabaseConnection.CreateTable<Models.Objects.String>();
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
So, basically the logic should be when no internet, work with sql lite (keep local changes), and when internet come back upload that changes that kept in db, and erase data from tables.
You noticed that Im using response model for api.
So, Im calling from my FavoriteObjectViewModel this:
var response = await ApiServiceProvider.GetObjectInstances(null, true);
and in the ApiServiceProvider:
public static async Task<ObjectInstanceResponseModel> GetObjectInstances(string queryString = null, bool? onlyFavorites = null)
{
HttpResponseMessage response;
response = await apiClient.GetAsync(objectInstancesEndpoint);
var resultContent = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<ObservableCollection<ObjectInstanceData>>(resultContent);
objectInstanceResponse.ObjectInstances = result;
//after each api call I need to update db
//delete previous data, and add fresh data from api
App.DatabaseConnection.DeleteAll<ObjectInstanceData>();
foreach (var item in result)
{
App.DatabaseConnection.Insert(item);
if (item.Strings != null && item.Strings.Count > 0)
App.DatabaseConnection.InsertAll(item.Strings);
}
//I only get the data for ObjectInstanceData, Strings model is empty!
var objectsResponseDb = App.DatabaseConnection.GetAllWithChildren<ObjectInstanceData>();
objectInstanceResponse.Succeeded = true;
return objectInstanceResponse;
}
So, my questions are:
If I create tables each time in App.cs is that mean, that I not store data, when user quit application, and re-enter again?
Why is the model Strings empty? When I invoke var strings = App.DatabaseConnection.GetAllWithChildren<Models.Objects.String>(); I can see that there is data?
What is the best approach for doing offline "logging"? maybe there is a some better nuget for sqllite?
I don't know much about persistent databases but one thing I can tell you for sure:you're approach is wrong.
You should create a separate class for the database's logic like creating tables and instantiating the connection along with other methods for manipulating data.
In the App class you can create a static resource of the database class which you can call it and make use of the methods in it.
I'm not very good with explaining but here it is a very basic example of how it should look:https://learn.microsoft.com/en-us/xamarin/get-started/tutorials/local-database/?tutorial-step=2&tabs=vswin.

Troubleshooting model binding problem in ASP.NET Core 3.1 API

I'm trying to send an object via a POST request to my ASP.NET Core 3.1 API but I keep getting Bad Request error. As far as I can see, I do have a class that matches what I'm expecting perfectly but clearly it's not. How can I see exactly what the problem is?
The following fails:
public async Task<IActionResult> Post([FromBody] MyCustomObject input)
{
// Do something here...
}
If I use a dynamic, it works fine. So the following code works fine:
public async Task<IActionResult> Post([FromBody] dynamic input)
{
// Do something here...
}
As I said, I'm just getting a 400, Bad Request error. I've been going over MyCustomObject again and again and it looks identical to the object that I'm sending.
Here's what my custom class looks like:
public class CreateContactVm
{
[GuidEmptyNotAllowed]
public Guid AccountId { get; set; }
[Required]
public string AccountName { get; set; }
[GuidEmptyNotAllowed]
public Guid ContactGroupId { get; set; }
[IntZeroNotAllowed]
public int ContactType { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string EntityName { get; set; }
public List<AddEmailVm> Emails { get; set; } = new List<AddEmailVm>();
public List<AddPhoneNumberVm> PhoneNumbers { get; set; } = new List<AddPhoneNumberVm>();
public List<AddAddressVm> Locations { get; set; } = new List<AddAddressVm>();
}
Here, I use some custom validations such as [GuidEmptyNotAllowed] or [IntZeroNotAllowed]. I inspect the object I send via my POST call and it satisfies ALL of these requirements and yet it still fails.
How can I get more information about why my API method is throwing a 400 error?
UPDATE:
The following code allows me to convert what comes in as a dynamic to my CreateContactVm custom class but I really shouldn't have to do this at all:
CreateContactVm request = new CreateContactVm();
try
{
var element = (JsonElement)input; // input is the dynamic received
request = JsonUtils.Deserialize<CreateContactVm>(element.GetRawText());
}
catch(Exception e)
{
var error = e.Message;
}
This also proves that the issue is with model binding. Something in my custom class is not liking the JSON object it receives.

Using Firesharp in Xamarin.Forms to get Data from Firebase Database

I want to push and get Recipe-Data from my Firebase Database in Xamarin.Forms with the Firesharp Plugin.
My Model Class is the Recipe Class:
public class Recipe
{
public string title { get; set; }
public string workTime { get; set; }
public string degreeOfDifficulty { get; set; }
public string caloriesPerPerson { get; set; }
public string preparation { get; set; }
public string cookingBakingTime { get; set; }
public string restTime { get; set; }
public int portions { get; set; }
public string pictureSource { get; set; }
public List<Ingredient> ingredients { get; set; }
public Recipe()
{
ingredients = new List<Ingredient>();
}
}
So Push Recipe-Objects to the Firebase DB works:
public async Task PushRecipe(Recipe recipe)
{
IFirebaseClient client = new FirebaseClient(config);
client.Push("Recipes", recipe);
}
Firebase Example
But when i want to get Data from the Database i get an Error..
public async Task<List<Recipe>> GetAllRecipes()
{
IFirebaseClient client = new FirebaseClient(config);
FirebaseResponse response = await client.GetAsync("Recipes");
try
{
List<Recipe> recipelist = response.ResultAs<List<Recipe>>();
return recipelist;
} catch (Exception e){
}
return null;
}
After this:
List<Recipe> recipelist = response.ResultAs<List<Recipe>>();
this Exception comes..
"{Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[dignaBon_App.Models.Recipe]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correc…}"
I don´t understand why i can´t get Data from the Database back..
Can someone help me?
You need instatiate the "config" with your firebase secret address key. Go to your Firebase console for this.

ASP.NET web API casting http response to json array

My Code works fine when calling REST URL:
http://api.feedzilla.com/v1/categories.json
but when I call following URL I get error:
http://api.feedzilla.com/v1/categories/15/articles.json?count=36&since=2012-11-15&client_source=&order=relevance&title_only=0&
Error:
{"Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.IEnumerable`1[Nitin.News.DAL.Resources.Article]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.\r\nPath 'articles', line 1, position 12."}
My Code is as follows:
public class Article
{
public string publish_date { get; set; }
public string source { get; set; }
public string source_url { get; set; }
public string summary { get; set; }
public string title { get; set; }
public string url { get; set; }
}
public IEnumerable<Article> Get()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://api.feedzilla.com/v1/");
//Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// call the REST method
HttpResponseMessage response = client.GetAsync("http://api.feedzilla.com/v1/categories/2/articles.json??count=36&since=2012-11-15&client_source=&order=relevance&title_only=0&").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
return response.Content.ReadAsAsync<IEnumerable<Article>>().Result;
//wont work
//string JSON =response.Content.ReadAsStringAsync().Result;
//return JsonConvert.DeserializeObject<IEnumerable<T>>(JSON);
}
else
{
throw new Exception(string.Format("Data access faild,{0} ({1}) method:{2}", (int)response.StatusCode, response.ReasonPhrase, MethodURL));
}
}
You need another level in your object hierachy... i.e. a root to contain the IEnumerable.
Runing the JSON into the tool at http://json2csharp.com/ generates the following proxy classes:
public class Enclosure
{
public int length { get; set; }
public string media_type { get; set; }
public string uri { get; set; }
}
public class Article
{
public string author { get; set; }
public string publish_date { get; set; }
public string source { get; set; }
public string source_url { get; set; }
public string summary { get; set; }
public string title { get; set; }
public string url { get; set; }
public List<Enclosure> enclosures { get; set; }
}
public class RootObject
{
public List<Article> articles { get; set; }
public string description { get; set; }
public string syndication_url { get; set; }
public string title { get; set; }
}
You just need to change your code to this then:
// Parse the response body. Blocking!
return response.Content.ReadAsAsync<RootObject>().Result.articles;
Obviously strip out any properties you dont need. Just thought I would show them all out of interest.

Resources