I am trying to set up a C# Web API that uses OData 4. I created an OdataController and believe I routed it correctly in the WebApiConfig. I put a debugger on each function in the controller to see if the request enters the method(s). When I hit GET http://localhost:10013/odata/Call the debugger in the first method hits but once I let the debugger move on the request fails. In Chrome I see the request returns with '406 Not Acceptable' but there is nothing in the preview or response tabs. What am I doing wrong? I can see that the request enters the controller correctly but why does it not return the string "call" as well as send a 406?
Secondly, if I send the request http://localhost:10013/odata/Call(0) the first method in the controller gets hit not the second (desired) or even the third. What am I doing wrong here if I want it to hit the second or third method?
I've included the controller and the WebApiConfig that I am using.
namespace JamesMadison
{
public static class Call
{
}
}
using System.Web.Http.OData;
namespace JamesMadison.Controllers
{
public class CallController : ODataController
{
public string GetCall()
{
return "call";
}
public string GetCall([FromODataUri] int id)
{
return "call";
}
public string GetCall([FromODataUri] string key)
{
return "call";
}
}
}
using System.Web.Http;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
namespace JamesMadison
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapODataServiceRoute("odata", "odata", model: GetModel());
}
public static Microsoft.OData.Edm.IEdmModel GetModel()
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Call>("Call");
return builder.GetEdmModel();
}
}
}
In the controller, I had using System.Web.Http.OData; and replaced it with using System.Web.OData;
Related
I have a controller as the following below:
using CadastroDerivativos.Domain.Interfaces.Services;
using Microsoft.AspNetCore.Mvc;
namespace CadastroDerivativos.WebApi.Controllers
{
[Route("api/equity")]
[ApiController]
public class EquityOptController : ControllerBase
{
private readonly IEquityOptService _equityOptService;
public EquityOptController(IEquityOptService equityOptService)
{
_equityOptService = equityOptService;
}
[HttpGet("{ticker}")]
public bool CheckTicker(string ticker)
{
return _equityOptService.hasTicker(ticker);
}
}
}
I can send a http request with the following url: https://localhost:44353/api/equity/ewz%20us%20-12-2021%20C41.
The problem is that I need to receive in my controller a string like the following: EWZ US 12/21/21 C41 Equity, completely as I passed it would be ideal. So, how can I pass a string like the one in the http request? I'm using the get method, is it the one I should use?
EDIT
When I try to make the request with this url: https://localhost:44353/api/equity/ewz%20us%12/21/21%20C41, I believe that the / of the date are interpreted as separating the url, causing that it is not possible to call the service on the backend.
I am trying to build a basic mini project on .NET Core Web API for basic operations like: GET, POST, PUT, DELETE.
I have the following code in my WeatherForecastController which triggers an AmbiguousMatchException:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Web;
using System.Net.Http;
namespace Webdemo.Controllers
{
[ApiController]
[Route("api/[controller]/[Action]")]
public class WeatherForecastController : ControllerBase
{
static List<string> names = new List<string>()
{
"c","a","b"
};
[HttpGet]
public IEnumerable<string> Get()
{
return names;
}
[HttpGet]
public string Get (int id) {
return names[id];
}
[HttpPost]
public void Post([FromBody]string value)
{
names.Add(value);
}
[HttpPut]
public void Put(int id,[FromBody]string value)
{
names[id] = value;
}
[HttpDelete]
public void Delete(int id)
{
names.RemoveAt(id);
}
}
}
error: AmbiguousMatchException: The request matched multiple endpoints.
[HttpGet]
public IEnumerable<string> Get()
{
return names;
}
[HttpGet]
public string Get (int id) {
return names[id];
}
The issue is related to the above code, try to add a placeholder variable for the unique identifier, change the code as below:
// GET: api/<WeatherForecastController>
[HttpGet]
public IEnumerable<string> Get()
{
return names;
}
// GET: api/<WeatherForecastController>/1
[HttpGet("{id}")] //When this action is invoked, the value of "{id}" in the URL is provided to the method in its id parameter
public string Get (int id) {
return names[id];
}
Edit:
Articles about passing parameters with Asp.net Core API:
Tutorial: Create a web API with ASP.NET Core
Parameter Binding in ASP.NET Web API
Multiple GET And POST Methods In ASP.NET Core Web API
Your Get methods match the same endpoint only with a different set of parameters.
You can work around that by changing the name of one the methods, for example the second Get method could become GetSingle as it seems to fetch a single entry from the names list by its id.
You can do it like:
[HttpGet]
public string GetSingle (int id)
{
return names[id];
}
This is my api configuration class:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}"
);
}
}
This is my api controller class:
public class RoleController : ApiController
{
// Some action that works fine...
// Another action that works fine...
public Result Delete([FromBody]int RoleID)
{
return RoleBL.Delete(RoleID);
}
}
I am calling my actions using POST and they are working fine.
But, when I try to call the Delete action using POST I get the following error:
405 Method Not Allowed
The requested resource does not support http method 'POST'.
Clearly, this is because ApiController enforces REST convention
which expects DELETE verb for Delete action.
Now, how do I disable this REST convention constraints
and write my actions in a classic manner?
You can use the HttpPostAttribute to enforce the Action to accept only POST:
public class RoleController : ApiController
{
[HttpPost]
public Result Delete([FromBody]int RoleID)
{
return RoleBL.Delete(RoleID);
}
}
You may want to keep the REST conventions while allowing certain clients (like HTML forms) to properly use you actions.
So, you can use a combination of HttpPostAttribute and HttpDeleteAttribute or AcceptVerbsAttribute (which allows multiple verbs) to allow multiple verbs:
public class RoleController : ApiController
{
[HttpPost, HttpDelete]
// OR
[AcceptVerbs("DELETE", "POST")
public Result Delete([FromBody]int RoleID)
{
return RoleBL.Delete(RoleID);
}
}
If you don't want magic verbs and magic action names you can use route attributes.
Delete config.Routes.MapHttpRoute and set:
config.MapHttpAttributeRoutes();
Now you have to set the routes manually:
[RoutePrefix("~/Role")]
public class RoleController : ApiController
{
[HttpPost]
[Route("~/Delete")]
public Result Delete([FromBody]int RoleID)
{
return RoleBL.Delete(RoleID);
}
}
In your case I'd stop using any kind of REST conventions.
Instead of having a Delete method on the Role controller you can have a DeleteRole method and allow POST on it. This way nothing will interfere with what you want to do. Nothing forces you to build a REST api if that's not what you need.
There are several things you could do to still build a nice api.
For example, you could return an IHttpActionResult
your method could look like this:
public class RoleController : ApiController
{
[HttpPost]
public IHttpActionResult DeleteRole([FromBody]int RoleID)
{
if ( RoleID <= 0 )
{
return BadRequest();
}
var deleteResult = RoleBL.Delete(RoleID);
return Ok(deleteResult);
}
}
You still return the same object but it's wrapped inside an object with a proper http code so your code which deals with the result, won't change much.
I have a OData web api using ADO.NET Framework in which the controller action is somehow not being reached.
The API correctly receives the HTTP request and parses it to go to the correct action but the action is not reached.
And in return the chrome browser shows the authentication window.
I have been debugging so long but cannot figure out how to solve this.
The controller is (stripped version):
public class DataController : ODataController
{
private readonly DataModel DataAccessModel = new DataModel();
public DataController()
{
.......
}
[HttpGet, EnableQuery]
public IQueryable<Record> GetRecord(ODataQueryOptions<Record> options)
{
try
{
IQueryable<ActivationRequestLog> result;
try
{
result = DataAccessModel.Recordss;
}
catch (Exception ex)
{
......
}
}
}
}
Can you show how the controller has been registered in the WebApiConfig class?
If you're using the ODataConventionModelBuilder, then you have to follow certain naming conventions for controllers of entity sets.
e.g. If I register an Airlines entity set of type Airline
builder.EntitySet<Airline>("Airlines");
....then by default/convention I need to implement
public class AirlinesController : ODataController<Airline>
{
[EnableQuery]
public IQueryable<Airline> Get()
{
DB db = Request.GetContext();
return db.Airlines();
}
}
I have created POST/GET request in MVC before.
In my HomeController
[HttpPost]
public string Index(int Value)
{
return Value.ToString();
}
And setting chrome extension POSTMAN with a form-data
I can call http://localhost/mvcApp/ with a variable 'Value' with value '1' and get a string '1' in return
But when I create a surveyController : ApiController doesn't work when I call http://localhost/mvcApp/api/survey/
public string Post(int Value)
{
return Value.ToString();
}
"Message": "No HTTP resource was found that matches the request URI 'http://localhost/mvcApp/api/survey/'.",
"MessageDetail": "No action was found on the controller 'survey' that matches the request."
I'm not sure if the error is in the way the api is created, or in the way the POSTMAN is trying to call the api. Because that '.'
Also try in my HomeControler Index
client.BaseAddress = new Uri("http://localhost/mvcApp");
var result = client.PostAsync("/api/survey", new
{
Value = 1
}, new JsonMediaTypeFormatter()).Result;
if (result.IsSuccessStatusCode) // here return Not found
The WebApi controllers' conventions are not the same as those of plain ol' MVC controllers.
Basically the problem is that you can't specify the int parameter the way you did.
Try this in you WebApi controller:
// nested helper class
public class PostParams {
public int Value { get; set; }
}
public string Post(PostParams parameters) {
return parameters.Value.ToString();
}
and see how that works.
Here's a thorough article on passing parameters within POST requests to WebAPI controllers:
Passing-multiple-POST-parameters-to-Web-API-Controller-Methods
Long story short, these are the conventions, roughly speaking:
you can't capture POST form name-value pairs in parameters
you can capture them inside the properties of a class if that class is the parameter type of one of your method's parameters
you can capture query parameters in method parameters
EDIT
If you wish to test your WebAPI server using C# you could follow these steps:
Create a nice Console Application (preferably within the same solution)
Add the Web API Client NuGet package to this Console Application
Make your Program.cs do something like this.
The following code uses the C# 5.0 async and await operators.
It also uses the Task class and anonymous types.
I've pointed out official MSDN articles (click on the links) should you be interested in what those things are.
using System.Net.Http;
using System.Threading.Tasks;
namespace ConsoleApplication1 {
class Program {
public static void Main(string[] args) {
Test().Wait();
}
private static async Task Test() {
HttpClient client = new HttpClient();
await client.PostAsJsonAsync(
"http://localhost/mvcApp/api/survey/",
new {
value = 10
}
);
}
}
}
This wasnt easy. After lot of reading I solve it like this.
First the api controler need to define the input parameter with the [FromBody] attribute
// POST api/survey
public void Post([FromBody]string value)
{
}
For testing I put a button in the view and use an Ajax / Post, the variable name need to be an empty string before the variable value.
$(document).ready(
$('#post').click(function () {
var url = 'http://localhost/mvcApi/api/survey';
var data = { "": 'Hola' }; // Define a simple variable this way
$.ajax({
type: "POST",
url: url,
data: data,
success: sucess
}
});
})
Or if you want send mutliple values
data = { "": ["update one", "update two", "update three"] };
But if you want receive an object
public void Post(Survey data)
{
string value = data.Value.ToString();
}
$('#post').click(function () {
....
var data = { value: 25 }
More info here Sending Data and here Binding