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.
Related
Having some issues getting started in C#, here's the error I'm getting:
HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
New to this language, any tips appreciated.
Here's my code:
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace Http5112Assignment2.Controllers
{
public class DiceGameController : ApiController
{
[Route("api/J2/DiceGame/{die1}/{die2})")]
public class J2Controller : ApiController
{
[HttpGet]
public int DiceGame(int die1, int die2)
{
int dieSum = die1 + die2;
return dieSum;
}
}
}
}
Kindly do the following.
Move each class to a separate file with the name as the Api controller. In here we can see that you have nested two Api controllers which should not be done.
Api controller route annotations usually looks like [Route("[controller]")] or [Route("api/[controller]")]. Below is an example.
[Route("api/[controller]")]
public class J2Controller : ApiController
{
[HttpGet("DiceGame/{die1}/{die2}")]
public int DiceGame([FromRoute] int die1, [FromRoute] int die2)
{
int dieSum = die1 + die2;
return dieSum;
}
}
If you want to call the DiceGame endpoint, then make sure your Api is running and you can simply do a HTTP GET request to the URL: https://localhost:[yourport]/api/j2/dicegame/69/420.
For more info on routing, visit the following URL.
Create web APIs with ASP.NET Core
I see that there may be a typo in your route string, note the ) in the /{die2})
So, Assuming that you didn't change anything in the default routing in your RouteConfig file, then Make sure you're URL is as follows (note that mine runs on https://localhost:44301 yours may differ)
https://localhost:44301/api/J2/DiceGame/12/34)
Note: It'd be helpful if you provide the request URL you were attempting to use that gave you the error or even better a cURL which is a standard format for that request that gave you the 404
I have a webservice to authentificate my user (from an app made with cordova) :
[RoutePrefix("api/RegisterUser")]
public class UsersController : ApiController
{
[HttpPost]
[Route("Authenticate")]
public async Task<IHttpActionResult> GetUserAuthenticated(string userName, string pwd)
{
//Code here
}
}
with cordova I use this :
let data = {
FK_BaseID:2,
FK_UserTypeID: this.IDs.userType,
Username: regData.email.toLowerCase(),
Password: regData.password,
}
this._http.post('RegisterUser', data);
But I get this error :
"Message":"No HTTP resource was found that matches the request URI '..../RegisterUser/Authentificate'.",
"MessageDetail":"No type was found that matches the controller named 'RegisterUser'."
My service was working fine with HttpGet but I can't manage to make it work with post.
I've tried something like this :
public IHttpActionResult GetUserAuthenticated(string body)
{
return Ok(body);
}
using [FromBody] also, but I'm unable to make it work. I've seen a few examples using Request.Form to navigate through the body of the request and thus the parameters. But my Request hasn't any Form property.
What am I missing ?
I would wrap the "body" param with a class or struct typedef
the way that the routing mechanism works is by matching methods and parameters received to those one who already been registered (see ApiExplorer class for instance in WebApi; this is how the asp App is actually booting itself into a full state web "actions receiver and invoker")
What happens with your code is, in my humble understanding, is that the app is searching for a complex parameter to build the "body" parameter since it is a POST message (hence the attribute name "FromBody") and thus fails to find an entry in the BODY sector of the http message for a complex "body" segment
just wrap the parameter with a class, i.e
public class MyMessage { public string Body {get;set;} }
notice here that this is actually the same as your code since the "MyMessage" identifier is actually not anywhere to be seen in the body of the message, and is a good example to understand since the web app mechanism actually does this kind of "pairing" on its own based on the url you have provided and the compile time signatures you have imposed on the action implementation
I'll do it like this :
public class AuthenticateViewModel {
public string Username {get; set;}
public string Password {get; set;}
}
And in your controller :
[RoutePrefix("api/RegisterUser")]
public class UsersController : ApiController {
[HttpPost]
[Route("Authenticate")]
public IHttpActionResult GetUserAuthenticated([FromBody]AuthenticateViewModel model){
var userName = model.Username;
var pwd = model.Password;
// Code logic here.
return Ok(model);
}
}
The names of your parameters have to match for the binding to execute.
You may want to check your client code also, seems like you're not posting on the right url :
let data = {
FK_BaseID:2,
FK_UserTypeID: this.IDs.userType,
Username: regData.email.toLowerCase(),
Password: regData.password,
}
this._http.post('api/RegisterUser/Authenticate', data);
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;
I have a controller in my web api. Let's call it TimeController.
I have a GET action and a PUT action. They look like this:
public class TimeController : ApiController
{
[HttpGet]
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.OK, DateTime.UtcNow);
}
[HttpPut]
public HttpResponseMessage Put(int id)
{
_service.Update(id);
return Request.CreateResponse(HttpStatusCode.OK);
}
}
I also have a route config as follows:
routes.MapHttpRoute("DefaultApi", "{controller}/{id}", new { id = RouteParameter.Optional });
so I can access it in a restful manner.
Now I also want to version the GET action using a custom Route attribute. I'm using code very similar to what Richard Tasker talks about in this blog post.
(the difference being that I use a regular expression to get the version from the accept header. Everything else is pretty much the same)
So my controller now looks like this:
public class TimeController : ApiController
{
private IService _service;
public TimeController(IService service)
{
_service = service;
}
[HttpGet, RouteVersion("Time", 1)]
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.Ok, DateTime.UtcNow);
}
[HttpGet, RouteVersion("Time", 2)]
public HttpResponseMessage GetV2()
{
return Request.CreateResponse(HttpStatusCode.Ok, DateTime.UtcNow.AddDays(1));
}
[HttpPut]
public HttpResponseMessage Put(int id)
{
_service.Update(id);
return Request.CreateResponse(HttpStatusCode.OK);
}
}
However, now when I try to access the PUT endpoint I'm getting a 404 response from the server. If I step through the code in debug mode, I can see that the RouteVersion attribute is being fired, even though I haven't decorated the action with it.
If I add the attribute to the PUT action with a version of 1, or I add the built in Route attribute like this: Route("Time") then it works.
So my question is: why is the attribute firing even though I haven't decorated the action with it?
Edit: Here is the code for the attribute:
public class RouteVersion : RouteFactoryAttribute
{
private readonly int _allowedVersion;
public RouteVersion(string template, int allowedVersion) : base(template)
{
_allowedVersion = allowedVersion;
}
public override IDictionary<string, object> Constraints
{
get
{
return new HttpRouteValueDictionary
{
{"version", new VersionConstraint(_allowedVersion)}
};
}
}
}
public class VersionConstraint : IHttpRouteConstraint
{
private const int DefaultVersion = 1;
private readonly int _allowedVersion;
public VersionConstraint(int allowedVersion)
{
_allowedVersion = allowedVersion;
}
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
if (routeDirection != HttpRouteDirection.UriResolution)
{
return true;
}
int version = GetVersionFromHeader(request) ?? DefaultVersion;
return (version == _allowedVersion);
}
private int? GetVersionFromHeader(HttpRequestMessage request)
{
System.Net.Http.Headers.HttpHeaderValueCollection<System.Net.Http.Headers.MediaTypeWithQualityHeaderValue> acceptHeader = request.Headers.Accept;
var regularExpression = new Regex(#"application\/vnd\.\.v([0-9]+)",
RegexOptions.IgnoreCase);
foreach (var mime in acceptHeader)
{
Match match = regularExpression.Match(mime.MediaType);
if (match.Success)
{
return Convert.ToInt32(match.Groups[1].Value);
}
}
return null;
}
}
Edit2: I think there is some confusion so I've updated the Put action to match the route config
So my question is: why is the attribute firing even though I haven't decorated the action with it?
It is clear from both the way your question is phrased "when I try to access the PUT endpoint" and the fact that it matches the GET action (and then subsequently runs its constraint) that you have not issued a PUT request to the server. Most browsers are not capable of issuing a PUT request, you need a piece of code or script to do that.
Example
using (var client = new System.Net.WebClient())
{
// The byte array is the data you are posting to the server
client.UploadData(#"http://example.com/time/123", "PUT", new byte[0]);
}
Reference: How to make a HTTP PUT request?
I think its because of your action signature in combination with the default route
In your default route you specify the Id attribute as optional, however in your action you use the parameter days, in this case the framework can't resolve it. you either have to add it as a query string parameter eg:
?days={days}
Or change the signature to accept id as input.
Since it can't resove the action with days in the url it will return a 404
Personally i don't use the default routes and always use Attribute routing to prevent this kinda behavior
So my question is: why is the attribute firing even though I haven't decorated the action with it?
Any controller methods that do not have a route attribute use convention-based routing. That way, you can combine both types of routing in the same project.
Please see this link :
attribute-routing-in-web-api-2
Also as method is not decorated with route attribute, When the Web API framework receives an HTTP request, it tries to match the URI against one of the route templates in the routing table. If no route matches, the client receives a 404 error. That is why you are getting 404
Please see this one as well : Routing in ASP.NET Web API
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