Web API Route not working properly in ASP.NET MVC - asp.net-mvc-4

This may be simple and its wierd and I cannot find out the reason why its hapenning.
I am using ASP.NET WEB API 2 Controller: I have a TransactionController, which suddenly stopped working after some modification in one of its action methods. I returned the code back, but its not working anymore.
All the other controllers are working normally. So, I decided two test it by creating two new controllers:
ValueController:
public class ValueController : ApiController
{
public List<string> GetValues()
{
return new List<string>() { "1", "2"};
}
public List<string> GetValues (int ID)
{
return new List<string>() { "1", "2" };
}
public List<string> GetValues(int UserID, DateTime CreateDate)
{
return new List<string>() { "1", "2" };
}
}
TransactionController:
public class TransactionController : ApiController
{
public List<string> GetTransaction(int ID)
{
return new List<string>() { "1", "2" };
}
}
This my webApiConfig
public static void Register(HttpConfiguration config)
{
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
When requesting the item controller by the url:
http://localhost:1607/api/value?ID=1, its hitting the GetValues(int ID) action
and the other actions are working fine.
But, the problem is still with the TransactionController. When requesting it using the url: http://localhost:1607/api/transaction?ID=1. Its not hitting the action.
This the response of the request:
And Its returning back the index view page in the home controller.
Any thoughts why this might happen?

Related

How can I get independent JSON reference resolution between requests in ASP.NET Core?

I am attempting to add a custom IReferenceResolver implementation to an ASP.NET Core 2.2 MVC API application to reduce data in a JSON payload. However the reference resolutions are being shared between different requests.
It appears that a single instance of the ReferenceResolver is shared between requests. I want the references to be resolved independent of other requests, as different users of my won't have this shared reference context.
This is my ConfigureServices method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(opts =>
{
opts.SerializerSettings.ReferenceResolverProvider = () => new ThingReferenceResolver();
});
}
This is my controller implementation along with my custom IReferenceResolver
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet("")]
public ActionResult<ThingsResponse> Get()
{
return new ThingsResponse
{
MainThing = new Thing { Id = "foo" },
Things = new List<Thing>
{
new Thing { Id = "foo" },
new Thing { Id = "bar" }
}
};
}
}
public class ThingsResponse
{
[JsonProperty(IsReference = true)]
public Thing MainThing { get; set; }
[JsonProperty(ItemIsReference = true)]
public List<Thing> Things { get; set; }
}
public class Thing
{
public string Id { get; set; }
}
public class ThingReferenceResolver : IReferenceResolver
{
private readonly IDictionary<string, Thing> _idReference = new Dictionary<string, Thing>();
public void AddReference(object context, string reference, object value)
{
_idReference[reference] = (Thing)value;
}
public string GetReference(object context, object value)
{
var thing = (Thing)value;
_idReference[thing.Id] = thing;
return thing.Id.ToString();
}
public bool IsReferenced(object context, object value)
{
var thing = (Thing)value;
return _idReference.ContainsKey(thing.Id);
}
public object ResolveReference(object context, string reference)
{
_idReference.TryGetValue(reference, out Thing thing);
return thing;
}
}
On my first request I get the following response:
{
"mainThing": {
"$id": "foo",
"id": "foo"
},
"things": [
{
"$ref": "foo"
},
{
"$id": "bar",
"id": "bar"
}
]
}
On my second request I get the following response:
{
"mainThing": {
"$ref": "foo"
},
"things": [
{
"$ref": "foo"
},
{
"$ref": "bar"
}
]
}
I want my second request to look like my first request i.e. repeatable outputs.
You get different results for the second request because MVC creates one serializer and caches it, which then caches references if you have reference tracking on like you do.
I think if you return a JsonResult with new serializer settings in each result then you won't have this problem:
new JsonResult(yourData, new JsonSerializerSettings { ... })
One option I have come up with is to bypass configuring the JSON serializer that MVC provides and create my own for the request in question.
[HttpGet("")]
public ActionResult<ThingsResponse> Get()
{
var serializerSettings = JsonSerializerSettingsProvider.CreateSerializerSettings();
serializerSettings.ReferenceResolverProvider = () => new ThingReferenceResolver();
return new JsonResult(
new ThingsResponse
{
MainThing = new Thing { Id = "foo" },
Things = new List<Thing>
{
new Thing { Id = "foo" },
new Thing { Id = "bar" }
}
},
serializerSettings
);
}
In my specific scenario this is OK, because I do not have many endpoints that this would need to be configured for.
This means the following code from the example Startup.cs is not needed to solve my problem (as I define it per request)
.AddJsonOptions(opts =>
{
opts.SerializerSettings.ReferenceResolverProvider = () => new ThingReferenceResolver();
});
I think I will settle on this option for my circumstances, but would love to know if there are better ways to implement it.

Mvc api dont access my Value Get function

Hello and thanks for taking your time to help me.
I have been using this guide, to try learn about api calls with mvc.
But when I Write Localhost:xxxxx/api/values I get the 404 error, and I cant seem to find out why.
WebApiConfig.cs
namespace APITEST
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
Index2.cshtml
<script src="~/Scripts/jquery-2.1.4.min.js"></script>
<script>
$(document).ready(function () {
$.ajax({
url: "http://localhost:49866/api/Values",
//url: '/API/Value',
type: "Get",
success: function (data) {
for (var i = 0; i < data.length; i++) {
$("<tr><td>" + data[i].Name + "</td></tr>").appendTo("#tbPerson");
}
},
error: function (msg) { console.log(msg) }
});
});
</script>
ValuesController.cs
public class ValuesController : ApiController
{
PersonEntities db = new PersonEntities();
// GET api/values
public IEnumerable<Person> Get()
{
return db.Persons.ToList();
//return new string[] { "value1", "value2" };
}
}
Can someone see why i get the 404 error?
Have you made sure that you Application_Start() in your Global.asax file contains the following code?
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalConfiguration.Configuration.EnsureInitialized();
}

ExceptionFilterAttribute not being invoked

I have the following WebApiConfig.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.EnableSystemDiagnosticsTracing();
// config.MessageHandlers.Add(new TokenValidationHandler());
// config.MessageHandlers.Add(new LoggingHandler());
config.Filters.Add(new ApiCustomExceptionHandler());
}
And I implemented the ExceptionFilterAttribute as follows
public class ApiCustomExceptionHandler : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
var exceptionType = context.Exception.GetType();
if (exceptionType == typeof(UnauthorizedAccessException))
{
context.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
My controller is declared thus:
[ApiCustomExceptionHandler]
public class UserManagementController : ApiController
{
....
}
The problem is OnException never gets invoked, when I throw a UnAuthorizedAccessException. Any idea why this is so?
-Arun
When I had this problem, it turned out actually to be an problem on one action only -- other actions in the controller were fine. The problematic method was returning a generic (in my case,IEnumerable<Dictionary<string, object>>). I fixed this by returning an array instead: Dictionary<string, object>[].
Notice that returning a generic also gives warning CA1006 if you have Static Code Analysis enabled.

ASP.Net MVC 4 WebAPI POST returns 404

I've looked at the many similar issues posted but couldn't find a solution that worked for me. So the call to Get is working fine but call to POST returns 404. I created a simple WebAPI project (MVC 4).
public class CasesController : ApiController
{
[Inject]
public ICaseManager CaseManager { get; set; }
// GET api/cases
public IEnumerable<Case> Get()
{
return CaseManager.ListCases();
}
// POST api/cases
[HttpPost]
public void Post([FromBody]Case objCase)
{
}
}
So when I navigate to http://localhost:34645/api/cases I get the following:
[{"CaseID":1,"CaseCode":"one","CaseDescription":"case one"},{"CaseID":2,"CaseCode":"two","CaseDescription":"case two"}]
I created another project (ASP.Net) and have an html file within it with the following code:
<script src="Scripts/jquery-2.0.3.js"></script>
<script src="Scripts/jquery-2.0.3.intellisense.js"></script>
<script type="text/javascript">
function postData() {
$.post('http://localhost:34645/api/cases', { "CaseID": 3, "CaseCode": "three", "CaseDescription": "case three" }).done(function (data) { alert("Success " + data); }).fail(function (xhr, textStatus, errorThrown) { alert("Error " + xhr.status); });
}
</script>
Every time I click the button that invokes postData, I get an alert "Error 404".
Here are my routes:
Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
WebAPIConfig.Register:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//RA: to get JSON
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
}
}
RouteConfig:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Please advise.
Be careful about the order of the WebApi registration line. I found when I specifically had the Global.asax.cs code in this order it worked:
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Otherwise, it failed with 404 error.
If these are two separate solutions, check they're both running - it's possible that they're trying to share a server instance, so the WebAPI you're trying to hit isn't running when the other app is. If they're projects within the same solution, check that they're both set to run on startup, or again, the WebAPI won't be running when the ASP.NET project tries to access it.
Try below. It works for me. I have removed some properties for brevity.
public class CasesController : ApiController {
// GET api/cases
public IEnumerable<Case> Get() {
var caseManager = new CaseManager();
return caseManager.ListCases();
}
// POST api/cases
[HttpPost]
public string Post([FromBody]Case objCase) {
return objCase.CaseName;
}
}
public interface ICaseManager {
IEnumerable<Case> ListCases();
}
public class CaseManager {
public IEnumerable<Case> ListCases()
{
return new List<Case>() { new Case() { CaseID = 1, CaseName = "one" } };
}
}
public class Case {
public int CaseID { get; set; }
public string CaseName { get; set; }
}
View
<script type="text/javascript">
//function postData() {
// $.post('http://localhost:58820/api/cases', { "CaseID": 3, "CaseCode": "three", "CaseDescription": "case three" })
// .done(function (data) { alert("Success " + data); }).fail(function (xhr, textStatus, errorThrown)
// { alert("Error " + xhr.status); });
//}
$(document).ready(function () {
$('#save-source').click(function (e) {
e.preventDefault();
var source = {
'ID': 0,
'CaseID': 3,
'CaseName': "three",
};
$.ajax({
type: "POST",
dataType: "json",
url: "/api/cases",
data: source,
success: function (data) {
alert(data);
},
error: function (error) {
jsonValue = jQuery.parseJSON(error.responseText);
}
});
});
});
</script>
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm"}))
{
<input type="submit" id="save-source" name="save-source" value="Add" />
}
After different attempts, this article helped me the most:
WebAPI and CORS enabled REST services
I also installed the Ninject WebApi DependencyResolver package through NuGet.
You write that you post to $.post('http://localhost:34645/api/cases'...
Either you change the url to include the action method name explicitly, like: $.post('http://localhost:34645/api/cases/post'..
or you add in your config.Routes.MapHttpRoute a default action which will be used when none action specified in the url
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action="Post", id = RouteParameter.Optional }
);
OR you can change your route to
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
);
(without {action} and then web api will reach the Post method when you use a post http verb (it knows to do it automatically, but if you set a default action it'll override it)

ASP.NET Web API - Multiple POST methods on one controller?

I've been trying to add a second POST method to the default ValuesController class that will take an id parameter and act identical to the PUT method, like so:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
namespace WebCalendar.Controllers {
public class ValuesController : ApiController {
// GET /values
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
// GET /values/5
public string Get(int id) {
return "value";
}
// POST /values
public void Post(string value) {
}
// POST /values/5
public void Post(int id, string value) {
Put(id, value);
}
// PUT /values/5
public void Put(int id, string value){
}
// DELETE /values/5
public void Delete(int id) {
}
}
}
Problem is, when I add this second post method, any time I make a POST request, I get the error:
"No action was found on the controller 'values' that matches the request."
If I comment out one of the methods (doesn't matter which one), POST will work with the other method. I've tried renaming the methods, and even using [HttpPost] on both of them, but nothing has worked.
How can I have more than one POST method in a single ApiController?
EDIT
Here is the only route that I'm using:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { controller = "values", id = RouteParameter.Optional }
);
You have to include the action in your route:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);