Unable to call web api POST action - ajax

Below is my web api stuff related post/save the record:
var c = $.ajax({
url: 'api/values/CreateAjaxNew',
type: 'POST',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: form.serializeArray(),
success: function (data) {
alert(data);
$("#msg").html("Saved Successfully.");
$("#msg").css("color", "green");
//window.location = "index";
//alert("in success");
},
error: function (e1, e2, e3) { alert(e3); }
});
Problem: it just gieves me error as Not Found.
Values controller api:
[HttpPost]
public HttpResponseMessage CreateAjaxNew(ProductViewModel vm)
{
try
{
// TODO: Add insert logic here
//manager.ProductManager m = new manager.ProductManager();
using (aRef.ServiceIntf2Client r = new aRef.ServiceIntf2Client())
{
r.InsertProduct(new common.DTO.ProductDTO() { Id = vm.Id, ProductName = vm.ProductName, Description = vm.Description, Cost = vm.Cost, ProductTypeId = vm.ProductTypeId });
}
//m.InsertProduct(new common.DTO.ProductDTO() { Id = vm.Id, ProductName = vm.ProductName, Description = vm.Description, Cost = vm.Cost, ProductTypeId = vm.ProductTypeId });
return Request.CreateResponse(HttpStatusCode.OK,true);
}
catch
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, false);
}
}
Please guide me how i can resolve this issue so, request reach to the api action.
routes as follow:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute("DefaultApiGet",
"api/{controller}",
new { action = "AllProductTypes" },
new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
config.Routes.MapHttpRoute("DefaultApiGet1",
"api/{controller}",
new { action = "GetAllProducts" },
new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
config.Routes.MapHttpRoute("DefaultApiGetWithId",
"api/{controller}/{id}",
new { id = RouteParameter.Optional, action = "Get" },
new { id = #"\d+" });
config.Routes.MapHttpRoute("DefaultApiWithAction",
"api/{controller}/{action}");
config.Routes.MapHttpRoute("DefaultApiWithActionAndId",
"api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional },
new { id = #"\d+(_\d+)?" });

The route ordering seems to be wrong. You need to move the default route to the last and the more generic route first .
http://richarddingwall.name/2008/08/09/three-common-aspnet-mvc-url-routing-issues/
As of now the api/{controller}/{id} the default route get executed and the controller is not found.
config.Routes.MapHttpRoute("DefaultApiGet",
"api/{controller}",
new { action = "AllProductTypes" },
new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
config.Routes.MapHttpRoute("DefaultApiGet1",
"api/{controller}",
new { action = "GetAllProducts" },
new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
config.Routes.MapHttpRoute("DefaultApiGetWithId",
"api/{controller}/{id}",
new { id = RouteParameter.Optional, action = "Get" },
new { id = #"\d+" });
config.Routes.MapHttpRoute("DefaultApiWithAction",
"api/{controller}/{action}");
config.Routes.MapHttpRoute("DefaultApiWithActionAndId",
"api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional },
new { id = #"\d+(_\d+)?" });
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
This ordering should be able to solve the issue. Hope this helps

Related

OWIN CORS Issue in Web API

I am working with WebApi and trying to add token based authentication using OWIN. It is working fine when client and service are in the same port. But facing a problem when both are on different servers.
I am using Jquery Ajax method to call the token service.
Here is the code sample I have used.
OWIN Code :
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
ConfigureOAuth(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/WagtokenService"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new ProjectAuthorizationServiceProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
Provider
public class ProjectAuthorizationServiceProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
bool isValidUser = false;
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (context.UserName == "Test#Mail.com" && context.Password == "national")
{
isValidUser = true;
}
if (!isValidUser)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "admin"));
context.Validated(identity);
}
}
WebApi Config
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("http://192.168.2.175:3330", "WagtokenService,accept,accesstoken,authorization,cache-control,pragma,content-type,origin", "GET,PUT,POST,DELETE,TRACE,HEAD,OPTIONS");
config.EnableCors(cors);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "NewActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Following code snippet will be called when login button is clicked.
$('#a_login').click(function (e) {
debugger;
if (isValidEmailAddress($('#txt_UID').val()) && $('#txt_PWD').val() != "") {
var loginData = {
grant_type: 'password',
username: $('#txt_UID').val(),
password: $('#txt_PWD').val()
};
$.ajax({
url: url_bearerToken,
type: 'POST',
data: loginData,
contentType: "application/json",
done: function (data) {
// alert('success fully sign in to the application');
sessionStorage.setItem(bearer_token_key, data.access_token);
},
success: function (data) {
// alert('success fully sign in to the application');
sessionStorage.setItem(bearer_token_key, data.access_token);
window.location.href = "../Admin/UserProfiler.html";
},
error: function (x, h, r) {
///e.preventDefault();
// alert("Invalid user credentials");
$('#div_alert').show();
sessionStorage.setItem(bearer_token_key, '');
}
});
}
else {
$('#div_alert').show();
}
});
Getting Following issue .
XMLHttpRequest cannot load http://localhost:53014/WagtokenService. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://192.168.2.175:3330' is therefore not allowed access.
As Marcus said , it is enough to mention CORS setting either in Web API or in OWIN.

Mvc web api route generation issue

I am trying to generate a url from within one of my web api actions:
var resultsModel =
results.Select(
r =>
new ResultModel(criteria,
r,
Url.Link("Default", new { Controller = "CV", Action = "Get", candidateid = r.CandidateId, filename = r.Name })
, false))
.ToList();
however the genrated url is missing the "api/" prepender and therefor does not work as the url cannot be resolved.
https://temp-search.recruiter.workstation.local/recruiter/temp-search/cv/get?candidateid=14350164&filename=Douggersby%20Mondays&id=143501
the following is my web api config:
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new AuthorizationHandler());
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
You're referring to the 'Default' route instead of the 'DefaultApi' route.
Instead of:
Url.Link("Default", new { Controller = "CV", Action = "Get", candidateid = r.CandidateId, filename = r.Name })
, false))
Use:
Url.Link("DefaultApi", new { Controller = "CV", Action = "Get", candidateid = r.CandidateId, filename = r.Name })
, false))
And the 'DefaultApi' route template will be used to generate the url, including the api/ prefix.

How to call web api multiple GET methods through AJAX

I have added two GET methods into webapi as follow:
public IList<ProductDTO> GetAllProducts()
{
ProductManager pm = new ProductManager();
return pm.RetrieveAllProducts();
//return pm.RetrieveAllProducts();
}
public ProductDTO GetProduct(int i)
{
ProductManager pm = new ProductManager();
return pm.RetrieveAllProducts().Where(c => c.Id == i).FirstOrDefault();
//return pm.RetrieveAllProducts();
}
Problem, when i only kept one get method GetAllProducts() then it works fine. but when i added GetProduct(int i) then giving me error as Not found 404 error.
Please guide me how i could keep both the mthod and allow to access method having argument.
calling as follow:
$.ajax({
type: 'GET',
url: 'api/values/GetProduct/1', //giving error as NOT FOUND
contentType: 'json',
dataType: 'json',
success: function (data) {
$.each(data, function (key, value) {
//stringify
alert(key);
alert(value);
var jsonData = JSON.stringify(value);
//Parse JSON
var objData = $.parseJSON(jsonData);
var id = objData.Id;
var Cost = objData.Cost;
var ProductName = objData.ProductName;
var Description = objData.Description;
$('<tr><td>' + id + '</td><td>' + ProductName +
'</td><td>' + Cost + '</td></tr>').appendTo('#ProductDivWebApi');
});
},
error: function (xhr) {
alert(xhr.responseText);
}
});
i have added this WEBAPI into MVC 4 project.
its route shows as below:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "ProductManagement", action = "Index", id = UrlParameter.Optional }
);
}
Please guide
The routes on the ASP.NET WEB API is different than ASP.NET MVC. You have the GET methods, so, just call the controller using the GET verb and the framework will solve it for you. I recommend you rename the methods to a verb, Get, Post, Put, Delete, etc.
In your case, call the method by /api/{controller}/{id}. In this case, data is a single object (because you have returned only a ProductDTO), so do not loop in it.
You have specified json, so jquery will be deserialized it in a object for you. For sample:
$.ajax({
type: 'GET',
url: 'api/values/1',
contentType: 'json',
dataType: 'json',
success: function (data) {
// data is a single object, do not loop in it.
// you have specified json, so, it will be deserialized
var id = data.Id;
var Cost = data.Cost;
var ProductName = data.ProductName;
var Description = data.Description;
$('<tr><td>' + id + '</td><td>' + ProductName +
'</td><td>' + Cost + '</td></tr>').appendTo('#ProductDivWebApi');
});
},
error: function (xhr) {
alert(xhr.responseText);
}
});
I also recommend you to do some things like this on Web API:
[HttpGet]
public HttpResponseMessage GetProduct(int id)
{
ProductManager pm = new ProductManager();
var result = pm.RetrieveAllProducts().Where(c => c.Id == id).FirstOrDefault();
return Request.CreateResponse(HttpStatusCode.OK, result);
}
On the WebApiConfig.cs file I recommend you configure the routes like this:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute("DefaultApiGet",
"api/{controller}",
new {action = "Get"},
new {httpMethod = new HttpMethodConstraint(HttpMethod.Get)});
config.Routes.MapHttpRoute("DefaultApiGetWithId",
"api/{controller}/{id}",
new {id = RouteParameter.Optional, action = "Get"},
new {id = #"\d+"});
config.Routes.MapHttpRoute("DefaultApiWithAction",
"api/{controller}/{action}");
config.Routes.MapHttpRoute("DefaultApiWithActionAndId",
"api/{controller}/{action}/{id}",
new {id = RouteParameter.Optional},
new {id = #"\d+(_\d+)?"});
}

ajax.actionlink shows different urls on server and visual studio

I am trying to use ajax.actionlink to remove the number of items in my shoppingcart. I added an ajax.actionlink which works fine on my local computer (with Visual Studio 2010), but not on my server. Here is the code:
#Ajax.ActionLink("-",
"RemoveQuantityFromProduct",
"ShoppingCart",
new { productId = item.Id.ToString() },
new AjaxOptions
{
UpdateTargetId = "AjaxMiniShoppingCart",
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
OnSuccess="toggleCart",
})
On my local computer the resulting link is:
http://localhost:2565/shoppingcart/removequantityfromproduct/16
On my server however the resulting link is instead:
http://www.domain.com/shoppingcart/removequantityfromproduct?productId=16
Both links works on the local computer, but both links results in a 404-error on the server.
Do anyone know why the url is different on the server and the local computer? Can anyone explain why the routing works on the local computer but not on the server?
(I am using nopCommerce 2.1 as a base for this webshop.)
[EDIT: Corrected the resulting URL:s. I typed the URL for adding a product instead of the URL for removing a product.] 2011-10-10 11:23
[EDIT added the RouteProvider.cs from NopCommerce] 2011-10-10 11:30
//home page
routes.MapLocalizedRoute("HomePage",
"",
new { controller = "Home", action = "Index"},
new[] { "Nop.Web.Controllers" });
//products
routes.MapLocalizedRoute("Product",
"p/{productId}/{SeName}",
new { controller = "Catalog", action = "Product", SeName = UrlParameter.Optional },
new { productId = #"\d+" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("RecentlyViewedProducts",
"recentlyviewedproducts/",
new { controller = "Catalog", action = "RecentlyViewedProducts" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("RecentlyAddedProducts",
"newproducts/",
new { controller = "Catalog", action = "RecentlyAddedProducts" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("RecentlyAddedProductsRSS",
"newproducts/rss",
new { controller = "Catalog", action = "RecentlyAddedProductsRss" },
new[] { "Nop.Web.Controllers" });
//comparing products
routes.MapLocalizedRoute("AddProductToCompare",
"compareproducts/add/{productId}",
new { controller = "Catalog", action = "AddProductToCompareList" },
new { productId = #"\d+" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("CompareProducts",
"compareproducts/",
new { controller = "Catalog", action = "CompareProducts" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("RemoveProductFromCompareList",
"compareproducts/remove/{productId}",
new { controller = "Catalog", action = "RemoveProductFromCompareList"},
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("ClearCompareList",
"clearcomparelist/",
new { controller = "Catalog", action = "ClearCompareList" },
new[] { "Nop.Web.Controllers" });
//product email a friend
routes.MapLocalizedRoute("ProductEmailAFriend",
"productemailafriend/{productId}",
new { controller = "Catalog", action = "ProductEmailAFriend" },
new { productId = #"\d+" },
new[] { "Nop.Web.Controllers" });
//catalog
routes.MapLocalizedRoute("Category",
"c/{categoryId}/{SeName}",
new { controller = "Catalog", action = "Category", SeName = UrlParameter.Optional },
new { categoryId = #"\d+" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("ManufacturerList",
"manufacturer/all/",
new { controller = "Catalog", action = "ManufacturerAll" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("Manufacturer",
"m/{manufacturerId}/{SeName}",
new { controller = "Catalog", action = "Manufacturer", SeName = UrlParameter.Optional },
new { manufacturerId = #"\d+" },
new[] { "Nop.Web.Controllers" });
//reviews
routes.MapLocalizedRoute("ProductReviews",
"productreviews/{productId}",
new { controller = "Catalog", action = "ProductReviews" },
new[] { "Nop.Web.Controllers" });
//login, register
routes.MapLocalizedRoute("Login",
"login/",
new { controller = "Customer", action = "Login" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("LoginCheckoutAsGuest",
"login/checkoutAsGuest",
new { controller = "Customer", action = "Login", checkoutAsGuest = true },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("Register",
"register/",
new { controller = "Customer", action = "Register" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("Logout",
"logout/",
new { controller = "Customer", action = "Logout" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("RegisterResult",
"registerresult/{resultId}",
new { controller = "Customer", action = "RegisterResult" },
new { resultId = #"\d+" },
new[] { "Nop.Web.Controllers" });
//shopping cart
routes.MapLocalizedRoute("AddProductToCart",
"cart/addproduct/{productId}",
new { controller = "ShoppingCart", action = "AddProductToCart" },
new { productId = #"\d+" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("ShoppingCart",
"cart/",
new { controller = "ShoppingCart", action = "Cart" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("AjaxShoppingCart",
"cart/",
new { controller = "ShoppingCart", action = "Cart" },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("AddQuantity",
"shoppingcart/addquantitytoproduct/{productId}",
new { controller = "ShoppingCart", action = "AddQuantityToProduct", productId = UrlParameter.Optional },
new[] { "Nop.Web.Controllers" });
routes.MapLocalizedRoute("RemoveQuantity",
"shoppingcart/removequantityfromproduct/{productId}",
new { controller = "ShoppingCart", action = "RemoveQuantityFromProduct", productId = UrlParameter.Optional },
new[] { "Nop.Web.Controllers" });
Action method:
public ActionResult RemoveQuantityFromProduct(int productId)
{
Response.CacheControl = "no-cache";
Response.Cache.SetETag((Guid.NewGuid()).ToString());
var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
foreach (var sci in cart)
{
if (productId.Equals(sci.Id))
{
if (sci.Quantity > 1)
{
_shoppingCartService.UpdateShoppingCartItem(_workContext.CurrentCustomer, sci.Id, sci.Quantity - 1, true);
}
else
{
_shoppingCartService.DeleteShoppingCartItem(sci, true);
}
}
}
//updated cart
cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
var model = PrepareShoppingCartModel(new MiniShoppingCartModel(), cart, true);
return PartialView("AjaxMiniShoppingCart", model);
}
My guess is that your routes are different between your local and production servers. On your local machine the route in global.asax.cs is matching the url and routing correctly.
On your production server I don't think its matching the route. So I suggest that you check both local and production are in sync.
If you post your code from RegisterRoutes -> Global.asax.cs might be able to help further (both production and dev).
It might be a typo but in your post the actionlink has removequantityfromproduct but the url you pasted has addquantitytoproduct - two different actions. So it could be that you have a maproute that is incorrectly matching the action removequantityfromproduct and redirecting to addquantitytoproduct.
The answer to this one was quite simple... I built the solution in release mode, but for some reason the script for deploying took some code from the debug folder instead, that is why the routing was incorrect...
#Ajax.ActionLink("-",
"RemoveQuantityFromProduct",
"ShoppingCart",
new { productId = item.Id.ToString() },
new AjaxOptions
{
UpdateTargetId = "AjaxMiniShoppingCart",
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
OnSuccess="toggleCart",
}, null)
Try setting HTML attributes to null.

MVC3 Ajax.ActionLink

For the following:
#Ajax.ActionLink("Delete", "Delete", "AdminGroup", new { id = item.AdminGroupId }, new AjaxOptions { Confirm = "Delete?", HttpMethod = "Delete", OnSuccess = "function() { $(this).parent().parent().remove() }" })
OnSuccess get's errored out. please help.
thanks
It should be like this:
#Ajax.ActionLink(
"Delete",
"Delete",
"AdminGroup",
new { id = item.AdminGroupId },
new AjaxOptions {
Confirm = "Delete?",
HttpMethod = "Delete",
OnSuccess = "handleSuccess"
}
)
where you have:
<script type="text/javascript">
function handleSuccess() {
// TODO: handle the success
// be careful because $(this) won't be
// what you think it is in this callback.
}
</script>
Here's an alternative solution I would recommend you:
#Html.ActionLink(
"Delete",
"Delete",
"AdminGroup",
new { id = item.AdminGroupId },
new { id = "delete" }
)
and then in a separate javascript file AJAXify the link:
$(function() {
$('#delete').click(function() {
if (confirm('Delete?')) {
var $link = $(this);
$.ajax({
url: this.href,
type: 'DELETE',
success: function(result) {
$link.parent().parent().remove();
}
});
}
return false;
});
});

Resources