I have this webapi method here:
// PUT api/Competitions/5
public HttpResponseMessage PutCompetitor(int id, CompetitionViewModel competitorviewmodel)
{
...
}
The CompetitionViewModel looks something like this:
public class CompetitionViewModel
{
public int CompetitorId { get; set; }
public string Owned{ get; set; }
public bool Sold { get; set; }
}
I have an angular http.put call to update a competition model that looks like this:
$scope.updateProject = function () {
$http.put(mvc.base + "API/Projects/" + masterScopeTracker.ProjectID, $scope.ProjectCRUD)
.success(function (result) {
})
.error(function (data, status, headers, config) {
masterScopeTracker.autoSaveFail;
});
}
On page load, a new competition is created. So I have a model like the following:
{
CompetitorId: 56,
Owned: null,
Sold: false
}
Every 15 seconds a call to update the model is made. If I don't change any of the values of the model, the webapi put method gets called and runs successfully without a problem. If I change the model to this:
{
CompetitorId: 56,
Owned: "Value",
Sold: false
}
I get a 500 Error and the method is not hit. Not understanding what I am doing wrong here. The view model accepts a string. A string is being sent in the payload. Yet I get the error. Anyone have any ideas?
UPDATE:
I was able to get the server to give me this error:
{"Message":"Anerrorhasoccurred.",
"ExceptionMessage":"Objectreferencenotsettoaninstanceofanobject.",
"ExceptionType":"System.NullReferenceException",
"StackTrace":"atClientVisit.Models.ClientVisitEntities.SaveChanges()\r\natClientVisit.Controllers.API.CompetitionsController.PutCompetitor(Int32id,CompetitionViewModelcompetitorviewmodel)\r\natlambda_method(Closure,Object,Object[])\r\natSystem.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Objectinstance,Object[]methodParameters)\r\natSystem.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Objectinstance,Object[]arguments)\r\natSystem.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1func,CancellationTokencancellationToken)"
}
I should also say that this doesn't happen locally. This only happens when deployed on the clients server.
You should check the event log to see what the actual error is on the server side. I've had trouble with IIS/IIS Express before with Put because WebDAV was enabled. You can disable it in your web.config:
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="WebDAV" />
</handlers>
</system.webServer>
Related
I'm new to React and I just can't find clear information on how to call a controller in my .NET Core application, send it values from the form inputs and then get a result back from it. Doesn't seem like it should be hard to do but maybe I know so little that I'm asking Google the wrong questions. Anyway...
I have a controller method that looks like this. I have called it from Postman and it seems to work.
[ApiController]
[Route("user")]
public class UserController : ControllerBase
{
[HttpGet]
[Route("login/authenticate")]
public async Task<IActionResult> AuthenticateUserAsync([FromBody] AuthenticateUserRequest request)
{
... // sends request to back-end and gets a response
return Ok(response);
}
}
There's a request class that looks like this, and I want the UserID and Password properties from the React form to be set here.
public class AuthenticateUserRequest: Request
{
public string UserID { get; set; }
public string Password { get; set; }
}
The response that is returned contains a list of strings which is used to authenticate the user with an identity in the controller. I want to return this list to React to show what permissions the user has.
public class AuthenticateUserResponse: Response
{
public List<String> Permissions { get; set; }
}
And I'm trying to call it with something like this:
I got this fetch example from here: ASP.NET Core React project template - how to link to C# backend
but it doesn't seem to work.
handleSubmit(event) {
if (this.state.userId == "" || this.state.password == "") {
alert('Please enter User ID and Password.');
event.preventDefault();
}
else {
fetch('user/login/authenticate', {
method: 'get',
headers: { 'Content-Type': 'application/json' },
body: {
"UserID": this.state.userId.value,
"Password": this.state.password.value
}
})
.then(response => response.json())
.then(data => {
alert(data);
});
}
}
The alert never gets called and I don't know if it's doing anything at all. When I press the Submit button the form clears, and that's it.
When you use get in ajax to request the action in the Core API, you need to accept the parameters by using [FromQuery] attribute instead of [FromBody] attribute.
[HttpGet]
[Route("login/authenticate")]
public async Task<IActionResult> AuthenticateUserAsync([FromQuery] AuthenticateUserRequest request)
{
... // sends request to back-end and gets a response
return Ok();
}
Refer to this.
I am trying to pass complex object from angular service to MVC controller. Below is the code-:
Angular Controller
$scope.saveData = function () {
var resultData = new Object();
resultData.Name = $scope.Name;
resultData.Address = new Object();
resultData.Address = $scope.Address;
resultData.Address.Contact = $scope.Address.Contact;
var promiseOrganization = AngularService.saveResult(resultData);
promiseOrganization.then(function (result)
{
alert("Saved successfully.");
}
)
}
Angular Service
this.saveResult = function (resultData) {
return $http.post("/Form/SaveData/" + resultData);
}
MVC Controller
[System.Web.Http.HttpPost]
public string SaveData([FromBody] resultData data)
{
//operation to perform
return "Data Reached";
}
When I try passing complex object from Angular service to mvc controller. It gives me null i.e. object becomes null.
Please suggest.
When using the $http.post method you need to pass the data object as the second paramenter. You can read up on it here
So your angular code should look like
$http.post("/Form/SaveData/", data);
You then need a server side representation of the data you are passing the WebApi controller
public class MyCustomObject
{
public string Name { get; set; }
public MyCustomAddress Address { get; set; }
}
public class MyCustomAddress
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string Contact { get; set; }
}
You need to update your WebApi controller code to use the new server side class as the parameter. Note that I am not using the [FromBody] attribute as this link explains you only need to use the [FromBody] attribute when you want to force Web API to read a simple type from the request body(your type is a complex type)
To force Web API to read a simple type from the request body, add the [FromBody] attribute to the parameter
Updated WebApi Controller code without the [FromBody] attribute:
[System.Web.Http.HttpPost]
public string SaveData(MyCustomObject data)
{
//operation to perform
return "Data Reached";
}
Change your post function to something like this,
$http.post('/Form/SaveData/', resultData)
.success(function (data, status, headers, config) {
})
.error(function (data, status, headers, config) {
});
You are wrong in your post command. It should be:
$http.post("/Form/SaveData/",{resultData: resultData});
The problem is you need to add the following header -> 'Content-Type': 'application/x-www-form-urlencoded'
$http.post('/Form/SaveData/', resultData, {headers: {'Content-Type': 'application/x-www-form-urlencoded'}});
selectedLocation is always null on the Server
I am using Angular , what is the proper way of passing object to get method
This is Class that is defined on Server, which is being passed as parameter in get request
public class Location
{
public string LocationName { get; set; }
public string MFGName { get; set; }
}
This is the method in WebAPI that is being called.
[HttpGet]
[ActionName("RenewToken")]
[AllowAnonymous]
[ResponseType(typeof(string))]
public async Task<IHttpActionResult> RenewToken(Location selectedlocation)
{
}
The Captured Request looks like this(In google Chrome Developers Tool)
http://localhost:58146/api/Account/RenewToken?selectedlocation=%7B%22LocationName%22:%22Guad%22,%22MFGName%22:%22Flex%22%7D
What am i doing wrong ?
Okay so from what i got from this
Why do we have to specify FromBody and FromUri in ASP.NET Web-API?
when the parameter of the method is a complex type is looks in the body of the request
since you're using GET the data gets put into the uri instead of the body
couple options you could do are
public async Task<IHttpActionResult> RenewToken(string LocationName, string MFGName)
{
}
you could always change your verb to a post or something that accepts data in teh body
You might try changing your get in angular to something like
$http({
method: "GET",
url: "/api/Account/RenewToken",
params: {
LocationName: "Guad",
MFGName: "Flex"
}
})
which will parameterize the data
I'm managing to post to the server OK, I'd like to get the updated data and load it back into the same JSON object, but the data response is null.
$scope.saveDetails = function() {
$http({
method : 'POST',
url : '/service/rest/orders',
data : $scope.orderDetails
})
.success(function(data, status) {
$scope.orderDetails = data;
})
.error(function() {
alert('error');
});
}
Also worth mentioning, that the initial object is being passed from another controller via $rootscope and injected into the local scope.
$scope.orderDetails = $rootScope.newOrder;
Thanks for any help.
Your code looks fine, I would be checking the backend to make sure data is actually being sent. Another option would be to use the chrome inspector and check the response to make sure you are actually getting something back.
It turns out it was returning the whole object and the order was deeper down, I didn't see that in my console at first.
$scope.orderDetails = data.order;
Thanks for all replies.
In case anyone else runs into this, in my case I had a class with a data contract attribute applied:
[DataContract(Namespace = "http://somespace.com")]
And my class members had not been given the [DataMember] attribute. Web API was not returning back the data. I added the [DataMember] attribute and it fixed it.
[DataMember] public int NumberUpdated { get; set; }
[DataMember] public int NumberInserted { get; set; }
[DataMember] public List<ServicesListImport> InvalidRows {get; set;}
Within my project I am trying to perform a save operation that updates my Breeze model as well as passes the updated object to my webAPI. In this project I am not using EF context as the project has been modeled to work with other interfaces. So within my webAPI class I have the following:
[BreezeController]
public class ReportLibraryApiController : ApiController
{
readonly long userid = 1;//testing
readonly IReportLibraryManager manager = new ReportLibraryManager();//interface
//method for share
[Route("reportlibrary/SetReportShare/")]
[HttpPost]
public DTOs.Report SetReportShare(JObject report )
{
//within here I plan to unwrap the JSobject and pull out the necessary
//fields that I need
DTOs.Report updatedreport=null;
//manager.ShareReport(updatedreport.ReportId);
//manager.UnShareReport(updatedreport.ReportId);
return updatedreport;
}
}
The Report Object looks like this
public class Report
{
public Int64 ReportId { get; set; }
public string ReportName { get; set; }
public string ReportDescription { get; set; }
public DateTime? ReportDateCreated { get; set; }
public string ReportOwner { get; set; }
public IEnumerable<ReportLabel> ReportLabels { get; set; }
public bool IsShared { get; set; }
public bool IsFavorite { get; set; }
}
From my angular service I am trying to call the save operation as:
app.factory('reportLibraryService', function(breeze, model){
var serviceName = "reportlibrary/";
var ds = new breeze.DataService({
serviceName: serviceName,
hasServerMetadata: false,
useJsonp: true,
});
var manager = new breeze.EntityManager({ dataService: ds });
model.initialize(manager.metadataStore);
function returnResults(data){ return data.results}
function setReportShare(report) {
var option = new breeze.SaveOptions({ resourceName: 'SetReportShare' })
return manager.saveChanges(null, option).then(returnResults)
};
}
I realize the return results may not be correct but at this point I am just trying to call the save operation in the API. When I run the code everything executes but the save/setting of the share does not fire. A secondary question is I'm still not clear on how the breeze model is updated? Am I supposed to issue a new query from the api and pass that back or is there a way to update the cached object. I'm new to breeze (obviously) and trying to figure out where to look.All the examples I have seen thus far use EF context to perform these actions. However in my case I don't have that option.
Should breeze be making the call or since I am not using EF should I be using $http directive to push the object up. then return a new object to breeze for binding? (that seems a little heavy to me and now how it is designed to work).
I'd appreciate any guidance, or information or how to figure this out.
Edited for more information...
Here is a little more detail based on some of the questions posted by Ward to my original question:
where is your metadata that you code says is not on the server?
The solution I am working on does not expose the EF context this far our. As a result interfaces have been created that handle the queries etc within the project. As a result I don't have the ability to use EF. Very similar to the Edmunds sample. I created a meta definition in the web project which references a report object I have defined. This class is much different from what is returned from the DB but it represents what the UI needs. I created two models
report.cs
public class Report
{
public Int64 ReportId { get; set; }
public string ReportName { get; set; }
public string ReportDescription { get; set; }
public DateTime? ReportDateCreated { get; set; }
public string ReportOwner { get; set; }
public IEnumerable<ReportLabel> ReportLabels { get; set; }
public bool IsShared { get; set; }
public bool IsFavorite { get; set; }
}
model.js
app.factory('model', function () {
var DT = breeze.DataType;
return {
initialize: initialize
}
function initialize(metadataStore) {
metadataStore.addEntityType({
shortName: "Report",
namespace: "Inform.UI.DTOs",
dataProperties: {
reportId: { dataType: DT.Int64, isPartOfKey: true },
reportName: { dataType: DT.String },
reportDescription: { dataType: DT.String },
reportDateCreated: { dataType: DT.String },
reportOwner: { dataType: DT.String },
reportLabels: { dataType: DT.Undefined },
isShared: { dataType: DT.Bool },
isFavorite: { dataType: DT.Bool }
},
navigationProperties: {
labels: {
entityTypeName: "Label:#Inform.UI.DTOs", isScalar: false,
associationName: "Report_Labels"
}
}
});
metadataStore.addEntityType({
shortName: "ReportLabel",
namespace: "Inform.UI.DTOs",
dataProperties: {
labelId: { dataType: DT.Int64, isPartOfKey: true },
reportId: { dataType: DT.Int64 },
labelName: { dataType: DT.String },
isPublic: { dataType: DT.Bool },
reports: { dataType: DT.Undefined }
},
navigationProperties: {
labels: {
entityTypeName: "Report:#Inform.UI.DTOs", isScalar: false,
associationName: "Report_Labels", foreignKeyNames: ["reportId"]
}
}
});
}
})
why are you configuring useJsonp = true ... and then POSTing to the SetReportShare endpoint?
The dataservice was originally defined for GET requests for querying/returning results to the client. I reused the dataservice and tacked on the POST event. Based on your comment though I assume this is a no-no. In looking at the project I am working within the same domain (and always will be) so I don't really think I need jsonp as part of the dataservice definition. Am I wrong in thinking that?
I gather from your question that I should have a separate dataservice for POST's and a separate one for GET's
why is it returning the DTOs.Report type when you can tell by looking at the standard Breeze SaveChanges method that it returns a SaveResult?
This was a typo on my part. The save Result was originally defined as just a JObject. My intent is to return (if necessary) an updated Report object. However I am unsure of the best practice here. If the client (breeze) is updating the cache object. Why do I need to return the report object. Would it not be better to just return a success fail result or boolean of some sort vs. returning an entire report object?
why is your client-side saveChanges callback treating the response as if it came from a query?
This is very simple as you stated, I have no idea what I am doing. I am certainly diving into the deep end as I don't have a choice right now... My question here is when you perform a CRUD operation are these not wrapped in a promise as when performing a query? Or is the promise only important for queries?
Thank you again-
-cheers
You're jumping into the deep end w/o knowing how to swim.
You haven't explained why you're going exotic. That's OK but you want to ease into it. I strongly recommend that you start with the "happy" path - Web API, EF, SQL Server - and then unwind them slowly as you start to understand what's going on.
If you can't do that, at least look at the NoDb sample which doesn't use EF or SQL Server (see the TodoRepository).
You absolutely can do what you're striving to do ... once you know how ... or find someone who does.
At this point, you've created nothing but questions. For example,
where is your metadata that you code says is not on the server?
why are you configuring useJsonp = true ... and then POSTing to the SetReportShare endpoint?
why is it returning the DTOs.Report type when you can tell by looking at the standard Breeze SaveChanges method that it returns a SaveResult?
why is your client-side saveChanges callback treating the response as if it came from a query?
As for your questions:
the breeze model is updated automatically when the save completes
no, you don't issue a new query from the api
yes, you can (and usually do) return the server-updated object back in the SaveResult.
$http (a method not a directive) is used by Breeze itself to communicate to the server; using it directly isn't going to change anything.
Not sure any of my answers help. But I do think you'll be fine if you take it from the top and work deliberately forward from one thing you understand to the next.