Calling a function from a C# API controller with Axios - c#

I have function in a C# controller file that makes an API call to fetch some placeholder data that is hard coded locally.
[Route("api/ReportingCenter/GetReportList")]
[HttpGet]
public class ReportController : ApiController
{
public async Task<IHttpActionResult> GetReportList(CancellationToken cancellationToken)
{
using (var source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken,
HttpContext.Current.Response.ClientDisconnectedToken))
{
var userId = 11;
var projId = 22;
using (var reportDb = new ReportDb(userId, projId))
{
var list = reportDb.GetReports(userId, projId);
return Json(list);
}
}
}
}
public class ReportDb : PortalDb
{
public List<ReportInfo> GetReports(int userid, int projectid)
{
var ReportList = new List<ReportInfo>();
ReportList.Add(new ReportInfo(1,"file1.pdf", 11, "a file with data in it", 11112222,"pdf", 1, "22"));
}
}
I coded a button and a function with an aspx file that should make an axios call to run the GetReportList function from the controller. When the button is click, nothing happens. Any suggestions on how to make it so that the API call is made when the button is clicked in the aspx file?
<asp:Content ID="Content5" ContentPlaceHolderID="primaryContent" runat="server">
<div id="reporting-app-container">
<input type="button" value="ReportInfo" class="btn btn-sm btn primary"
onclick="getReportInfo"/>
</div>
<script src="/ReportsModule/app/dist/reporting-app-bundle.js"></script>
<script type="text/javascript">
$(document).ready(function () {
window.ReportingApp();
});
async function getReportInfo(id) {
console.log("pre-axios call")
const response = await axios.get("api/ReportingCenter/GetReportList")
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
});
}
</script>
</asp:Content>

.then and .catch are methods of a Promise, which is what you'd get if you called axios.get without awaiting it. A Promise represents an action which you've started in the background, which may or may not have finished yet, and which can call callback functions supplied by you if/when it completes successfully or throws an error. Or at least that's a very short description of what it is.
To pass your callback functions to the Promise returned by axios.get, remove the async and await, a bit like this:
function getReportInfo(id) {
axios.get('api/ReportingCenter/GetReportList')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
}
If you want response to be the actual response from the API, rather than a Promise to give you the actual response once the web request has completed, then you can await the Promise, a bit like this:
async function getReportInfo(id) {
try {
const response = await axios.get('api/ReportingCenter/GetReportList');
console.log(response);
} catch (error) {
console.error(error);
}
}
A note of caution if you choose the second option - are you sure all your users' browsers support ECMAScript 2017? If not then the await / async approach probably isn't a good idea and you should stick with the first option.
Code snippets are adapted from this bit of the axios API documentation

Moved the javascript function that handles the axios call into reporting-app.js. Vue is being used on top of javascript. The .aspx file and the Vue app are connected by the "el" key, with the value set to the html id: '#reporting-app-container".
.aspx file
<div>HELLO WORLD!</div>
<div id="reporting-app-container">
<input type="button" value="ReportInfo" class="btn btn-sm btn primary" #click="getReportInfo" />
</div>
<script src="/ReportsModule/app/dist/reporting-app-bundle.js"></script>
.js file
import Vue from 'vue';
window.ReportingApp = function () {
new Vue({
el: '#reporting-app-container',
//store: FilesStore,
data: {},
components: {
'reporting-component': reporting_component,
'list-reports-component': list_report_component
},
methods: {
getReportInfo: function (id) {
console.log("pre-axios call");
axios.get("/api/ReportingCenter/GetReportList")
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
});
}
}
});
}
The javascript and the C# Controller file are linked by the axios method within the methods section of the Vue app.
Controller.cs
using ###.###.###.api.Models;
namespace ###.###.ReportsModule.api
{
public class ReportController : ApiController
{
protected readonly IPortalStateService PortalState;
public ReportController(IPortalStateService portalStateService)
{
PortalState = portalStateService;
}
[Route("api/ReportingCenter/GetReportList")]
[HttpGet]
public IHttpActionResult GetReportList(CancellationToken cancellationToken)
{
using (var source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken,
HttpContext.Current.Response.ClientDisconnectedToken))
{
var user = PortalState.GetUserId();
if (user == null)
{
return BadRequest("User is not allowed");
}
var userId = 11;
var projId = 22;
try
{
using (var reportDb = new ReportDb(userId, projId))
{
var list = reportDb.GetReports(userId, projId);
return Json(list);
}
}
catch (Exception e)
{
return Json(e);
}
}
}
}
public class ReportDb : PortalDb
{
public List<ReportInfo> GetReports(int userid, int projectid)
{
var ReportList = new List<ReportInfo>();
ReportList.Add(new ReportInfo(1, "file1.pdf", 11, "a file with data in it",
11112222, "pdf", 1, "22"));
return ReportList;
}
}
}
GetReports function uses the model from ReportInfo.cs that defines the field values and the constructor for the data.
ReportInfo.cs Model
namespace ###.###.reportsmodule.api.Models
{
public class ReportInfo
{
public long Id { get; set; }
public string Name { get; set; }
public int Ordinal { get; set; }
public string Description { get; set; }
public int ProjectId { get; set; }
public string SourceType { get; set; }
public int MinPermissionLevel { get; set; }
public string SourceId { get; set; }
public ReportInfo(long id, string name, int ordinal, string description,
int projectId, string sourceType, int minPermissionLevel, string sourceId)
{
Id = id;
Name = name;
Ordinal = ordinal;
Description = description;
ProjectId = projectId;
SourceType = sourceType;
MinPermissionLevel = minPermissionLevel;
SourceId = sourceId;
}
}
}

Related

Unable to connect to the C# Controller from frontend request

I am implementing Search by siteId feature using typescript and C#. I am not able to connect to C# controller from frontend request.
Here i am suppose to pass siteId as parameter. Please find my code below.
HTML:
<div class="form-group">
<label>Site Id search:</label>
<input type="text" title="You can search for a partial SiteId or a list of SiteId separated by semicolons" ng-model="vm.siteIdFilter" /> <button ng-click="vm.loadAvailableDevices()"><i class="fa fa-search" aria-hidden="true"></i> Search</button><br />
</div>
controller.ts
loadAvailableDevices(currentPage: number = 1) {
if (currentPage === 1) {
this.isLoading = true;
this.availableDevices = new Array<any>();
}
let queryFilter: any = { serialNumber: this.serialNumberFilter, deviceType: this.selectedDeviceType };
this.deviceService.loadUpdatableDevices(this.currentTenant.Id, queryFilter, this.siteIdFilter)
.then((response: any) => {
this.availableDevices = this.availableDevices.concat(response);
this.deviceCnt = this.availableDevices.length;
this.isLoading = false;
});
}
service.ts
loadUpdatableDevices(tenantId: number, filter: any, siteId: string): ng.IPromise<any> {
const uri = this.routes.getUpdatableDevices.replace('{:tenantId}', tenantId.toString());
return this.restService
.get(uri, { siteId, filter }, true)
.then((response: any) => {
if (response.data) {
return response.data;
}
})
.catch((response: any) => {
return this.$q.reject(response);
});
}
RoutePath
getUpdatableDevices: '/device-management/tenants/{:tenantId}/updatableDevices'
C# Controller.cs
[Route("tenants/{tenantId}/updatableDevices")]
[AcceptVerbs("GET")]
public IEnumerable<EtpDevice> GetUpdatableDevices(int tenantId, [FromUri] string filter, string siteId)
{
var connectedUser = GetUser();
if (siteId != null)
{
var siteList = this.DataAccess.GetSitesListById(connectedUser, siteId);
}
}
I am not able to connect to C# controller when I pass siteid from the frontend. Below is the error i am getting in inspect element.
"{"StatusCode":500,"Message":"Can't bind multiple parameters ('siteId') to the request's content."}"
May I know what's wrong with the code? Please help me to fix this. Thanks in advance.
Try to modify your service.ts as follows, pass the siteId parameter as a query string instead of the request body.
loadUpdatableDevices(tenantId: number, filter: any, siteId: string): Promise<any> {
const uri = this.routes.getUpdatableDevices.replace('{:tenantId}', tenantId.toString());
return this.restService
.get(uri + `?filter=${encodeURIComponent(JSON.stringify(filter))}&siteId=${siteId}`)
.then((response: any) => {
if (response.data) {
return response.data;
}
})
.catch((response: any) => {
return Promise.reject(response);
});
}
Assume that your AngularJS side successfully passes the nested object as params to the API.
The Uri should be as:
/device-management/tenants/{:tenantId}/updatableDevices/?filter.serialNumber=<SerialNumberValue>&filter.deviceType=<DeviceTypeValue>&siteId=<SiteIdValue>
Create the DeviceFilterModel class to match the object type that receives from Uri.
public class DeviceFilterModel
{
public Filter Filter { get; set; }
public string SiteId { get; set; }
}
public class Filter
{
public string SerialNumber { get; set; }
public string DeviceType { get; set; }
}
Modify the GetUpdatableDevices API action method signature as below:
[Route("tenants/{tenantId}/updatableDevices")]
[AcceptVerbs("GET")]
public IEnumerable<EtpDevice> GetUpdatableDevices(int tenantId,
[FromUri] DeviceFilterModel model)
Reference: Using [FromUri]

How to refresh partial view in a .net-core mvc project

I have read multiple post on partial view but still can't figure out how it works.
I have a main page, within if I have a partial view, basically rendering an image.
I am "listening" a folder using FileSystemWatcher, when a new image arrives in it, depending on it's type it's doing some process and at the end of the process it should "refresh" the partial view.
in the main cshtml "fullPage.cshtml" I did this:
<div id="_PartialView">
<h2><b>Images:</b></h2>
#Html.Partial("PatialViewImage");
</div>
in the PartialViewImage.cshtml:
<div style="text-align:center">
<img id="img_logo" alt="AIExampleResult" src=#ViewBag.CurrentImage style="margin-left:auto;margin-right:auto;width:80%" />
<h3>ImageProperties: #ViewBag.Resolution etc ...</h3>
</div>
in the controller:
public async Task<IActionResult> fullPage(int? id)
{
if (id == null)
{
return NotFound();
}
var patientdata = await _context.Patientdata
.FirstOrDefaultAsync(m => m.IdPatient == id);
if (patientdata == null)
{
return NotFound();
}
//start to listen dragonfly folder
_watcher = new FolderWatcher();
_watcher.PathToWatch = _configuration["ConfigFolderListner:ImagesFolder"];
_watcher.patientdata = patientdata;
_watcher.Run();
_watcher.PropertyChanged += HandleNewJImage;
return View(patientdata);
}
so when a new image is detected it goes into (in the controller class) :
public void HandleNewJpeg(object sender, EventArgs e)
{
ViewBag.CurrentImage = _watcher.LastImageProcessed;
PartialView("PatialViewImage");
return;
}
but it doesn't do anything.
when I debug:
it stops at the line PartialView("PartialViewImage"), but it doesn't seem to go into PartialViewImage.cshtml and the fullPage is not uploaded.
I tried to add in the fullPage.cshtml this javascript code:
<script language="JavaScript" type="text/javascript">
$("#ViewBag.CurrentImage").change(function refresh)
function refresh() {
$("#_PartialView").load("/Views/PatialViewImage");
};
</script>
without success.
any idea how I could connect the FileSystemWatcher to my fullPage ?
UPDATE
in the full page I have add this:
$(document).ready(function () {
CheckNewImages();
var refreshId = window.setInterval(function () {
CheckNewImages();
}, 2000);
});
function CheckNewImages() {
$.ajax({
url: "#Url.Action("ReportPartialView")",
type: "POST",
dataType: "html",
data: {id : #Model.IdPatient},
success: function (data) {
$('#_ReportPartialView').html(data);
clearInterval(refreshId);
},
fail: function () {
//do nothing
}
});
to check time to time if I have a new image.
from the filesystemwatcher event,
I try to update a database define like this:
in models:
public class PathImages
{
public int Id { get; set; }
public string RawImage { get; set; }
public string IAImage { get; set; }
}
public class TempContext : DbContext
{
public TempContext(DbContextOptions options) : base(options)
{ }
public DbSet<PathImages> PathImages { get; set; }
}
in startup.cs
services.AddDbContext<SQLDATABASEContext>(options => options.UseMySQL(Configuration.GetConnectionString("SQLDATABASE")));
services.AddDbContext<TempContext>(options => options.UseInMemoryDatabase("name"));
and in the controller:
public void HandleNewJpeg(object sender, EventArgs e)
{
_context2.Add<PathImages>(new PathImages { RawImage = _context._watcher.LatestJPEGThumb, IAImage = "" });
return;
}
but I get this error:
System.ObjectDisposedExceptionHResult Cannot access a disposed object
I still don't get how I can pass data from public void HandleNewJpeg(object sender, EventArgs e) to a controller action.
You can use <partial> tag helper,<div id="imageChanged"> <partial name="Partial/PatialViewImage" model="new ViewModel() { ImSrc = Model.ImSrc}" /> </div>. If you want refresh the page you need to have a controller action that will returns partial view.
User JQuery with event or without event for example
(function () {
$.ajax({
url: ROOT + 'Home/GetImage',
data: { imgId : #Model.ImgId },
type: 'POST',
success: function (data) {
$('#imageChanged').html(data);
},
error: function (res) {
}})
})();

How to use HttpPostedFileBase in WebApi Model (Post Action)

Server side code:
public class SomeModel
{
public Int64 Id { get; set; }
[Required]
public Int64 From_UserId { get; set; }
public string Text { get; set; }
public List<HttpPostedFileBase> Files {get; set;} //<-- Wonder if this is right way ?
}
Action Method in Controller
[HttpPost]
[Route("Upload")]
public IHttpActionResult Upload( SomeModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
//More code
return Ok();
}
Would angular client code like this work?
$http.post("api/upload",{
Id: 1,
From_UserId: 1,
Text: "First File",
Files: [file1, file2, file3] //<-These are the ones obtained through file type input
})
Additional Info: Using Azure Storage to store files uploaded.
Here is the great directive for it. ng-file-upload
Here is the Demo using Asp.net WebApi.
JS
//inject directives and services.
var app = angular.module('fileUpload', ['ngFileUpload']);
app.controller('MyCtrl', ['$scope', 'Upload', function ($scope, Upload) {
// upload later on form submit or something similar
$scope.submit = function() {
if ($scope.form.file.$valid && $scope.file) {
$scope.upload($scope.file);
}
};
// upload on file select or drop
$scope.upload = function (file) {
Upload.upload({
url: 'upload/url',
data: {file: file, 'username': $scope.username}
}).then(function (resp) {
console.log('Success ' + resp.config.data.file.name + 'uploaded. Response: ' + resp.data);
}, function (resp) {
console.log('Error status: ' + resp.status);
}, function (evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ' + evt.config.data.file.name);
});
};
// for multiple files:
$scope.uploadFiles = function (files) {
if (files && files.length) {
for (var i = 0; i < files.length; i++) {
Upload.upload({..., data: {file: files[i]}, ...})...;
}
// or send them all together for HTML5 browsers:
Upload.upload({..., data: {file: files}, ...})...;
}
}
}]);

How to to invoke an #DELETE web service in REST using ajax call?

I am working on web api project wherein I have to write method to delete an account.
I have created a controller called RemoveAccount where I have written my Delete account method.
Following is the code.
public RemoveAccountRes Delete(RemoveAccountModel objRemove, string function = "")
{
RemoveAccountRes objModel = new RemoveAccountRes();
if (ModelState.IsValid)
{
User user = null;
try
{
user = UserHelper.GetLoggedInUser(Convert.ToString(user.UserGuid));
if (user == null || objRemove.User == null)
{
objModel.ErrorMessage = "User Not Valid";
objModel.ErrorCode = 403;
objModel.Success = false;
return objModel;
}
if (function == "delete")
{
UserRepository.Delete(objRemove.User.UserId);
UserHelper.LogOutUser();
objModel.ErrorMessage = "Account is Deleted";
objModel.ErrorCode = 200;
objModel.UserGuid = Convert.ToString(user.UserGuid);
objModel.Success = true;
}
}
catch (Exception ex)
{
objModel.ErrorMessage = "Unidentified Error";
objModel.ErrorCode = 500;
objModel.UserGuid = Convert.ToString(user.UserGuid);
}
return objModel;
}
else
{
objModel.ErrorMessage = "Invalid/Incomplete requests";
objModel.ErrorCode = 400;
return objModel;
}
}
And the model for the above method is:
public class RemoveAccountModel
{
public User User { get; set; }
}
public class RemoveAccountRes
{
public int ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public int UserId { get; set; }
public string UserGuid { get; set; }
public bool Success { get; set; }
}
I have created a separate Test project to test this api method.
Following is the ajax call which gets invoked on click of Delete button:
$(document).ready(function () {
$("#btnDelete").click(function () {
alert("Hello");
var request = $.ajax({
url: "http://localhost:13979/api/RemoveAccount?function='delete'",
type: "DELETE",
dataType: "json"
});
request.done(function (msg) {
alert("Request success: " + msg);
$("#divResponse").empty().append("<span>" + JSON.stringify(msg) + "</span>");
//console.log(JSON.stringify(msg));
});
request.fail(function (jqXHR, textStatus) {
alert("Request failed: " + textStatus);
$("#divResponse").empty().append("<span>" + JSON.stringify(textStatus) + "</span>");
//console.log(JSON.stringify(textStatus));
});
});
});
When I tried to debug, I found that its not calling that URL and am getting "Internal Server Error" only for this method, rest all are working fine.
Where am I going wrong?
you already have the parameter function=delete does it really matter if the type of HTTP request is DELETE, I would recommend changing it to POST
from jQuery docs:
The type of request to make ("POST" or "GET"), default is "GET". Note: Other HTTP request methods, such as PUT and DELETE, can also be used here, but they are not supported by all browsers.
another thing ... Do you have this mapped properly?
http://localhost:13979/api/RemoveAccount is calling a function called Delete - which is fine if you handle that in your routing code.

Cannot implicitly convert type - missing cast - models.views

I am having trouble building this code - I have an api controller which gets its data from the service layer via the models and this is what i have:
api controller
public class RoleApiController : ApiController
{
private RoleService _roleService = new RoleService();
public RoleUser GetRoleUser(int sectionID)
{
if (sectionID != null)
{
return _roleService.GetUsers(sectionID);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
Model
public partial class RoleView
{
public RoleView()
{
this.Users = new HashSet<RoleUser>();
}
public ICollection<RoleUser> Users { get; set; }
}
public class RoleUser
{
public string Name { get; set; }
public string Email { get; set; }
}
ERROR MESSAGE:
Cannot implicitly convert type System.Collections.Generic.IEnumberable<...RoleUser> to RoleUser. An explicit conversion exists(missing a cast?)
for this line: return _roleService.GetUsers(sectionID);
JavaScript
<div>
Name: <span id="name"></span>
</div>
<div>
Email: <span id="email"></span>
</div>
<script type ="text/javascript" src="~/Scripts/jquery-1.9.1.min.js"></script>
<script type ="text/javascript">
getRoleUser(9, function (roleUser) {
$("#name").html(roleUser.Name);
$("#email").html(roleUser.Email);
});
function getRoleUser(id, callback) {
$.ajax({
url: "/api/RoleUser",
data: { id: id },
type: "GET",
contentType: "application/json;charset=utf-8",
statusCod: {
200: function (roleUser) { callback(roleUser); },
404: function () { alter("Not Found!"); }
}
success: function(result){
result.each(function())
}
});
}
</script>
Umm your returning a collection of users when you should be returning just one? I can't really tell whether you actually want a collection or not from the code you've shown, but that is your issue.
This will let your code compile, but may not be the solution you want:
return _roleService.GetUsers(sectionID).FirstOrDefault();
Your method declaration says it returns RoleUser (singular) while your method would hint it returns a collection. So either you need to fix the method to return a list or return only a single result from your GetUsers method.

Categories

Resources