apicontroller not getting from ajax - asp.net-mvc-4

I get "404 Not Found" on doing a Ajax call ,
maybe that I don't understand how routing works ...
function ApproveAlert(ID) {
$.ajaxPost('/api/DeviceApi/ApproveAlertNew', { 'ID': ID }, function (data) {
... get error "404 Not Found"
}, null);
}
in my mvc4 C# app I have a rout config :
RouteTable.Routes.MapHttpRoute(
name: "defaultapiaction",
routeTemplate: "api/{controller}/{action}"
);
RouteTable.Routes.MapHttpRoute(
name: "defaultapiid",
routeTemplate: "api/{controller}/{action}/{id}"
);
and apicontroller :
public class DeviceApiController : ApiController
{
//
// GET: /DeviceApi/
[HttpPost]
public bool ApproveAlertNew(int ID)
{
..do
}

Web API controllers don't use "Actions" in the same sense that MVC controllers do. Web API controllers also don't really use [HttpPost], [HttpGet] attributes either. They dispatch requests based on the names of the methods inside of the ApiControllers. I suggest to read up a little more on Web API differences from MVC as it is similar but sometimes hard to get up and running...
Here's some pretty generic examples from a Web API that I made for testing. I do not have JavaScript to post to this API because I was posting from a .NET WPF app. You would post to "/Important" NOT "/Important/Post" Hopefully this will get you on the right track...
WebAPIConfig.cs (Routes):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace ArrayTest.WebAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
API Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using ArrayTest.Models;
using System.Threading;
namespace ArrayTest.WebAPI.Controllers
{
public class ImportantController : ApiController
{
// POST api/important
public HttpResponseMessage Post(ImportantList values)
{
//manipulate values received from client
for (int i = 0; i < values.ImportantIDs.Count; i++)
{
values.ImportantIDs[i] = values.ImportantIDs[i] * 2;
}
//perhaps save to database, send emails, etc... here.
Thread.Sleep(5000); //simulate important work
//in my case I am changing values and sending the values back here.
return Request.CreateResponse(HttpStatusCode.Created, values);
}
}
}
Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ArrayTest.Models
{
public class ImportantList
{
public List<int> ImportantIDs { get; set; }
}
}

Can you try with:
function ApproveAlert(ID) {
$.ajax({
type: 'POST',
url: "/api/DeviceApi/ApproveAlertNew",
data: {
ID: ID
},
success: function (data) {
//Handle your success
},
error: function (jqXHR, textStatus, errorThrown) {
//Handle error
}
});
}

Related

.NET CORE web api Changes not detected

I'm currently working on a VueJS app using .NET Core 2.2 for the back-end part.
I was working on it for a few months but it suddenly stopped working like a charm when I updated from 2.0 to 2.2.
My web API changes are not detected and I don't know why.
For instance, I have a few controllers and whenever I change them, and then use the web API, the changes are not made. I can even delete the whole file and the web API using this file will still be working!
Another problem I get is that when I create new controller files, it's not detected; I'm stuck with my old controllers, which I'm not able to update.
Others files updates are detected (at least if I change the VueJS front-end)
I can also change the providers, delete whatever file used for the web API, changes are not detected. It may be a configuration issue?
Is there anything I could try to make things update again?
Update: I can change whatever I want in the back-end and it will do nothing. Compilations errors are the only problem I have to care about, it's like the app doesn't use the code anymore.
Here is an example I can provide:
I have a controller InterventionController which retrieve data about operations (I am french in a french context so variables names, etc will be in french) :
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Vue2Spa.Models;
using Vue2Spa.Providers;
namespace Vue2Spa.Controllers
{
[Produces("application/json")]
[Route("api/[controller]")]
[ApiController]
public class InterventionController : Controller
{
private readonly IInterventionProvider interventionProvider;
public InterventionController(IInterventionProvider interventionProvider)
{
this.interventionProvider = interventionProvider;
}
[HttpGet("[action]")]
public IActionResult Interventions([FromQuery(Name = "from")] int from = 0, [FromQuery(Name = "to")] int to = 5000)
{
var quantity = to - from;
if (quantity <= 0)
{
return BadRequest("La quantité doit être positive !");
}
else if (from < 0)
{
return BadRequest("Vous devez spécifier un indice de départ non nul !");
}
var allInterventions = interventionProvider.GetInterventions();
var result = new
{
TotalInterventions = allInterventions.Count,
Interventions = allInterventions.Skip(from).Take(quantity).ToArray()
};
return Ok(result);
}
}
// Others methods not useful for my example
}
It calls a provider which has the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Vue2Spa.Models;
namespace Vue2Spa.Providers
{
public class DBInterventionProvider : IInterventionProvider
{
private List<Intervention> interventions { get; set; }
DbContextOptionsBuilder<DepouillementTestContext> optionsBuilder = new DbContextOptionsBuilder<DepouillementTestContext>();
public DBInterventionProvider()
{
optionsBuilder.UseSqlServer(credentials); // Credentials are correct but not including it there for obvious reasons
using (var context = new LECESDepouillementTestContext(optionsBuilder.Options))
{
interventions = context.Intervention.ToList();
}
}
public List<Intervention> GetInterventions()
{
using (var context = new LECESDepouillementTestContext(optionsBuilder.Options))
{
interventions = context.Intervention.ToList();
}
return interventions;
}
// Others methods not useful for this example
}
}
I can delete these files, and I'm still able to access my operations web API
If needed, here is my startup.cs file:
using System;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Vue2Spa.Models;
namespace Vue2Spa
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
// Additional code for SQL connection
services.AddDbContext<DepouillementTestContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.
MigrationsAssembly(
typeof(Startup).
GetTypeInfo().
Assembly.
GetName().Name);
//Configuring Connection Resiliency:
sqlOptions.
EnableRetryOnFailure(maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
});
// Changing default behavior when client evaluation occurs to throw.
// Default in EFCore would be to log warning when client evaluation is done.
options.ConfigureWarnings(warnings => warnings.Throw(
RelationalEventId.QueryClientEvaluationWarning));
});
// Provider pour les interventions
services.AddSingleton<Providers.IInterventionProvider, Providers.DBInterventionProvider>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// Webpack initialization with hot-reload.
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true,
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}
}
Thanks in advance,
Well I've found why I had this problem, and I feel kinda dumb for that but well, it's working now.
I didn't change my launch.json when I upgraded from .NETCORE 2.0 to 2.2, all I had to do was changing
"program": "${workspaceFolder}/content/bin/Debug/netcoreapp2.0/Vue2Spa.dll",
by
"program": "${workspaceFolder}/content/bin/Debug/netcoreapp2.2/Vue2Spa.dll",
For more informations, see : https://learn.microsoft.com/en-us/aspnet/core/migration/21-to-22?view=aspnetcore-2.2&tabs=visual-studio

MVC4 how to set default url?

I'm working on building a MVC4 web app and want to set specific URL I would like to use in the start point of my web application.
So I changed some values in RouteConfig.cs like this below.
routes.MapRoute(
name: "Default",
url: "{action}.mon/{id}",
defaults: new { controller = "login", action = "index", id = UrlParameter.Optional }
);
you may notify this, but I put a suffix after action name, so that I could invoke a controller, displaying the URL like " index.mon "
if I manually put "index.mon" after host address in URL bar, then it works just okay.
But when the app gets started automatically, it throws 403.14 error. ( "automatically start" means here that I ran this app by putting F5 key to run a temporary IIS server. )
the login controller looks like this
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.OleDb;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Monarch815MVC.Controllers
{
public class loginController : Controller
{
//
// GET: /login/
public ActionResult index()
{
return View();
}
public ActionResult loginProcess(string id = "000000", string pass = "example", string scode = "co")
{
Dictionary<string, object> sessionData = null;
String SqlCommand = "USP_LOGIN";
DataSet UserInfo = dataController.ExecuteDataset(dataController.CONN_STRING, CommandType.StoredProcedure, SqlCommand, arParms);
if (UserInfo.Tables[0].Rows.Count > 0)
{
sessionData = new Dictionary<string, object>();
for (int i = 0; UserInfo.Tables[0].Rows[0].Table.Columns.Count > i; i++)
{
sessionData.Add(UserInfo.Tables[0].Rows[0].Table.Columns[i].Caption, UserInfo.Tables[0].Rows[0].ItemArray[i]);
}
}
return View();
}
}
}
( let's forget about the loginProcess, I took off a few codes. )
Do I have to do something on returning phase in index() or on Web.config? or, RouteConfig.cs?
I have to use the suffix ".mon" to invoke controllers with it.
I could think of solving this issue by only using a hack. You might want to create a dummy action which will be the default action and upon being hit will redirect the request to your index.mon
i tried following, Please check if this helps you. Thanks.
In the route config
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "DefaultY",
url: "{action}-mon/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "DefaultX2",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Startup", id = UrlParameter.Optional }
);
}
In controller
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Startup()
{
return RedirectToRoute("DefaultY", new {controller = "Home", action = "Index" });
}
}
For me i used index-mon. index.mon was not working (something which i'l explore later, perhaps it relates to static files), but above code demonstrates the approach.
Hope that helps.

ASP.Net MVC4->How to setup this routing

I was wondering how to setup routing in my MVC4 application where I can have a controller named TMZ and have it handle all of these routes:
/TMZ/About
/TMZ/Webinars
/TMZ/News
/TMZ/Conferment
/TMZ/CustomerCare
/TMZ/Marketing/Emails
/TMZ/Marketing/Brochures
/TMZ/Marketing/Print
/TMZ/Marketing/Press
/TMZ/Marketing/Presentations
/TMZ/Marketing/Graphics
/TMZ/Marketing/OCSRY
/TMZ/Marketing/Resources
/TMZ/Marketing/DesignStandards
/TMZ/Marketing/Videos
/TMZ/Marketing/PromoKits
/TMZ/Faculty/Forms
/TMZ/Faculty/Reports
/TMZ/CE/Guides
/TMZ/CE/Reports
/TMZ/Academy/Papers
/TMZ/Academy/Books
/TMZ/Academy/Promotions
/TMZ/ManualOfOperations
Showing Code:
Here is my RouteConfig.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace LicenseeArchive
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("TMZ",
"TMZ/{action}/{subaction}/{id}",
new { controller = "TMZ", action = "Index", subaction = UrlParameter.Optional, id = UrlParameter.Optional },
null,
new[] { "LicenseeArchive.Web.Controllers" });
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
null,
new[] { "LicenseeArchive.Web.Controllers" });
}
}
}
Here is my Global.asax.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace LicenseeArchive
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
}
Here is my TMZController:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace LicenseeArchive.Controllers
{
public class TMZController : Controller
{
//
// GET: /TMZ/
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
public ActionResult Marketing(string subaction)
{
string _view = "Index";
switch (subaction)
{
case "Brochures":
_view = "Marketing/Brochures";
break;
}
return View(_view);
}
}
}
And here is my View folder structure:
Views
Account
Login.cshtml
Manage.cshtml
Register.cshtml
Home
About.cshtml
Contact.cshtml
Index.cshtml
Shared
_Footer.cshtml
_Head.cshtml
_HeaderBlock.cshtml
_Layout.cshtml
_LeftAside.cshtml
_LoginPartial.cshtml
_TopNav.cshtml
Error.cshtml
TMZ
Academy
Books.cshtml
Papers.cshtml
Promotions.cshtml
CE
Guides.cshtml
Reports.cshtml
Faculty
Forms.cshtml
Reports.cshtml
Marketing
Emails.cshtml
Brochures.cshtml
Print.cshtml
Press.cshtml
Presentations.cshtml
Graphics.cshtml
OCSRY.cshtml
Resources.cshtml
DesignStandards.cshtml
Videos.cshtml
PromoKits.cshtml
About.cshtml
Conferment.cshtml
CustomerCare.cshtml
News.cshtml
ManualOfOperations.cshtml
Webinars.cshtml
_ViewStart.cshtml
Web.Config
The simplest way would be modify the "default" route in Global.asax.cs
routes.MapRoute("Default",
"{controller}/{action}/{subaction}/{id}",
new {subaction= UrlParameter.Optional, id = UrlParameter.Optional});
This would mean you could use the same url format in other Controllers as well. Your TMZController would look something like:
public class TMZController : Controller
{
public ActionResult About()
{
return View();
}
public ActionResult Marketing(string subaction)
{
return View();
}
}
Because the subaction/id are both optional you can construct 2, 3 or 4-part urls and just pickup the extra parameters in your actions, then handle them as required within the body of the methods.
routes.MapRoute("TMZ",
"TMZ/{action}/{id}",
new {controller = "TMZ", action = "Index", id = UrlParameter.Optional},
null,
new[] {"YourNamespace.Web.Controllers"});
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new {controller = "Home", action = "Index", id = UrlParameter.Optional},
null,
new[] {"YourNamespace.Web.Controllers"});
In this case i assumed that you've a Controller named TMZ and in that controller you've set all other actions including Marketing, Academy, Faculty, CE
But here is two things important to consider
This route should be before the default route "as I put it here"
The Actions in the TMZController should have an string id parameter to handle the request. this id would be passed something like "Resources" for the Marketing action or "Books" for Academy Action.
Hope this was clear.

How to add web API to an existing MVC Hottowel project

I have one Hottowel project created using it's template from Visual Studio. I want to add the Web API feature in that project. I have created a Web Api controller to the controller folder and tries to access like "http://localhost:53397/api/Values" But I get an error saying The resource cannot be found error.
My controller code looks like below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace MvcApplication8.Controllers
{
public class ValuesController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
// POST api/<controller>
public void Post([FromBody]string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
I have the cs file in APP_start folder called BreezeWebApiConfig.cs which contains the logic to map the route like below.
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "api/{controller}/{action}"
);
Let me know If I am missing any configuration setting for Web APi.
Try to decorate your ApiController like bellow :
[BreezeController]
public class NorthwindIBModelController : System.Web.Http.ApiController {
readonly EFContextProvider<NorthwindIBContext> ContextProvider =
new EFContextProvider<NorthwindIBContext>();
[HttpGet]
public String Metadata() {
return ContextProvider.Metadata();
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle) {
return ContextProvider.SaveChanges(saveBundle);
}
[HttpGet]
public IQueryable<Customer> Customers() {
return ContextProvider.Context.Customers;
}
For more information have a look to breeze documentation here.
Its seems like you are making a wrong Url Request. Look at your breeze route configuration for WebApi. You need to Pass like that http://localhost:53397/api/Values/Get as breeze is using Controller action based routing.
Hope this will help.

How do I authenticate a WCF Data Service?

I've created an ADO.Net WCF Data Service hosted in a Azure worker role. I want to pass credentials from a simple console client to the service then validate them using a QueryInterceptor. Unfortunately, the credentials don't seem to be making it over the wire.
The following is a simplified version of the code I'm using, starting with the DataService on the server:
using System;
using System.Data.Services;
using System.Linq.Expressions;
using System.ServiceModel;
using System.Web;
namespace Oslo.Worker
{
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class AdminService : DataService<OsloEntities>
{
public static void InitializeService(
IDataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
}
[QueryInterceptor("Pairs")]
public Expression<Func<Pair, bool>> OnQueryPairs()
{
// This doesn't work!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (HttpContext.Current.User.Identity.Name != "ADMIN")
throw new Exception("Ooops!");
return p => true;
}
}
}
Here's the AdminService I'm using to instantiate the AdminService in my Azure worker role:
using System;
using System.Data.Services;
namespace Oslo.Worker
{
public class AdminHost : DataServiceHost
{
public AdminHost(Uri baseAddress)
: base(typeof(AdminService), new Uri[] { baseAddress })
{
}
}
}
And finally, here's the client code.
using System;
using System.Data.Services.Client;
using System.Net;
using Oslo.Shared;
namespace Oslo.ClientTest
{
public class AdminContext : DataServiceContext
{
public AdminContext(Uri serviceRoot, string userName,
string password) : base(serviceRoot)
{
Credentials = new NetworkCredential(userName, password);
}
public DataServiceQuery<Order> Orders
{
get
{
return base.CreateQuery<Pair>("Orders");
}
}
}
}
I should mention that the code works great with the signal exception that the credentials are not being passed over the wire.
Any help in this regard would be greatly appreciated!
Thanks....
You must throw an exception of type DataServiceException.