MVC 6 API Multiple Parameters - angularjs

I have the following API, which takes care of updating items in the database:
[Route("update")]
[HttpPost("")]
public JsonResult UpdateRecords([FromBody]ICollection<ShoppingItemViewModel> vm)
{
if (ModelState.IsValid)
{
try
{
var items = Mapper.Map<IEnumerable<ShoppingItem>>(vm);
//update database
_repository.UpdateValues(items, User.Identity.Name);
return Json(null);
}
catch (Exception Ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(null);
}
}
else
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(null);
}
}
Then under my Angular code I am executing this POST method like following:
$scope.SaveChanges = function () {
$http.post("/api/items/update", $scope.items)
.then(function (response) {
}, function (err) {
$scope.errorMessage = "Error occured: " + err;
}).finally(function () {
});
};
What I would like to do, is to introduce new parameters to my initial UpdateRecords function, where some of them are optional. Then depending on the inserted parameters my procedure would do different things.
What I have tried to do is to change my function like following (example):
public JsonResult UpdateRecords([FromBody]ICollection<ShoppingItemViewModel> vm, [FromBody]bool EraseOldItems)
and under my Angular App:
$http.post("/api/items/update", {vm:$scope.items, EraseOldItems: true})
or even
$http.post("/api/items/update", {'vm':$scope.items, 'EraseOldItems': true})
but I could not get the code to work (my parameters were all the time null).
What am I doing wrong here?

From Parameter Binding in ASP.NET Web API:
At most one parameter is allowed to read from the message body.
// Caution: Will not work!
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }
The reason for this rule is that the request body might be stored in a
non-buffered stream that can only be read once.
You can pass a request object that contains other objects:
public class Request
{
public ICollection<ShoppingItemViewModel> vm { get; set; }
public bool eraseOldItems { get; set; }
}
And then your action:
[Route("update")]
[HttpPost("")]
public JsonResult UpdateRecords([FromBody]Request request){ ... }

Related

How to call Grpahql with .Net core from a React component using Axios?

I am new to graphql and trying to implement Graphql with dot net core using graphql-dotnet library.
We do not have a dedicated database in this application. The high level flow of the application is
Front End(React)
(Calls) > GraphQlController (.Net core)
(Calls) > Sales force api
Send data back to front end.
Graphql Setup.
public class GraphQLController : ControllerBase
{
private readonly IOptions<ApplicationConfiguration> _configuration;
public GraphQLController(IOptions<ApplicationConfiguration> config)
{
this._configuration = config;
}
public async Task<IActionResult> Post([FromBody] GraphQLQuery query)
{
var inputs = query.Variables.ToInputs();
var schema = new Schema()
{
Query = new OrderQuery(_configuration)
};
var result = await new DocumentExecuter().ExecuteAsync(_ =>
{
_.Schema = schema;
_.Query = query.Query;
_.OperationName = query.OperationName;
_.Inputs = inputs;
}).ConfigureAwait(false);
if (result.Errors?.Count > 0)
{
return BadRequest();
}
return Ok(result);
}
}
Query class
public class GraphQLQuery
{
public string OperationName { get; set; }
public string NamedQuery { get; set; }
public string Query { get; set; }
public JObject Variables { get; set; }
}
Model Class which used for the de-serialization
public class OrderModel
{
public string Id { get; set; }
public string Name { get; set; }
}
Equivalent type in Graphql
public class OrderType : ObjectGraphType<OrderModel>
{
public OrderType()
{
Name = "Order";
Field(x => x.Id).Description("The ID of the order.");
Field(x => x.Name).Description("The name of the order");
}
}
The Query class to call the sales force service
public class OrderQuery : ObjectGraphType
{
public OrderQuery(IOptions<ApplicationConfiguration> config)
{
Field<OrderType>(
"Order",
arguments: new QueryArguments(
new QueryArgument<IdGraphType> { Name = "id" }),
resolve: context =>
{
var id = context.GetArgument<object>("id");
var service = new SalesForceService(config);
var data = service.GetAccountByAccountID(id.ToString());
return data;
});
}
}
The application compiles fine in visual studio. when i press f5 and run this in the browser. I get this response
http://localhost:61625/api/graphql
{"":["The input was not valid."]}
When i try to run in postman by passing the following parameters in the body
{
OperationName:"test",
NamedQuery: "Orders",
Query:{},
Variables:{id:"123"}
}
i get this response ""A non-empty request body is required."
Can some one explain to me how do you make a request to graphql end point and what values should be passed in the below parms in postman.
{
OperationName:
NamedQuery:
Query:,
Variables:
}
How do you make a similar call from react , We are using axios:.
like below example how are parameters set for the call.
doRestCall = (id) => {
const model = {
variable: id
};
const headers = {
'Content-Type': 'application/json'
}
Axios.post('http://localhost:49776/api/graphql', model, headers)
.then(result => {
debugger;
console.log(result);
});
console.log(this.state);
};
Many thanks for the help.
It appears you're trying to use "named queries" with the use of NamedQuery, which is a design pattern with GraphQL. That design pattern is implemented by having well known queries that are pre-defined and cached on the server. Looking at your Controller you do not have named queries implemented. You will need to do a regular GraphQL query.
This is what the JavaScript would look like:
{
query: "query MyOrderQuery($id: ID) { order(id: $id) { id name } }",
variables: {
id: "123"
}
}
This would be the JSON:
{
"query": "query MyOrderQuery($id: ID) { order(id: $id) { id name } }",
"variables": {
"id": "123"
}
}
See https://graphql-dotnet.github.io/docs/getting-started/variables
I also suggest to use Apollo with your React components.
https://www.apollographql.com/docs/react/essentials/get-started.html
https://www.apollographql.com/docs/react/essentials/get-started.html#request

Model parameter passed (HTTP POST) to Web API 2 from AngularJS is null

I am making a POST request from Angular to Web API 2:
let params = {
ReportName: reportName,
ParamName: paramName,
ParamValues: paramValues
};
this.$http.post("/reportsweb/api/reports/", params).then(response => {
deferred.resolve(response.data);
}, response => {
deferred.reject(response);
})
Web API method is defined like this:
[HttpPost]
[ResponseType(typeof(Dictionary<string, string>))]
public IHttpActionResult GetDependentParameterValues([FromBody]ArgumentModel args)
{
// args here is null for some reason
}
where ArgumentModel is defined the same way as params var in Angular:
public class ArgumentModel
{
public string ReportName { get; set; }
public string ParamName { get; set; }
public string ParamValues { get; set; }
}
However, when I hit a breakpoint in WebAPI method, I see that args = null :(
Any idea why?
Thanks.
Here is the screenshot from angular app:

HttpPostedFileBase is null - Posting files from AngularJS to MVC

Similar questions have been asked so many times, but there are no clear answers, I still have trouble getting mine to work.
This is the model in C#
public class SubmitModel
{
public string Name { get; set; }
public HttpPostedFileBase File { get; set; }
public IEnumerable<HttpPostedFileBase> Files { get; set; }
}
This is the MVC code
[HttpPost]
public ActionResult Test(SubmitModel model)
{
// Here model.File and model.Files is always null
}
This is what I submitted using AngularJS
var data = {
name: scope.name, // This is passed to MVC successfully
file: scope.files[0], // Doesn't even work with single file
files: scope.files // This is a FileList
};
$http.post("/umbraco/surface/MyController/Test", data).success(...);
If you want to know how I assign scope.files:
$('#upload').on('change', function (e) {
scope.$apply(function () {
scope.files = e.target.files;
});
});
Could someone see what I am missing?
Solved it!
This is how it should be submitted
var data = new FormData();
angular.forEach(scope.item, function (value, key) {
if (key == "files") {
for (var i = 0; i < value.length; i++) {
data.append(value[i].name, value[i]); // Filename:File
}
} else {
data.append(key, value);
}
});
$http.post("/umbraco/surface/MyController/Test", data, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
}).success(...);
Then in MVC, we get the files from Request.Files, it won't be in the model.
[HttpPost]
public ActionResult Test(SubmitModel model)
{
var files = Request.Files; // a collection of HttpPostedFileBase
Save(model, files);
}
More info:
https://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs
http://www.codeproject.com/Tips/878730/File-Upload-Using-AngularJS-and-ASP-NET-MVC

Send IEnumerable from web api controller to angular js

I'm trying to send a IEnumerable from a web api controller to a AngularJs controller.
The code I was using was
Web Api:
readonly InventoryEntities _db = new InventoryEntities();
public IEnumerable<FDVOEligibilityRequest> Get()
{
return _db.FDVOEligibilityRequests.AsEnumerable();
}
AngularJS:
//get all customer information
$http.get("/api/Customer/").success(function (data) {
$scope.requests = data;
$scope.loading = false;
})
.error(function () {
$scope.error = "An Error has occured while loading posts!";
$scope.loading = false;
});
This worked fine, but now I'm using linq to include related tables and it doesn't work. The angularJs code is the same.
What am I doing wrong?
readonly InventoryEntities _db = new InventoryEntities();
public IEnumerable<FDVOEligibilityRequest> Get()
{
return _db.FDVOEligibilityRequests
.Include("FDVOEligibilityRequestMandatoryField")
.Include("FDVOEligibilityRequestDCCField").AsEnumerable();
}
I do get the data I want in the controller, but when i try to send it back to angular, I get a 500 (Internal Server Error) angular.js:10722
This is what FDVOEligibilityRequest looks like in the new Web Api controller
public partial class FDVOEligibilityRequest
{
public int Id { get; set; }
public string TestName { get; set; }
public string GroupName { get; set; }
public int MandatoryFieldsID { get; set; }
public int DCCFieldsID { get; set; }
public virtual FDVOEligibilityRequestMandatoryField FDVOEligibilityRequestMandatoryField { get; set; }
public virtual FDVOEligibilityRequestDCCField FDVOEligibilityRequestDCCField { get; set; }
}
If it's 500 and happens after you successfully make a return from your action then it can be an exception during serialization.
I would suggest to check that there are no circular references between FDVOEligibilityRequest and FDVOEligibilityRequestMandatoryField/FDVOEligibilityRequestDCCField.
Or try to diagnose it using code from here:
string Serialize<T>(MediaTypeFormatter formatter, T value)
{
// Create a dummy HTTP Content.
Stream stream = new MemoryStream();
var content = new StreamContent(stream);
/// Serialize the object.
formatter.WriteToStreamAsync(typeof(T), value, stream, content, null).Wait();
// Read the serialized string.
stream.Position = 0;
return content.ReadAsStringAsync().Result;
}
try {
var val = _db.FDVOEligibilityRequests
.Include("FDVOEligibilityRequestMandatoryField")
.Include("FDVOEligibilityRequestDCCField").AsEnumerable();
var str = Serialize(new JsonMediaTypeFormatter(), val);
}
catch (Exception x)
{ // break point here
}
ps: the common suggestion in this case is to use DTOs instead of EF objects with something like Automapper.

PUT/DELETE giving 405 error with angular and WebAPI in VS Community 2015

I am getting 405(Method not found) error in PUT and DELETE requests in my angular and WebAPI. GET and POST are working fine. I have checked all the solutions with this type of error on 'SO' but it didn't worked. I have added required handlers(with PUT/DELETE verbs) in my WebConfig, updated applicationhost.config of IIS EXpress and also uninstalled WebDAV module but still the problem persists.
Here is my controller code:
[RoutePrefix("api/BlogPost")]
public class BlogPostController : ApiController
{
// GET: api/BlogPost
public IQueryable<BlogPostModel> GetblogPostTb()
{
return db.blogPostTb;
}
// GET: api/BlogPost/5
[ResponseType(typeof(BlogPostModel))]
public IHttpActionResult GetBlogPostModel(int id)
{
BlogPostModel blogPostModel = db.blogPostTb.Find(id);
if (blogPostModel == null)
{
return NotFound();
}
return Ok(blogPostModel);
}
// PUT: api/BlogPost/5
[ResponseType(typeof(void))]
public IHttpActionResult PutBlogPostModel(int id, BlogPostModel blogPostModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != blogPostModel.ID)
{
return BadRequest();
}
db.Entry(blogPostModel).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!BlogPostModelExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
[ResponseType(typeof(BlogPostModel))]
public IHttpActionResult DeleteBlogPostModel(int id)
{
BlogPostModel blogPostModel = db.blogPostTb.Find(id);
if (blogPostModel == null)
{
return NotFound();
}
db.blogPostTb.Remove(blogPostModel);
db.SaveChanges();
return Ok(blogPostModel);
}
}
And here is the client side code:
var updateBlogPost = function (id, blogPost) {
return $http.put(blogPostsUrl+"/"+id, blogPost)
.then(function (response) {
return response;
})
Just for the info,I am working with WebAPI2, IIS Express 10 in Visual Studio Community 2015. I am not sure if this is the error of IIS EXpress 10 or Community version of VS.
This seems to be the known issue with attribute routing with WebAPI.
Here update AcceptVerbs and Route attribute with DELETE and PUT methods like this :
[ResponseType(typeof(void))]
[Route("{id:int}")]
[AcceptVerbs("PUT")]
public IHttpActionResult PutBlogPostModel(int id, BlogPostModel blogPostModel)
{
// Your code
}
And Delete as :
[ResponseType(typeof(BlogPostModel))]
[Route("{id:int}")]
[AcceptVerbs("DELETE")]
public IHttpActionResult DeleteBlogPostModel(int id)
{
// Your Code
}
And also use AcceptVerbs attribute for GET method as well because these three(GET,PUT,DELETE) have same URL structure to call their methods.

Resources