I have a little problem with routing in my app Angular + wep api. I want to put user, and it doesn't work. Server returns 404 and error:
Failed to load resource: the server responded with a status of 404 (Not Found)
and
Message":"No HTTP resource was found that matches the request URI 'http://localhost:53544/api/Users/PutUser/1'.","MessageDetail":"No action was found on the controller 'Users' that matches the name 'PutUser'."
It's strange because the method exists.
The entirety of my code follows:
My route:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
My put method:
[HttpPut]
[ActionName("PutUser/{userId}")]
[ResponseType(typeof(void))]
public IHttpActionResult PutUser(int userId, User user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (userId != user.Id)
{
return BadRequest();
}
db.Entry(user).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!UserExists(userId))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
My angular put method:
this.PutUser = function (userId, user) {
var promise = $http({
method: 'PUT',
url: 'api/Users/PutUser/' + userId,
data: user
})
.then(function (response) {
return "update";
},
function (response) {
return response.statusText;
});
return promise;
}
Specify the [FromUri] and [FromBody] to define parameter mapping
[HttpPut]
[ActionName("PutUser/{userId}")]
[ResponseType(typeof(void))]
public IHttpActionResult PutUser([FromUri]int userId, [FromBody]User user)
{
You have to make sure that the post request HTTP header contains
Content-Type: application/x-www-form-urlencoded
Related
Problem :
API controller cannot access HTTP request data which are included FormData object in HTTP request
I need to pass file object data appended in FormData Object in http Post request to asp.net api controller and front-end is angularjs.i can not retrieve http request data from api controller.my code is below. please look into this, it would be great :)
when i pass,
Content-type : undefined
the error says 415 Unsupported Media Type
if Content-type: multipart/form-data then cannot access data from API controller.
Front-end
$scope.submit = function (files) {
var formData = new FormData();
var getFormData = function(appendFiles){
if (appendFiles.length) {
angular.forEach(appendFiles,function(file){
if(!file.uploaded){
formData.append("imgFiles",file);
file.uploaded = true;
}
});
} else {
formData.append('imgFiles', files);
}
console.log(formData.values());
return formData;
}
$http({
url : "URL",
method: "POST",
data: getFormData(files),
headers: {
'Content-Type': undefined
},
transformRequest: angular.identity,
})
.then(
function (resp) {
// alert(JSON.stringify(resp));
console.log(resp)
},
function (resp) {
console.log(resp)
}
);
};
Api controller Method
[HttpPost]
[Route("route")]
public string UploadFiles(List<HttpPostedFileBase> files)
{
var filesToDelete = HttpContext.Current.Request.Files;
//i need to access file here.using param or otherway
return stat;
}
I have solved the problem.i have changed the api controller method as below,
[HttpPost]
[Route("route")]
public async Task<string> UploadFiles()
{
FileUploadService fileService = new FileUploadService();
if (!Request.Content.IsMimeMultipartContent())
{
this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
var uploadFolder = "upload folder physical path"
//this method is in below
var provider = GetMultipartProvider(uploadFolder);
await Request.Content.ReadAsMultipartAsync(provider);
foreach (MultipartFileData fileData in provider.FileData)
{
var physicalPath = fileData.LocalFileName;
var fileName = fileData.Headers.ContentDisposition.FileName;
}
return "return value";
}
private MultipartFormDataStreamProvider GetMultipartProvider(string uploadFolder)
{
try
{
var root = HttpContext.Current.Server.MapPath(uploadFolder);
if (!Directory.Exists(root))
{
Directory.CreateDirectory(root);
}
return new MultipartFormDataStreamProvider(root);
}
catch (Exception ex)
{
throw ex;
}
}
I'm trying to use Angularjs to send a Post request to My Spring Mvc Controller to login User.But I can't get the Parameter from the request.
this is my Angular js code:
$scope.submit = function () {
$http({
url: serviceURL.LoginUrl,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
phone: $scope.userName,
password: $scope.userPsw,
}
}).success(function (data) {
if (!data.state) {
alert(data.errorMsg);
} else {
alert('success');
}
console.log(data);
}).error(function (data) {
console.log('服务器错误!');
});
}
and this is the Spring MVC Controller code:
#RequestMapping(value = "/login", method = RequestMethod.POST)
#ResponseBody
public Object loginUser(Model model,User user, HttpSession session, HttpServletRequest request) {
String phone = request.getParameter("phone");
String password = request.getParameter("password");
System.out.println(phone+","+password);
System.out.println(user.getPhone()+","+user.getPassword());
UserDTO u = userService.loginUser(phone, password);
session.setAttribute("loginUser",u.getUser());
return u;
}
I have searched many resource,they said I should change the header and I have set the header:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
return true;
}
Actually,I can't request the login url,but after I setHeader,I can request the url,but the parameter is null.
Forgive my poor English, I am newbie in StackOverFlow.
I didn't konw is it have the same question in here ,but I can find the same question. Thank you for your view.
There are two points to fix. At first, data should be converted to a URL-encoded string. You can convert data object with $.param() method or set params property instad of data so it will look like this:
$scope.submit = function () {
$http({
url: serviceURL.LoginUrl,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
params: {
phone: $scope.userName,
password: $scope.userPsw,
}
}).success(function (data) {
if (!data.state) {
alert(data.errorMsg);
} else {
alert('success');
}
console.log(data);
}).error(function (data) {
console.log('服务器错误!');
});
}
The second point is server-side controller method. Here you have to annotate method's arguments appropriately. Consider using #RequestParam annotation.
#RequestMapping(value = "/login", method = RequestMethod.POST)
#ResponseBody
public Object loginUser(
#RequestParam String phone,
#RequestParam String password,
HttpSession session,
HttpServletRequest request
) {
System.out.println(phone + ", " + password);
UserDTO u = userService.loginUser(phone, password);
session.setAttribute("loginUser", u.getUser());
return u;
}
<!--In your script-->
var app = angular.module("myApp", [])
.controller("myController", function($http){
var vm= this;
Posting = function(name)
{
var data = 'name=' + name;
var url="example.htm";
$http.post(url, data).then(function (response) {
vm.msg = response.data;
alert(vm.msg);
});
}
});
// Above is same as using GET, but here below is important
//Dont forget to add this config ortherwise http bad request 400 error
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.headers.post['Content-Type'] =
'application/x-www-form-urlencoded; charset=UTF-8';
}]);
//In spring controller same as of GET Method
#RequestMapping(value="example.htm", method = RequestMethod.POST)
#ModelAttribute("msg")
public String doingPost(#RequestParam(value="name") String name){
System.out.println(name);
return "successfully Posted";
}
I Am trying to call an Async method in MVC controller (example Login) from an angular client and calls fail. I tried it with google postman tool also.
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
and here is the snippet of the angular service that is calling the Login method
var loginUser = function (email, password, returnUrl) {
var req = {
method: 'post',
url: '/Mysite/Login',
headers: {
'Content-Yype': undefined
},
data: {
model: {
Email: email,
Password: password,
RememberMe: false
},
returnUrl: returnUrl
}
};
return $http(req)
.then(function (response) {
return response.data;
}, function (reason) {
return reason;
});
};
the response throws me internal error with status 500.
Does angular.js support asynchronous calls to web methods?
Thanks and appreciate your help
If you are using the [ValidateAntiForgeryToken] decorator, the action needs a request verification token to be passed in the post data.
You could remove [ValidateAntiForgeryToken] but this would leave your action open to tampered requests.
The other option is to add an anti forgery token to the page and then pass its value in the request.
Your razor view will need a form with a token in it (Note: This is just a dummy form to allow the token to be added to the page).
#using(Html.BeginForm("Login", "ControllerName", FormMethod.Post, new { id = "verification-form"}) {
#Html.AntiForgeryToken()
}
In your javascript, you can then pass its value
var loginUser = function (email, password, returnUrl) {
var req = {
method: 'post',
url: '/Mysite/Login',
headers: {
'Content-Type': undefined
},
data: {
model: {
Email: email,
Password: password,
RememberMe: false
},
__RequestVerificationToken: $("#verification-form input[name=__RequestVerificationToken]").val(),
returnUrl: returnUrl
}
};
return $http(req)
.then(function (response) {
return response.data;
}, function (reason) {
return reason;
});
};
I am trying to login and make a post to my project on my web host. I have removed the cors issue by adding the cors attribute to my web api 2 controller and my WebApiConfig.cs file. When I make a post with my login information included my console.log message shows the html of my login.cshtml file that is on my web host. And the "Job" information I am trying to post is not being posted as well.
Angular Controller
$scope.submitJob = function () {
var data = {
username: $scope.currentItem.username,
password: $scope.currentItem.password,
JobId: $scope.JobId,
JobNumber: $scope.currentItem.JobNumber,
JobName: $scope.currentItem.JobName,
}
var json = JSON.stringify(data);
// json is the json data you need to post
$http.post('http://www.example.com/api/apiJob', json, { headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } })
.success(function (data, status) {
console.log(data);
})
.error(function (data, status) {
// error code
});
};
Web Api 2 Controller
[Authorize]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class apiJobController : ApiController
{
// GET api/<controller>
[AllowAnonymous]
public IEnumerable<JobResult> Get()
{
using (var context = new ApplicationDbContext())
{
return context.Jobs
.ToResults();
}
}
// GET api/<controller>/5
public Job GetJob(int? id)
{
using (var context = new ApplicationDbContext())
{
Job model = new Job();
model = context.Jobs
.FirstOrDefault(j => j.JobId == id);
return model;
}
}
// POST api/<controller>
public async Task<IHttpActionResult> PostnewJob([FromBody]JobViewModel newJob)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
using (var context = new ApplicationDbContext())
{
var job = new Job();
Mapper.CreateMap<JobViewModel, Job>();
Mapper.Map(newJob, job);
context.Jobs.Add(job);
await context.SaveChangesAsync();
return CreatedAtRoute("JobApi", new { job.JobId }, job);
}
}
Screenshot
ng-resource returns an object with the following default resource actions
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
I'm not sure exactly the best method for querying data from REST WebApi endpoints from AngularJS, but I've implemented Predicate Builder server-side in order to query my db using Linq. I have a (POST) endpoint named "Search()" #/api/Product/Search that will accept a searchCriteria JSON object which is deserialized, fed to the Linq predicate builder, and executed against the dbContext. My WebApi2 controller is structured like this, using the new route attribute feature:
[RoutePrefix("Product")]
public class ProductController : ApiController
{
[HttpGet]
[Route("")]
public IEnumerable<Product> Get()
{
try
{
return _productBL.Get();
}
catch
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
[HttpGet]
[Route("{productId}")]
public Product Get(string productId)
{
try
{
var product= _externalWorkStepBL.GetById(productId);
if (product== null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return product;
}
catch (Exception)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
[HttpPost]
public HttpResponseMessage Post([FromBody]Product product)
{
try
{
_productBL.Insert(product);
var response = Request.CreateResponse(HttpStatusCode.Created, product);
response.Headers.Location = new Uri(Request.RequestUri, string.Format("Product/{0}", product.workItemID));
return response;
}
catch
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
}
[HttpPost]
[Route("Search")]
public IEnumerable<Product> Where([FromBody] SearchCriteria searchCriteria)
{
if (searchCriteria == null || (searchCriteria.FieldContainsList == null || searchCriteria.FieldEqualsList == null || searchCriteria.FieldDateBetweenList == null))
{
throw new HttpRequestException("Error in, or null, JSON");
}
return _productBL.Where(searchCriteria);
}
[HttpPut]
[Route("")]
public HttpResponseMessage Put([FromBody]Productproduct)
{
try
{
_productBL.Update(product);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Headers.Location = new Uri(Request.RequestUri, string.Format("Product/{0}", product.Id));
return response;
}
catch ()
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
[HttpDelete]
[Route("{productId}")]
public void Delete(string productId)
{
HttpResponseMessage response;
try
{
_productBL.Delete(productId);
response = new HttpResponseMessage(HttpStatusCode.NoContent);
response.Headers.Location = new Uri(Request.RequestUri, string.Format("Product/"));
}
catch (Exception)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
}
On the client side I've wrapped $resource in a factory called $myResource, adding a PUT method. I then use $myResource for my other factories as follows:
var app = angular.module('App', ['ngResource'])
.factory('$myResource', ['$resource', function ($resource) {
return function (url, paramDefaults, actions) {
var MY_ACTIONS = {
'update': { method: 'PUT' }
};
actions = angular.extend({}, MY_ACTIONS, actions);
return $resource(url, paramDefaults, actions);
}
}])
.service('ProductFactory', ['$myResource', function ($myResource) {
return $myResource('/api/Product/:productId')
}]);
This works great, but now I wish to add my Search endpoint. The Angular documentation for ng-Resource states that a url can be overridden in the action method, but it's not clear to me how to do this. I'm able to add the "search" action to $myResource, but how do I modify the url in the ProductFactory?
.factory('$myResource', ['$resource', function ($resource) {
return function (url, paramDefaults, actions) {
var MY_ACTIONS = {
'update': { method: 'PUT' },
'search': { method: 'POST','params': { searchCriteria: '#searchCriteria' }, isArray: true }
};
actions = angular.extend({}, MY_ACTIONS, actions);
return $resource(url, paramDefaults, actions);
}
}])
As it currently is, calling ProductFactory.search(searchCriteria) sends a POST request with the correct JSON, but to the wrong url, "/api/Product". I need it to post to "/api/Product/Search". How can I modify $myResource to use "api/xxx/Search" where xxx is the controllername?
Nevermind! Didn't expect this to work, but it does.
.factory('$myResource', ['$resource', function ($resource) {
return function (url, paramDefaults, actions) {
var searchUrl = url + "/Search/:searchCriteria"
var MY_ACTIONS = {
'update': { method: 'PUT' }, //add update (PUT) method for WebAPI endpoint
'search': { method: 'POST', url : searchUrl,'params': { searchCriteria: '#searchCriteria' }, isArray: true } //add Search (POST)
};
actions = angular.extend({}, MY_ACTIONS, actions);
return $resource(url, paramDefaults, actions);
}
}])