I have been given this API which is based in PHP but I need to convert it into C#. This is only the first method and this is my result.
I seem to be missing a lot. Will this c# method function as the PHP ones does
PHP:
// Call SVP Service
function callSVPService($service, $arg, $raw_data = false) {
global $entry_point;
$count_attempts = 3;
$token_error_codes = array(2004, 2005, 2006);
$counter = 0;
// Make a attempts to call a SVP API service.
// With this we can automatically generate a new token if the token is empty,
// the token is not valid or the validation period of the token is expired.
while(true) {
$counter++;
$token_response = getToken();
// If token error occurs - exit calling service.
if($token_response['error_code']) {
return array('error_code' => $token_response['error_code'], 'response' => '');
}
$token = $token_response['token'];
$arg['token'] = $token;
$response = callSVP_API_Service($service, $arg);
$result = getTextBetweenTags($response, 'result', true);
if(!$result) {
if(!$raw_data) {
return array('error_code' => -1, 'response' => $response);
}
else if(!$response) {
return array('error_code' => -1, 'response' => 'Empty server response');
}
else {
$error_code = 0;
}
}
else if($result == 'ERROR') {
$error_code = getTextBetweenTags($response, 'code', true);
}
else if($result == 'OK') {
$error_code = 0;
}
if(!$error_code) {
return array('error_code' => 0, 'response' => $response);
}
else if(in_array($error_code, $token_error_codes)) {
// The service response contains a token error. Try to generate a new token.
saveToken(0);
}
if($counter >= $count_attempts) {
return array('error_code' => $error_code, 'response' => $response);
}
}
}
C#
public dynamic callSVPService(dynamic service, dynamic arg, dynamic raw_data)
{
var countAttempt = 3;
int[] tokenErrorCodes;
tokenErrorCodes = new int[2004];
tokenErrorCodes = new int[2005];
tokenErrorCodes = new int[2006];
var counter = 0;
while (true)
{
counter++;
var tokenResponse = getToken();
var token = tokenResponse["token"];
var response = callSVPAPIService(service, arg);
var result = getTextBetweenTags(response, "result", true);
return result;
}
}
Related
I'm passing in a selected row to delete in my Angular Application using selection on a material data table. For some reason though, I'm getting a 415 error. Not sure what I'm doing wrong, either on the server or the client side, but I'm not sure even if I'm passing the correct object.
What's the issue here? I'm using Angular 7 for the client and making the API in .NET Core
ActionsController.cs .NET Core
[HttpDelete("deleteRow")]
public Msg DeleteRows(string sessionId, T table, Tag[] rows)
{
try
{
UserState userState = GetUserState(sessionId);
Msg m = CheckTableAccess(sessionId, table, TableAccessLevel.ReadModifyCreateDelete, userState);
if (m.IsNotOk)
return m;
if (table == T.Action)
{
foreach (Tag t in rows)
{
m = CheckUpdatableAction(sessionId, rows[0]);
if (m.IsNotOk)
return m;
}
}
if (table == T.RouteStop)
{
XioTransaction xt = new XioTransaction(userState);
XioWriter xwd = null;
xwd = xt.CreateDeleteWriter(table);
foreach (Tag t in rows)
{
XioTable routeStop = new XioTable(userState, T.RouteStop);
Tag ownerTag = ((DbrRouteStop)routeStop.LoadSingleRow(t, C.RouteStop_RouteTag)).RouteTag;
xwd.DeleteRow(t, ownerTag);
}
xt.WriteAll();
}
else if (table == T.RouteEvent)
{
XioTransaction xt = new XioTransaction(userState);
XioWriter xwd = null;
xwd = xt.CreateDeleteWriter(table);
foreach (Tag t in rows)
{
XioTable routeEvent = new XioTable(userState, T.RouteEvent);
Tag ownerTag = ((DbrRouteEvent)routeEvent.LoadSingleRow(t, C.RouteEvent_RouteTag)).RouteTag;
xwd.DeleteRow(t, ownerTag);
}
xt.WriteAll();
}
else if (table == T.CompanyResource)
{
XioTransaction xt = new XioTransaction(userState);
XioWriter xwd = null;
xwd = xt.CreateDeleteWriter(table);
foreach (Tag t in rows)
{
XioTable cr = new XioTable(userState, T.CompanyResource);
DbrCompanyResource crRec = (DbrCompanyResource)cr.LoadSingleRow(t, C.CompanyResource_CompanyTag, C.CompanyResource_Tab);
XioTable xtr = new XioTable(userState, crRec.Tab);
// the critical where is on divisiontag and all tables that are passed in will have a divion tag
// luckily the code will just look at the field name
xtr.WhereList.Where(C.Driver_DivisionTag, ComparisonOp.EqualTo, crRec.CompanyTag);
xtr.LoadData();
if (xtr.GetAllRows().Length > 0)
return new Msg(M.ResourcesExistAtCompanyLevel);
xwd.DeleteRow(t);
}
xt.WriteAll();
}
else
DbRow.DeleteRecursive(userState, table, rows);
userState.Completed(LogEntryType.DeleteRows, null);
}
catch (MsgException e)
{
return e.Msg;
}
catch (SqlException e)
{
if (e.Number == 547)
{
return new Msg(M.CannotDeleteOwnerRowWithComponent);
}
else
return new Msg(M.UnexpectedViewDeleteError, e.ToString());
}
catch (Exception e)
{
return new Msg(M.UnexpectedViewDeleteError, e.ToString());
}
return Msg.Ok;
}
ViewComponent.ts
export class ViewComponent implements OnInit, OnDestroy {
// User Fields
currentUser: User;
users: User[] = [];
currentUserSubscription: Subscription;
loading : boolean;
// Action Fields
viewData: any;
viewName: string;
refNumber: number;
currentActionSubscription: Subscription;
displayedColumns: string[] = [];
dataSource: any = new MatTableDataSource([]);
pageSizeOptions: number[] = [10, 20, 50];
#ViewChild(MatSort) sort: MatSort;
#ViewChild(MatPaginator) paginator: MatPaginator;
selection = new SelectionModel<TableRow>(true, []);
defaultSort: MatSortable = {
id: 'defColumnName',
start: 'asc',
disableClear: true
};
defaultPaginator: MatPaginator;
constructor(
private iconRegistry: MatIconRegistry,
private sanitizer: DomSanitizer,
private actionService: ActionService
) {
this.loading = false;
this.iconRegistry.addSvgIcon(
'thumbs-up',
this.sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/examples/thumbup-icon.svg'
)
);
}
loadAction(action: any) {
this.loading = true;
// If there is already data loaded into the View, cache it in the service.
if (this.viewData) {
this.cacheAction();
}
if (this.sort) {
// If there is sorting cached, load it into the View.
if (action.sortable) {
// If the action was cached, we should hit this block.
this.sort.sort(action.sortable);
} else {
// Else apply the defaultSort.
this.sort.sort(this.defaultSort);
}
}
if (this.paginator) {
// If we've stored a pageIndex and/or pageSize, retrieve accordingly.
if (action.pageIndex) {
this.paginator.pageIndex = action.pageIndex;
} else { // Apply default pageIndex.
this.paginator.pageIndex = 0;
}
if (action.pageSize) {
this.paginator.pageSize = action.pageSize;
} else { // Apply default pageSize.
this.paginator.pageSize = 10;
}
}
// Apply the sort & paginator to the View data.
setTimeout(() => this.dataSource.sort = this.sort, 4000);
setTimeout(() => this.dataSource.paginator = this.paginator, 4000);
// Load the new action's data into the View:
this.viewData = action.action;
this.viewName = action.action.ActionName;
this.refNumber = action.refNumber;
// TODO: add uniquifiers/ids and use these as the sort for table
const displayedColumns = this.viewData.Columns.map((c: { Name: any; }) => c.Name);
displayedColumns[2] = 'Folder1';
this.displayedColumns = ['select'].concat(displayedColumns);
// tslint:disable-next-line: max-line-length
const fetchedData = this.viewData.DataRows.map((r: { slice: (arg0: number, arg1: number) => { forEach: (arg0: (d: any, i: string | number) => any) => void; }; }) => {
const row = {};
r.slice(0, 9).forEach((d: any, i: string | number) => (row[this.displayedColumns[i]] = d));
return row;
});
this.dataSource = new MatTableDataSource(fetchedData);
this.loading = false;
}
// Stores the current Action, sort, and paginator in an ActionState object to be held in the action service's stateMap.
cacheAction() {
let actionState = new ActionState(this.viewData);
// Determine the sort direction to store.
let cachedStart: SortDirection;
if (this.sort.direction == "desc") {
cachedStart = 'desc';
} else {
cachedStart = 'asc';
}
// Create a Sortable so that we can re-apply this sort.
actionState.sortable = {
id: this.sort.active,
start: cachedStart,
disableClear: this.sort.disableClear
};
// Store the current pageIndex and pageSize.
actionState.pageIndex = this.paginator.pageIndex;
actionState.pageSize = this.paginator.pageSize;
// Store the refNumber in the actionState for later retrieval.
actionState.refNumber = this.refNumber;
this.actionService.cacheAction(actionState);
}
ngOnInit() {
// Subscribes to the action service's currentAction, populating this component with View data.
this.actionService.currentAction.subscribe(action => this.loadAction(action));
}
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
masterToggle() {
this.isAllSelected()
? this.selection.clear()
: this.dataSource.data.forEach((row: TableRow) => this.selection.select(row));
}
// Delete row functionality
deleteRow() {
console.log(this.selection);
this.selection.selected.forEach(item => {
const index: number = this.dataSource.data.findIndex((d: TableRow) => d === item);
console.log(this.dataSource.data.findIndex((d: TableRow) => d === item));
this.dataSource.data.splice(index, 1);
this.dataSource = new MatTableDataSource<Element>(this.dataSource.data);
});
this.selection = new SelectionModel<TableRow>(true, []);
this.actionService.deleteRow(this.selection).subscribe((response) => {
console.log('Success!');
});
}
ActionsService.ts
deleteRow(selection: any): Observable<{}> {
console.log('testing service');
// create an array of query params using the property that you use to identify a table row
const queryParams = [selection._selection].map((row: { value: any; }) => `id=${row.value}`);
// add the query params to the url
const url = `http://localhost:15217/actions/deleteRow`;
return this.http.delete<any>(url);
}
You need to add an http header to specify the content type for your http request body. If you are sending a json body, the header is content-type: application/json
You can update actionService.ts
deleteRow(selection: any): Observable<{}> {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
const queryParams = [selection._selection].map((row: { value: any; }) => `id=${row.value}`);
const url = `http://localhost:15217/actions/deleteRow`;
const options = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
}),
body: {}
}
return this.http.delete<any>(url, options);
}
I have a visual C# application that connects via WebSocket to my remote Node.js server that is deployed using Heroku.
The server uses the npm WebSocket module "ws" to create a WebSocket-Server.
The C# client application uses the WebSocketSharp Library from this GitHub repository: https://github.com/sta/websocket-sharp to create a WebSocket-Client that connects to the server.
Here is the necessary Node Server server.js code:
require('dotenv').config()
var express = require('express');
const API = require('./api_handler').api;
const PORT = process.env.PORT || 5000;
const HOSTNAME = process.env.HOST || '127.0.0.1';
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.post('/', function(req, res){
api.Auth(req.body,res);
});
app.get('/', function(req, res){
res.send(':)');
});
const httpServer = app.listen(PORT, function () {
console.log('Server running at http://' + HOSTNAME + ':' + PORT + '/');
});
const api = new API(httpServer);
Here is the Node Server ApiHandler.js code
class API_Handler{
constructor(httpServer){
const Database = require('./database').db;
const { Server : wsServer} = require('ws');
this.db = new Database();
this.wss = new wsServer({ server: httpServer });
this.actions = {write : 'write', read : 'read', authenticate : 'authenticate'};
this.operations = {insert : 'insert', update : 'update', delete : 'delete', login : 'login', logout : 'logout'};
this.template = {action : 'action', operation : 'operation',categories : 'categories',category : 'category',IDs : 'ids',fields : 'fields',data : 'data',userid : 'userid',password : 'password',Token : 'Token',Logged : 'logged'};
this.status = {ok : 'ok', error : 'error'};
this.CLIENT_TOKENS = [];
this.wsClients = new Object();
this.ClientIDs = new Object();
this.wss.on('connection', (client,req) => {
var cookie = this.CookieParseJSON(req.headers.cookie)
if(this.CLIENT_TOKENS.includes(cookie.Token)){
this.CLIENT_TOKENS.splice(this.CLIENT_TOKENS.indexOf(cookie.Token), 1);
this.wsClients[cookie.Token] = client;
client.on('message', (msg) => {
this.handle(JSON.parse(msg),client);
});
client.on('close', () => {
console.log('Client disconnected');
});
console.log('Client connected');
this.InitializeClient(client);
}
else{
console.log('Unauthorized Client connected');
client.close();
}
});
}
async Auth(req,res){
if(this.template.password in req){
var tmp = JSON.parse(JSON.stringify(req));
tmp.password = (Array.from({length:req.password.length}).map(x=>'*')).join('');
console.log(tmp);
}else{console.log(req);}
var result;
if(this.template.action in req){
if (req.action === this.actions.authenticate){
if(this.template.operation in req){
if(req.operation === this.operations.login){
if(this.template.userid in req && this.template.password in req ){
result = await this.executeLogin(req);
}else{result = this.missingAuthCredentialsResult();}
}else{result = this.invalidAuthOperationResult();}
} else{result = this.noAuthOperationResult();}
}else if(req.operation === this.operations.read || req.operation === this.operations.write || req.operation === this.operations.logout){
result = this.UnAuthedActionResult();
} else{result = this.invalidActionResult();}
}else{result = this.noActionResult();}
res.json(result);
}
async handle(req,client){
console.log(req);
var result;
if(this.template.Token in req){
if(req.Token in this.wsClients){
if(this.template.action in req){
if(req.action === this.actions.authenticate){
if(this.template.operation in req){
if(req.operation === this.operations.logout){
await this.executeLogout(req);
client.close();return;
}else{result = this.invalidAuthOperationResult();}
} else{result = this.noAuthOperationResult();}
}if (req.action === this.actions.read){
if(this.template.categories in req){
if(this.db.validateCategories(req.categories)){
result = await this.executeRead(req);
client.send(JSON.stringify(result));return;
}else{result = this.invalidCategoriesResult();}
}else{result = this.noCategoriesResult()}
}else if (req.action === this.actions.write){
if(this.template.category in req){
if(this.db.validateCategory(req.category) && this.db.isWritableCategory(req.category)){
if(this.template.operation in req){
if(req.operation === this.operations.insert){
if(this.template.data in req){
await this.executeInsert(req);
return;
}else{result = this.noDataResult()}
}else if(req.operation === this.operations.update){
if(this.db.isUpdatableCategory(req.category)){
if(this.template.IDs in req){
if(this.template.fields in req && Array.isArray(req.fields) && req.fields.length > 0){
if(this.template.data in req){
await this.executeUpdate(req);
return;
}else{result = this.noDataResult()}
}else{result = this.noFieldsResult()}
}else{result = this.noIDsResult()}
}else{result = this.invalidCategoryResult();}
}else if(req.operation === this.operations.delete){
if(this.template.IDs in req){
await this.executeDelete(req);
return;
}else{result = this.noIDsResult()}
}else{result = this.invalidOperationResult();}
}else{result = this.noOperationResult();}
}else{result = this.invalidCategoryResult();}
}else{result = this.noCategoryResult();}
}else{result = this.invalidActionResult();}
}else{result = this.noActionResult();}
}else{result = this.invalidTokenResult();}
}else{result = this.noTokenResult();}
client.send(JSON.stringify(result));
client.close();
}
async executeLogin(req){
if(await this.db.authenticate(req.userid,req.password)){ //successfully logged in
console.log("Auth Passed");
var res = new Object();
var token = this.hex();
res[this.template.Token] = token;
res[this.template.Logged] = true;
this.CLIENT_TOKENS.push(token);
this.ClientIDs[token] = req.userid;
return new Promise((resolve,reject) =>{resolve ({ status : this.status.ok, message : this.messages.success.loggedIn, result: res});});
}else{
console.log("Auth Failed");
var res = new Object();
res[this.template.Logged] = false;
return new Promise((resolve,reject) =>{resolve ({ status : this.status.ok, message : this.messages.error.loggedIn, result: res});});
}
}
async executeLogout(req){
this.wsClients[req.Token].close();
delete this.wsClients[req.Token];
delete this.ClientIDs[req.Token];
}
async executeRead(req){
req.categories = this.removeDuplicates(req.categories);
var res = new Object();
var promises = [];
for(var i = 0; i < req.categories.length; i++){ promises[i] = this.db.select(req.categories[i]); }
await Promise.all(promises).then( (results) => {
for(var i = 0; i < results.length; i++){
res[req.categories[i]] = (results[i].command === 'SELECT')?{count: results[i].rowCount, values: results[i].rows} : this.messages.error.selectCategory;
}});
return new Promise((resolve,reject) =>{ resolve ({ status : this.status.ok, message : this.messages.success.read, result: res});});
}
async executeInsert(req){
for(var i = 0; i < req.data.length; i++){
var dbResponse = await this.db.insert(req.category,req.data[i],this.ClientIDs[req.Token]);
}
this.UpdateClientData(req,(req.category === this.db.tables.Transactions || req.category === this.db.tables.ItemTypes));
}
async executeUpdate(req){
for(var i = 0; i < req.ids.length; i++){
var dbResponse = await this.db.update(req.category,req.ids[i],req.fields,req.data[i],this.ClientIDs[req.Token]);
}
this.UpdateClientData(req);
}
async executeDelete(req){
req.ids = this.removeDuplicates(req.ids);
for(var i = 0; i < req.ids.length; i++){var dbResponse = await this.db.delete(req.category,req.ids[i],this.ClientIDs[req.Token]);}
this.UpdateClientData(req);
}
async InitializeClient(client){
var read = await this.ReadInit();
client.send(JSON.stringify(read));
}
async ReadInit(){
return this.executeRead({categories : this.db.tableNames});
}
async UpdateClientData(req, updateSender = false){
var cats = [req.category];
if(req.category === this.db.tables.ItemListings){cats.push(this.db.tables.Transactions);}
var read = await this.ReadAll(cats);
var readString = JSON.stringify(read);
this.wss.clients.forEach((client) => {
if(!updateSender || client !== this.wsClients[req.Token]){
client.send(readString);
console.log("REFRESH: Client updated, columns: " + cats);
}else{
console.log("REFRESH: Sender-Client was skipped");
}
});
};
async ReadAll(cats){
return this.executeRead({categories : cats});
}
removeDuplicates(array){return [...new Set(array)]; }
hex(){
return this.randHex(16);
}
randHex(len) {
var maxlen = 8;
var min = Math.pow(16,Math.min(len,maxlen)-1);
var max = Math.pow(16,Math.min(len,maxlen)) - 1;
var n = Math.floor( Math.random() * (max-min+1) ) + min;
var r = n.toString(16);
while ( r.length < len ) { r = r + this.randHex( len - maxlen ); }
return r;
}
CookieParseJSON(cookieStr){
var sep = cookieStr.indexOf('=');
var key = cookieStr.substr(0,sep);
var value = cookieStr.substr(sep+1,cookieStr.length - sep -1);
var obj = new Object();
obj[key] = value;
return obj;
}
}
module.exports.api = API_Handler;
Here is the necessary C# Client Application Code
public void init_Socket() {
wsSocket = new WebSocket(Socket_URL);
wsSocket.OnOpen += (sender, e) => {
Console.WriteLine("Connected to server at "+Socket_URL);
};
wsSocket.OnClose += (sender, e) => {
setToken(NO_TOKEN);
Console.WriteLine("Disconnected from server! - " + e.Code.ToString());
};
wsSocket.OnMessage += (sender, e) => {
if (wsSocket.ReadyState == WebSocketState.Open && e.IsText) {
Console.WriteLine("Server Message: " + e.Data);
ReadHandler handler = new ReadHandler(null);
handler.handle(e.Data);
}
};
}
public void setToken(string token) {
if(wsSocket != null) {
wsSocket.SetCookie(new Cookie(Props.Token, token));
}
}
public void SocketConnect() {
wsSocket.Connect();
}
private void SendSocketMessage(APIRequest req, APIResponseHandler handler) {
Console.WriteLine("SOCKET MESSAGE: " + req.ToString());
if (wsSocket.IsAlive && wsSocket.ReadyState == WebSocketState.Open) {
wsSocket.SendAsync(req.ToString(), new Action<bool>((bool completed) => {
Console.WriteLine("SENDING completed!");
})
);
} else{ Console.WriteLine("Socket must be alive in order to send a message.");}
}
So how it works is that when a client updates information on the server it sends the updated information to the server and the server then sends this updated information to all its clients except the sender.
When the Clients receive updated information from the server they update their local copies to match the data received from the server.
The problem is that after the client has been connected to the server via WebSocket for about a minute, the client throws a WebSocketException with the message "The header of frame cannot be read from the stream".
A quick google search leads me to this GitHub issue https://github.com/sta/websocket-sharp/issues/202.
From the above-mentioned link:
"
We managed to fully fix it by modifying
ReadBytesAsync(this Stream stream, int length, Action completed, Action error) in Ext.cs.
We noticed that 'NetworkStream.EndRead' can return zero (0) bytes even when connection is not being closed. Close reading of documentation of 'EndRead' only says that zero bytes is returned when connection is closed. Other way around is not always true, receiving 0 bytes does not always mean connection is being closed it seems. It is quite common to get zero bytes returned even when the connection is not being closed. Happens once in a while with Chrome, and seems to happen more often with Firefox.
Therefore replacing:
if (nread == 0 || nread == length)
with:
if(nread == length)
fixes the problem. It makes sure that when one is waiting for example for the two frame bytes one really gets two bytes back.
".
But on inspecting the code in the WebSocket-Sharp library the above-quoted fix has already been applied to the library(the post was from 2016 so it was probably pathed).
The code from the library in 2020:
public static class Ext{
//Other functions and properties of Ext goes here
internal static void ReadBytesAsync (this Stream stream,int length,Action<byte[]>
completed,Action<Exception> error){
var buff = new byte[length];
var offset = 0;
var retry = 0;
AsyncCallback callback = null;
callback =
ar => {
try {
var nread = stream.EndRead (ar);
if (nread <= 0) {
if (retry < _retry) {
retry++;
stream.BeginRead (buff, offset, length, callback, null);
return;
}
if (completed != null)
completed(buff.SubArray(0, offset));
return;
}
if (nread == length) {
if (completed != null)
completed (buff);
return;
}
retry = 0;
offset += nread;
length -= nread;
stream.BeginRead (buff, offset, length, callback, null);
}
catch (Exception ex) {
if (error != null) {
error(ex);
}
}
};
try {
stream.BeginRead (buff, offset, length, callback, null);
}
catch (Exception ex) {
if (error != null)
error (ex);
}
}
}
Another strange thing is that when I run the server locally and connect to it this problem never occurs. It only happens when I connect to the remote copy that is deployed on Heroku.
This case should be very simple, but I have a little bug that I don't know how to solve. I'm working with a Xamarin.Forms app that does some API calls to a localhost API running in my PC. This is the main part of the API call:
try
{
HttpClient client = new HttpClient();
client.MaxResponseContentBufferSize = BUFFER_SIZE;
client.DefaultRequestHeaders.Add(CULTURE_ID, "es");
client.DefaultRequestHeaders.Add(VALUE, LAST_WEEK_VALUE);
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(BEARER, token);
var content = new StringContent("", System.Text.Encoding.UTF8, APPLICATION_JSON);
HttpResponseMessage response = await client.GetAsync(url);
var result = await response.Content.ReadAsStringAsync();
List<SubProductParser> prices;
try
{
prices = JsonConvert.DeserializeObject<List<SubProductParser>>(result);
}
catch
{
// exception missin the final character in JSON "]"
}
return prices.ElementAt(0);
}
catch
{
return null;
}
The content of the result variable before trying to do the deserialize:
As you can see is not a valid JSON format, is strange because as you can see in the next screenshot when I do the same API call with POSTMAN is working always perfectly, but I don't know why sometimes doesn't receive all the JSON content when I do the call in the app.
Any idea, seeing the variable result content in debug, sometimes doesn't have the final character of the JSON "]", the API call is made after a button tapped in this way:
private async void Handle_Item_Tapped(object sender, ItemTappedEventArgs e)
{
var selected = e.Item as ProductParser;
var lastPrice= await ApiConnector.GetLastWeekSubProductPrices(
App.ViewModel.LoginViewModel.SesionToken,
subCatViewModel.Selected.ProductId,
subCatViewModel.Selected.Id);
await subCatViewModel.IsFavorite();
subCatViewModel.BarViewModel.Selected = selected;
subCatViewModel.BarViewModel.Categories = _categories;
subCatViewModel.LinearViewModel.Selected = selected;
subCatViewModel.LinearViewModel.Categories = _categories;
await Navigation.PushAsync(new Graphs(subCatViewModel));
}
The API functions that implement this API call, the API is developed in Larvael using php. I tried to simplify them as much as possible:
public static function getLastWeekPrices($cultureId)
{
self::initialize();
$year = self::getLastYear();
$prejson = json_encode(DB::table('product')
->join(SOME_CONDITIONS..)
...
->where(SOME_CONDITIONS..)
...
->select(SOME_ROWS)
->get());
$currentWeek = self::getCurrentWeek();
$json_decoded = json_decode($prejson, true);
$json_final = array();
foreach ($json_decoded as $slider_value)
{
$max_week= DB::table(SOME_CONDITIONS)
->max(SOME_CONDITIONS);
$last_week_price = json_encode(SOME_CONDITIONS)
...
->get());
...
//ADDING SOME VALUES TO $slider_value
...
array_push($json_final, $slider_value);
}
return json_encode($json_final);
}
public static function subProductsPricesQuery($id, $subId, $cultureId, $values)
{
self::initialize();
if (self::doesCultureExist($cultureId)){
if ($values == self::LAST_WEEK) {
$json = self::getLastWeekSubProductPrices($id, $subId, $cultureId);
}
....
}
}
public function FunctionCalledInAPIRoute(Request $request, $id, $subId)
{
try{
$user = JWTAuth::parseToken()->toUser();
$json = DatabaseQuerys::subProductsPricesQuery($id, $subId, $request->header("cultureId"), $request->header("value"));
return self::jsonIsEmpty($json);
}catch(JWTException $e){
return $this->respondInternalError("Any token found!");
}
}
public static function jsonIsEmpty($products)
{
self::initialize();
$initial = $products[0] === "[" ? TRUE : FALSE;
$final = $products[1] === "]" ? TRUE : FALSE ;
if (strlen($products) === 0 || ($initial == TRUE && $final == TRUE))
{
header('Content-type:application/json;charset=utf-8');
return (new self)->respondNotFound("Any result found in this category");
} else {
header('Content-type:application/json;charset=utf-8');
return $products;
}
}
The JsonIsEmpty is a little method that I create to control if the database return something, if not as the functions return json_encoder($array), always will be [].
The content of the variable result when receives the faulty JSON:
[
{
"id": 19,
"name": "Conejo de más de 2125 gr",
"units": "€/Kg",
"price": "1.91",
"week": 10,
"year": 2019,
"last_price": "1.79",
"difference": "0.12",
"arrow": "up_arrow.png"
}
As you can see is missing the closing "]".
I implemented a little test to see if the app is working fine, the test function and the function that calls it:
public static function jsonIsEmptyTest($products)
{
self::initialize();
$initial = $products[0] === "[" ? TRUE : FALSE;
$final = $products[1] === "]" ? TRUE : FALSE ;
if (strlen($products) === 0 || ($initial == FALSE && $final == FALSE))
{
return 1;
} else {
return 0;
}
}
public static function the foo(....)
{
$correct = 0;
$wrong = 0;
for($x = 0; $x < 1000; $x++)
{
$json = DatabaseQuerys::subProductsPricesQuery($id, $subId, $request->header("cultureId"), $request->header("value"));
print_r($json);
print_r("\n\n");
$result = JsonController::jsonIsEmptyTest($json);
if($result == 0)
{
$correct = $correct + 1;
}
if($result == 1)
{
$wrong = $wrong + 1;
}
}
print_r("Correct: ");
print_r($correct);
print_r("\n");
print_r("Wrong: ");
print_r($wrong);
exit();
}
Doing the API call with postman the result is:
Yesterday I've found out how to create several async http requests without async/await. But today I need to do it in a loop: if some of responses don't satisfy some condition - I need to change a request for them and send these requests again. It may be repeated several times.
I've tried this code:
do
{
var loadingCoordinatesTasks = new List<Task<Terminal>>();
var totalCountOfTerminals = terminalPresetNode.ChildNodes.Count;
var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
foreach (var terminal in terminals.Except(_terminalsWithCoordinates))
{
var address = terminal.GetNextAddress();
var webRequest = (HttpWebRequest)WebRequest.Create(GeoCoder.GeoCodeUrl + address);
var webRequestTask = Task.Factory.FromAsync<WebResponse>(webRequest.BeginGetResponse,
webRequest.EndGetResponse,
terminal);
var parsingTask = webRequestTask.ContinueWith(antecedent =>
{
// Parse the response
});
loadingCoordinatesTasks.Add(parsingTask);
}
Task.Factory.ContinueWhenAll(loadingCoordinatesTasks.ToArray(), antecedents =>
{
foreach (var antecedent in antecedents)
{
var terminalWithCoordinates = antecedent.Result;
if (antecedent.Status == TaskStatus.RanToCompletion &&
!terminalWithCoordinates.Coordinates.AreUnknown)
{
_terminalsWithCoordinates.Add(terminalWithCoordinates);
_countOfProcessedTerminals++;
}
}
});
} while (_countOfProcessedTerminals < totalCountOfTerminals);
but is it possible to check the condition in while just after every single set of requests executed?
You can perform the check after increasing the count:
_countOfProcessedTerminals++;
if (_countOfProcessedTerminals >= totalCountOfTerminals)
{
break;
}
Is _countOfProcessedTerminals thread-safe though?
I manage to do it using recursion:
public void RunAgainFailedTasks(IEnumerable<Task<Terminal>> tasks)
{
Task.Factory.ContinueWhenAll(tasks.ToArray(), antecedents =>
{
var failedTasks = new List<Task<Terminal>>();
foreach (var antecedent in antecedents)
{
var terminal = antecedent.Result;
// Previous request was failed
if (terminal.Coordinates.AreUnknown)
{
string address;
try
{
address = terminal.GetNextAddress();
}
catch (FormatException) // No versions more
{
continue;
}
var getCoordinatesTask = CreateGetCoordinatesTask(terminal, address);
failedTasks.Add(getCoordinatesTask);
}
else
{
_terminalsWithCoordinates.Add(terminal);
}
}
if (failedTasks.Any())
{
RunAgainFailedTasks(failedTasks);
}
else
{
// Display a map
}
}, CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
private Task<Terminal> CreateGetCoordinatesTask(Terminal terminal, string address)
{
var webRequest = (HttpWebRequest)WebRequest.Create(GeoCoder.GeoCodeUrl + address);
webRequest.KeepAlive = false;
webRequest.ProtocolVersion = HttpVersion.Version10;
var webRequestTask = Task.Factory.FromAsync<WebResponse>(webRequest.BeginGetResponse,
webRequest.EndGetResponse,
terminal);
var parsingTask = webRequestTask.ContinueWith(webReqTask =>
{
// Parse the response
});
return parsingTask;
}
I am new to Web api and json. I am unaware of calling method which are in WebApi. Below method is in Webapi:
[HttpGet]
public bool AddAccount([FromRoute]string accountname)
{
try
{
BizFramework.Web.Model.Account data = new BizFramework.Web.Model.Account();
data.AccountGuid = Guid.NewGuid();
data.AccountName = accountname;
data.ParentAccountID = 0;
data.AccountTypeID = 13;
data.AccountNo = "8060";
data.Active = true;
data.HierarchyLevel = 1;
data.CashFlowID = 3;
data.OpeningBalanceDate = DateTime.Now;
data.IscashBasis = true;
data.Createdby = "BAOwner";
data.CreatedDatetime = DateTime.Now;
data.Modifiedby = "BAOwner";
data.ModifiedDatetime = DateTime.Now;
BA.AddToAccounts(data);
BA.SaveChanges();
return true;
}
catch (Exception ex)
{
File.AppendAllText(AppDomain.CurrentDomain.BaseDirectory + "log.txt", ex.ToString());
return false;
}
}
Using this link :
http://example.com/CustomerPortalService12/AddAccount/AccountsReceivable
I am able to add. But through the coding how can I add?
public void AddAccount(string accountname, Action<bool> success, Action<bool> failure)
{ var client = new RestClient("http://[localhost]/CustomerPortalService12");
// client.Authenticator = new HttpBasicAuthenticator(username, password);
var request = new RestRequest("AddAccount/AccountsReceivable/" + accountname, Method.GET);
client.ExecuteAsync(request, (response) =>
{
if (response.ResponseStatus == ResponseStatus.Error)
{
failure(response.ErrorMessage);
}
else
{
var result= JsonConvert.DeserializeObject<bool>(response.Content);
success(result);
}
}); }
to call this function
AddAccount("accountname",
(item) => Dispatcher.BeginInvoke(() =>
{
MessageBox.Show("Done!");
}),
(error) => Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(error);
}));
I have done this by using RestSharp.
For more details go to here
http://restsharp.org/