Byte array and JSON in [FromBody] - angularjs

I am trying pass an object which consists of different data type. I am always getting null value for orderDetails in Web API.
However if do this,
purchaseOrder.Attachments = null,
in the client then orderDetails is no longer null and I have other informations like "SendEmail" and PurchaseOrderNumber.
It looks I might not be correctly set the parameter in the client (angular 2).
However testing the same Web Api method from Console app works fine and I am not getting a null value.
Do I need to separate the JSON data and byte array?
regards,
-Alan-
Models
public class Attachments
{
public int AttachmentId { get; set; }
public string FileName { get; set ;}
public byte[] FileData { get; set ;}
}
public class UpdatePurchaseOrderViewModel
{
public bool SendEmail { get; set; }
public int PurchaseOrderNumber { get; set; }
public Attachments Attachments { get; set;
}
Here is my Web API put method definition
[HttpPut("AddPurchaseOrderNumber/{purchaseOrderId}")]
public StatusCodeResult AddPurchaseOrderNumber(int purchaseOrderId, [FromBody] UpdatePurchaseOrderViewModel orderDetails)
{
try
{
var status = _service.AddPurchaseOrderNumber(purchaseOrderId, orderDetails);
if (status == 200)
_unitOfWorkAsync.SaveChanges();
else return StatusCode(status);//No Data
}
catch
{
return StatusCode(400); // Bad Request
}
return StatusCode(200);//OK
}
Typescript snippet
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept','application/json');
let options = new RequestOptions({ headers: headers });
var body = JSON.stringify(
purchaseOrder
);
var uri = 'http://localhost:33907/api/purchaseorder/addpurchaseordernumber/' + purchaseOrderId;
return this._http.put(uri, body , options)
.map((response: Response) => {
let data = response.json();
if (data) {
return true;
}
else {
return false;
}
})
Update
The orderDetails is created as below
let file = Observable.create((observer) => {
let fr = new FileReader();
let data = new Blob([this.attachment]);
fr.readAsArrayBuffer(data);
fr.onloadend = () => {
observer.next(fr.result);
observer.complete();
};
fr.onerror = (err) => {
observer.error(err);
}
fr.onabort = () => {
observer.error("aborted");
}
});
file.map((fileData) => {
//build the attachment object which will be sent to Web API
let attachment: Attachments = {
AttachmentId: '0',
FileName: this.form.controls["attachmentName"].value,
FileData: fileData
}
//build the purchase order object
let order: UpdatePurchaseOrder = {
SendEmail: true,
PurchaseOrderNumber:this.form.controls["purchaseOrderNumber"].value * 1, //for casting purpose
Attachments: attachment
}
console.log("Loading completed");
return order;
})

When sending objects that have byte arrays as a property back and forth between a client to a WebAPI endpoint, I typically use a DTO that stores the property to explicitly define it as a Base64 string. On the server side I map the DTO to my entity by converting the Base64 string to / from the byte array for server side operations and storing in the database.
The serializer will do something like this automatically but the format passed from JavaScript may not match what the WebAPI JSON serializer is expecting (which is why it's working from your C# Console App).
You didn't include how you are creating the purchaseOrder object in your JavaScript so I can't comment on how that object is being setup - which may be where your issue is.

Related

How can i fetch datas positioned in list and send it to another api's query parameter?

i need to fetch a string data from an api (this data located in a list) and i need to send this data to another api as a query parameter. I dunno how to send this as query parameter because this data positioned in a list.
For a better understating : in this api there is a key named steamid i need to fetch this steam id each time and send those as a parameter to this api
class GDCubit extends Cubit<GDState> {
GDCubit({
required this.steamService,
required this.steamReviews,
}) : super(GDInitial()) {
emit(GDInitial());
}
final SteamService steamService;
final SteamReviews steamReviews;
late final steamdata;
late final steamreview;
late final player;
late String appId = '2208920';
late String userId = '76561198078971744';
late String? id = steamReviews.reviews![0].author?.steamid; // I tried to send first users steam id but cannot initialize
void getDatas() async {
try {
emit(GDLoading());
steamdata = await steamService.fetchRelatedAppWithId(appId);
steamreview = await steamService.fetchSteamReviewsRelatedAppId(appId);
player = await steamService.fetchPlayerInfo(id);
//player = await steamService.fetchPlayerInfo(userId); //works properly but manually
emit(GDLoaded(steamdata, steamreview, player));
} catch (e) {
return print('${e.toString()}' 'Error');
}
}
}
`
I tried to send steamid of first user in list but cannot initialize it
Are you trying to initialize the variable id before steamReviews, try creating the id inside your getDatas method, like this:
class GDCubit extends Cubit<GDState> {
GDCubit({
required this.steamService,
required this.steamReviews,
}) : super(GDInitial()) {
emit(GDInitial());
}
final SteamService steamService;
final SteamReviews steamReviews;
late final steamdata;
late final steamreview;
late final player;
late String appId = '2208920';
late String userId = '76561198078971744';
void getDatas() async {
try {
emit(GDLoading());
steamdata = await steamService.fetchRelatedAppWithId(appId);
steamreview = await steamService.fetchSteamReviewsRelatedAppId(appId);
final id = steamReviews.reviews![0].author?.steamid; // Create id here
player = await steamService.fetchPlayerInfo(id);
emit(GDLoaded(steamdata, steamreview, player));
} catch (e) {
return print('${e.toString()}' 'Error');
}
}
}

How to parse this JSON Array in Flutter?

I am working with Flutter and am currently trying to create a graph. I am looking to parse this JSON Array from the link below. My issue is that the information provided in the "prices" object, the values are all inside arrays themselves. I want to get those values and split them into an X and Y list but I have no idea how to accomplish this. I posted a snippet of the JSON data below.
https://api.coingecko.com/api/v3/coins/bitcoin/market_chartvs_currency=usd&days=1
I am only familiar with parsing data by creating a class and constructor. Then create a fromJSON(Map<String, dynamic> json) class and putting the data into a list, as shown in the code snippet below that I created from another URL with object values. How could I go about parsing this array JSON data into two list data?
CODE TO PARSE JSON
List<Coins> _coins = List<Coins>();
Future<List<Coins>> fetchCoins() async {
var url = 'URL';
var response = await http.get(url);
var coins = List<Coins>();
if (response.statusCode == 200) {
var coinsJSON = json.decode(response.body);
for (var coinJSON in coinsJSON) {
coins.add(Coins.fromJson(coinJSON));
}
}
return coins;
}
#override
void initState() {
fetchCoins().then((value) {
setState(() {
_coins.addAll(value);
});
});
super.initState();
}
class Coins{
String symbol;
String name;
Coins(this.symbol, this.name);
Coins.fromJson(Map<String, dynamic> json) {
symbol = json['symbol'];
name = json['name'];
JSON DATA SNIPPET
{
"prices":[
[
1566344769277,
10758.856131083012
],
[
1566345110646,
10747.91694691537
],
[
1566345345922,
10743.789313302059
],
]
}
EDIT: SOLVED WITH THE HELP OF #EJABU.
class HistoricalData {
List prices;
List<num> listX = [];
List<num> listY = [];
HistoricalData(this.prices,this.listX, this.listY);
HistoricalData.fromJson(Map<String, dynamic> json) {
prices = json['prices'];
for (var price in prices) {
listX.add(price[0]);
listY.add(price[1]);
}
}
You may try this...
New class Coins definition:
class Coins {
List<num> listX = [];
List<num> listY = [];
Coins(this.listX, this.listY);
Coins.fromJson(Map<String, dynamic> json) {
List<List<num>> prices = json['prices'];
for (var price in prices) {
listX.add(price[0]);
listY.add(price[1]);
}
}
}
Then later you can fetch it by these lines :
// Future<List<Coins>> fetchCoins() async { // Remove This
Future<Coins> fetchCoins() async {
var url = 'URL';
var response = await http.get(url);
// var coins = List<Coins>(); // Remove This
Coins coins;
if (response.statusCode == 200) {
var coinsJSON = json.decode(response.body);
// Remove This
// for (var coinJSON in coinsJSON) {
// coins.add(Coins.fromJson(coinJSON));
// }
//
coins = Coins.fromJSON(coinsJSON);
}
return coins;
}
Accessing Data in Widget
In Widgets , our expected variable resides as property inside Coins class.
For example, if you use FutureBuilder, you may use these lines:
child: FutureBuilder(
future: fetchCoins(),
builder: (_, __) {
return SomeChartWidget(
listX: coins.listX,
listY: coins.listY,
);
},
),
Generating Serializers automatically
I suggest you take a look at https://pub.dev/packages/json_serializable, which is a package that does the boilerplate code generation for you. Although it might me a bit overkill to add something like this to your code or your workflow, automatically generating serializers is very convenient.
Not that in order to have custom sub-classes, they need to provide serialization as well.
If you want to extend your knowledge even further, you can also have a look at https://pub.dev/packages/built_value and https://pub.dev/packages/built_value_generator

Delete not working with Web Api

When I try to use the DELETE verb I either get a null parameter or the controller doesn't fire.
First I tried this:
[HttpDelete]
public IHttpActionResult Delete(Announcement announcement) {
_unitOfWork.Announcements.Remove(announcement);
_unitOfWork.Complete();
return Ok();
}
The controller fires, but announcement is null. If I check on the client side the parameter is not null, it is a properly formed object.
If I add a Route attribute like the below, then the controller doesn't fire at all.
[HttpDelete]
[Route("api/announcements/{announcement}")]
public IHttpActionResult Delete(Announcement announcement) {
_unitOfWork.Announcements.Remove(announcement);
_unitOfWork.Complete();
return Ok();
}
The client side is initiating the DELETE via angular.
myAPIservice.DeleteAnnouncement = function (announcement) {
console.log('In myAPIservice DeleteAnnouncement');
console.log(announcement);
return $http.delete(serviceURLRoot + 'api/announcements/', announcement, { withCredentials: true }).success(function (data) {
console.log('myAPIservice.DeleteAnnouncement Success');
});
};
EDIT ---
The Announcement class:
public class Announcement {
public int AnnouncementId { get; set; }
public string AnnouncementText { get; set; }
}
You can't send a 'body' with a DELETE call.
You could send the announcement id in the form of a parameter:
myAPIservice.DeleteAnnouncement = function (announcementId) {
console.log('In myAPIservice DeleteAnnouncement');
console.log(announcement);
return $http.delete(serviceURLRoot + 'api/announcements/', announcementId, { withCredentials: true }).success(function (data) {
console.log('myAPIservice.DeleteAnnouncement Success');
});
};
Then retrieve it from your database and delete it server side:
[HttpDelete]
[Route("api/announcements/{announcementId}")]
public IHttpActionResult Delete(int announcementId) {
var announcement = _unitOfWork.GetAnnouncementById(announcementId);
_unitOfWork.Announcements.Remove(announcement);
_unitOfWork.Complete();
return Ok();
}
Or of course delete by id... whatever works.
The important part to note here is that DELETE can't carry a payload / body.

Json array is null when received in controller

function viewReports(firstDate, lastDate) {
var selected = $('#ReportSelected').find(":selected");
var controller = "PortalReports";
var method = "GetReport";
var urlAjax = $("#basePath").val() + controller + "/" + method;
var companydropdown = $('#ReportSelected :selected').data("companydropdown");
var agencydropdown = $('#ReportSelected :selected').data("agencydropdown");
var userdropdown = $('#ReportSelected :selected').data("userdropdown");
var data =
{
reportSelected: selected.text(),
firstDate: firstDate,
lastDate: lastDate,
companydropdown: companydropdown,
agencydropdown: agencydropdown,
userdropdown: userdropdown
};
/*var data =
[{
"reportSelected": selected.text(),
"firstDate": firstDate,
"lastDate": lastDate,
"companydropdown": companydropdown,
"agencydropdown": agencydropdown,
"userdropdown": userdropdown
}];*/
var answer = JSON.stringify({ data });
$.ajax({
traditional: true,
data: JSON.stringify({ data }),
url: urlAjax,
success: function (response) {
loadReport(response);
},
error: function (ob, errStr) {
alert("An error occured. Please try again.");
}
});
//Mvc
public JsonResult GetReport(JArray data)
{
var persons = data.Select(x => x.ToObject<InputJson>());
JArray data is always null irrespective of how many ways I add square brackets of remove quotation marks etc, what am I doing wrong!!!
Prefer simple Object returned in array for readability as I might need to add to it.
Since you are sending a complex data structure(array), you should specify the contentType property when making ajax call. Specify the value of contentType property as "application/json"
//Values hard coded, you may replace with real values from your form.
var dataArray = [{
reportSelected:'201501',
firstDate: '11/12/2010',
lastDate: '12/12/2010',
companydropdown: 4,
agencydropdown: 6,
userdropdown: 16,
}];
var urlAjax = "/Home/GetReport"; // Hard coded for demo. Read further..
$.ajax({
type: "POST",
contentType: "application/json",
data: JSON.stringify(dataArray),
url: urlAjax,
success: function (response) {
console.log(response);
},
error: function (ob, errStr) {
alert("An error occured. Please try again.");
}
});
I suggest you create a view model /DTO to represent the data you are sending and use that in your action method.
public class ReportRequest
{
public string reportSelected { get; set; }
public DateTime firstDate { get; set; }
public int companydropdown { get; set; }
}
[HttpPost]
public JsonResult GetReport(IEnumerable<ReportRequest> data)
{
//do something with data
// to do : Return something
}
In the example, I hardcoded the value of urlAjax variable. You may consider using the html helper methods to generate the correct relative url path to the action method(s) as explained in this post.

Anotar.Serilog and logging

I premit that I'm quite new to Anotar / Serilog, I've my WPF application that uses the Repository Pattern and I've the repo strucured as
public class CalendarRepository : DefaultRepositoryBase, ICalendarRepository
{
public Task<IList<CalendarTemplate>> GetCalendarTemplatesAsync(int? template)
{
JsonServiceClient client = GetServiceStackClient();
var request = new CalendarTemplatesRequest
{
Template = template
};
return client.PostAsync(request);
}
public Task<IList<Currency>> GetCurrenciesAsync(int? currency)
{
JsonServiceClient client = GetServiceStackClient();
var request = new CurrenciesRequest
{
Currency = currency
};
return client.PostAsync(request);
}
public Task<IList<CurrencyCalendar>> GetCurrencyCalendarsAsync(IEnumerable<int> currencies)
{
JsonServiceClient client = GetServiceStackClient();
var request = new CurrencyCalendarsRequest
{
Currencies = currencies
};
return client.PostAsync(request);
}
Right now I was logging in the viewmodels as
LogTo.Information("Getting calendar currencies {SelectedCurrencies}",selectedCurrenciesId.Select(x=>x.Id));
var items = await repository.GetCurrencyCalendarsAsync(selectedCurrenciesId.Select(x => x.Id));
I was wondering if there's an Attribute that I can apply to a class in order to automatically log the Method and the parameters.
Right now my logged is configured as
var log =
new LoggerConfiguration().MinimumLevel.ControlledBy(
levelSwitch: new LoggingLevelSwitch(LogEventLevel.Debug)).WriteTo.File(#".\logs\serilog.log",outputTemplate: "{Timestamp: yyyy-MM-dd HH:mm:ss.fff} [{Level}] [{SourceContext}] {Message}{NewLine}{Exception}").CreateLogger();
Serilog.Log.Logger = log;
Thanks
You can get the method name and line number by adding {Method} and {LineNumber} to the output template respectively.
As far as I know, there is no way to automatically log the parameters; you would need to do this manually.

Resources