Web API Defining routes to controllers asp.net - asp.net

I'm beginner on stack overflow and in ASP.NET in general but I'll try to make my point clear here.
I'm developping a Web API in VB.NET but I'm stuck when I try to define routes.
I have for example these functions :
Public Function GetAllInformations() As IEnumerable(Of cl_information)
'return all informations
End Function
Public Function GetInformations(p_id As Int16) As IHttpActionResult
'return a specific informations
End Function
Public Function PutInformation(p_information As cl_information) As IHttpActionResult
'return the http statuscode depending on the update of the information
End Function
Public Function PostInformation(p_information As cl_information) As IHttpActionResult
'return the http statuscode depending on the post of the information
End Function
When I try this controller, using postman, I firsty check the GET method for the URI : /api/informations. The GetAllInformations() method is correctly triggered.
But when I try the GET method for a specific information item, on this kind of URI : /api/informations/i , the GetAllInformations() is also triggered.
I've got these informations from the event journal in visual studio :
"data": {
"baseType": "RequestData",
"baseData": {
"ver": 2,
"id": "12785441767974844366",
"name": "GET informations [id]",
"startTime": "2016-05-12T08:56:49.4044704+02:00",
"duration": "00:00:04.1740006",
"success": true,
"responseCode": "200",
"url": "http://localhost:51651/api/informations/i",
"httpMethod": "GET",
"properties": {
"DeveloperMode": "true"
}
}
I don't know why the request is not correctly routing to my GetInformations(p_id As Int16) function. Could you help me here please ?
FYI : I have this basic routes configuration :
Public Module WebApiConfig
Public Sub Register(ByVal config As HttpConfiguration)
' Configuration et services API Web
' Itinéraires de l'API Web
config.MapHttpAttributeRoutes()
config.Routes.MapHttpRoute(
name:="DefaultApi",
routeTemplate:="api/{controller}/{id}",
defaults:=New With {.id = RouteParameter.Optional}
)
End Sub
End Module
EDIT :
I tried to implement a method to handle both cases, with an optional argument, but the parameter isn't detected, event if I test the URI : /api/informations/i
Public Function GetInformations(Optional p_id As Int16 = 0) As IHttpActionResult
If p_id = 0 Then
'return all informations
End If
'return a specific information
End Function

after a day and a half on this, my mind is blowing but I finally found the problem.
I was using a wrong parameter name :
Public Function GetInformations(p_id As Int16) As IHttpActionResult
So I changed it by :
Public Function GetInformations(id As Int16) As IHttpActionResult
and it's working.

Related

GET calls to WebAPI

I have a problem with an XML response to a call to the Web API.
Specifically, I have a function "GetValue" call that when I should return in XML format based to the id or class "Cellulare" or class "Televisore".
The problem is that if I make a request from browser gives me the following error:
<Message>An error has occurred.</Message>
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
</ExceptionMessage>
This is the example:
Public Class Cellulare
Public Property Colore As String
Public Property SistemaOperativo As String
End Class
Public Class Televisore
Public Property Colore As String
Public Property Marca As String
End Class
Public Function GetValue(ByVal id As Integer) // ' As Cellulare
If Id = 1 Then
Dim MyTelevisore As New Televisore
MyTelevisore.Colore = "grigio"
MyTelevisore.Marca = "lg"
Return MyTelevisore
Else
Dim MyCellulare As New Cellulare
MyCellulare.Colore = "nero"
MyCellulare.SistemaOperativo = "android"
Return MyCellulare
End If
End Function
Can anyone help me to solve this problem???
Thank in advance
greetings
Donato
I think your approach is wrong.
You have simple objects to return, that can be handled easily by the default serializers webapi has to offer.
Your returned object type should be IHttpActionResult (webapi2) or HttpResponseMessage.
I would NOT go for what #Frank Witte suggested, cause returning the object itself is bad practice. Specifically here you can just return a generic object through IHttpActionResult / HttpResponseMessage.
You should do something like:
Public Function GetValue(ByVal id As Integer) As IHttpActionResult
If Id = 1 Then
Dim MyTelevisore As New Televisore
MyTelevisore.Colore = "grigio"
MyTelevisore.Marca = "lg"
Return Ok(MyTelevisore)
Else
Dim MyCellulare As New Cellulare
MyCellulare.Colore = "nero"
MyCellulare.SistemaOperativo = "android"
Return Ok(MyCellulare)
End If
End Function
It throws the error because you do not supply any return type to your GetValue function. You commented that out.
As I can tell from your code you are returning a different type of object depending on the id you supply to the GetValue call. I do not know the complete context of what you are trying to do, but from what I can see it would make more sense to have a different controller, or route at least, for the different types of object:
/api/cellulare/<id>
Would map to a controller CellulareController.
/api/televisore/<id>
Would map to a controller TelevisoreController. Each with their own Get(), Post() and Delete() methods if you will.
Hope this helps.

Web API routing for non-REST services

I'm designing a webservice which has nothing to do with REST. It backs up a single-page application and currently must implement three simple methods:
public class ImportController : ApiController
{
[HttpPost]
public string[] Parse(string source) { ... }
[HttpPost]
public ConvertResponse Convert(ConvertRequest request) { ... }
[HttpGet]
public object GetHeaders() { ... }
}
It worked pretty well when I was using Controller, except for one thing: I needed to convert all returned JSON data to camelCase. I found a pretty reasonable solution on the web which used CamelCasePropertyNamesContractResolver, but it was only applicable to WebApi controllers since MVC controllers that return JsonResult always use JavascriptSerializer and ignore this configuration.
When I switched the base class to ApiController, the routing broke: GetHeaders works, but other methods return a 404 error!
My route configuration is as follows:
routes.MapHttpRoute(
name: "ImportParse",
routeTemplate: "import/{action}",
defaults: new { controller = "Import" }
);
The successful request (AngularJS):
var baseUrl = 'http://localhost:3821/';
$http.get(baseUrl + 'import/getHeaders').success( ... );
The unsuccessful request:
$http.post(baseUrl + 'import/parse', { source: 'test' }).success( ... );
Error:
message: "No HTTP resource was found that matches the request URI 'http://localhost:3821/import/parse'."
messageDetail: "No action was found on the controller 'Import' that matches the request."
How do I define the correct routing rules for those methods?
Most probably Web Api is looking for a Parse action that supports HttpPost and has object parameter. Because you post object, but not a string, that is why you get 404.
To solve this problem try to send :
$http.post(baseUrl + 'import/parse', 'test').success( ... );

InvalidCastException 'HttpResponseMessage' to 'IHttpActionResult' on WebApi2 GET

Recently I've been following some WebApi2 tutorials. I have a situation whereby if a requested GET operation returns data outside of the user's remit, then I need to return a Forbidden code.
Imports System.Net
Imports System.Net.Http
Imports System.Web.Http
Namespace Controllers
Public Class MyController
Inherits ApiController
<Route("Records/{id}")>
Public Function [Get](id As Int32) As IHttpActionResult
If Not Remit.IsWithinRemit(id) Then
Return Request.CreateErrorResponse(HttpStatusCode.Forbidden, "This data is not within your remit")
Else
Dim r As New CustomObject(id)
Return Ok(r)
End If
End Function
End Class
End Namespace
Unfortunately, although the Ok(r) part works okay, CreateErrorResponse throws an InvalidCastException:
Unable to cast object of type 'System.Net.Http.HttpResponseMessage' to type 'System.Web.Http.IHttpActionResult'.
I know why the error is happening, but am unsure of the correct approach of how to fix it.
In other threads, people advise that CreateErrorResponse() is the best approach for WebApi2, but VS creates it's sample GET request returning IHttpActionResult. Its like stuff doesn't seem to fit together for us newbies at the moment...
No, it isn't obvious, but you can get what you want (error code plus message) AND return it from a method of type IHttpActionResult. No need to change the return type or go without error messages.
This is the helper class:
public class ErrorResult : IHttpActionResult
{
private HttpRequestMessage Request { get; }
private HttpStatusCode statusCode;
private string message;
public ErrorResult(HttpRequestMessage request, HttpStatusCode statusCode, string message)
{
this.Request = request;
this.statusCode = statusCode;
this.message = message;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Request.CreateErrorResponse(statusCode, message));
}
}
and you can call it like this:
public IHttpActionResult MyMethod()
{
MyServiceLayer myServiceLayer = new MyServiceLayer();
MyType myvar;
if (MyServiceLayer.EverythingIsOK(ref myvar))
return Ok(myvar);
else
return new ErrorResult(Request, HttpStatusCode.SomeErrorCode, "Something Is Wrong");
}
try this
Change your Get method to return "HttpResponseMessage"
<Route("Records/{id}")>
Public Function [Get](id As Int32) As HttpResponseMessage
If Not Remit.IsWithinRemit(id) Then
Return Request.CreateResponse(HttpStatusCode.Forbidden, "This data is not within your remit")
Else
Dim r As New CustomObject(id)
Return Request.CreateResponse(HttpStatusCode.OK, r)
End If
End Function
Check below link
http://www.asp.net/web-api/overview/web-api-routing-and-actions/action-results
I found an alternative possible solution (there may be better but this one works and is simple). It returns
403 Forbidden
but with no content:
<Route("Records/{id}")>
Public Function [Get](id As Int32) As IHttpActionResult
If Not Remit.IsWithinRemit(id) Then
Return New Results.StatusCodeResult(HttpStatusCode.Forbidden, Request)
Else
Dim r As New CustomObject(id)
Return Ok(r)
End If
End Function
Because HttpResponseMessage comes from the same namespace, and also allows you to return custom error messages in addition to a HTTP status code, that option is more suitable to use in most cases.
I guess IHttpActionResult is for basic status code returns with no frills. I posted this alongside the above answer to give new coders visibility of both options.
I encountered the same error using .NET Framework 4.7.2. Rather than changing the return type** from IHttpActionResult, I corrected the bug by wrapping myHttpResponseMessage object in a ResponseMessageResult like this: -
return new ResponseMessageResult(myHttpResponseMessage);
** it was a single, custom case amongst a set of straightforward IHttpActionResult cases in a switch block

VB.Net Web Api Action not invoked

I have vb.net web api controller that I am trying to invoke but I'm getting back the following:
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:26944/api/employee/GetPerson/'.","MessageDetail":"No action was found on the controller 'Employee' that matches the request."}
This is the web controller:
Public Class EmployeeController
Inherits ApiController
Private ReadOnly dbContext As MyEntities
Sub New()
Me.dbContext = New MyEntities
End Sub
<HttpGet>
<ActionName("GetPerson")>
Function Person(ByVal missionaryId As Integer) As IPRS_Data.getPersInfoDetail_Result
Return Me.dbContext.getPersInfoDetail(missionaryId).First
End Function
End Class
WebApiConfig:
Public Shared Sub Register(ByVal config As HttpConfiguration)
' Web API configuration and services
' Web API routes
config.MapHttpAttributeRoutes()
config.Routes.MapHttpRoute(
name:="DefaultApi",
routeTemplate:="api/{controller}/{id}",
defaults:=New With {.id = RouteParameter.Optional}
)
Dim xmlFormat = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(Function(t) t.MediaType = "application/xml")
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(xmlFormat)
End Sub
I'm invoking the service using: appbase/api/employee/GetPerson/
Your method is decorated with HTTPGET and Actionname attribute. You don't need that if you have your method name starting with "Get" (like GetPerson). However, the Actionname is obsolete as it is not considered in your actual routing. Your routing is "api/{controller}/{id}". If you want your action name being considered you need to modify your routing to "api/{controller}/{action}/{id}". And if you want to have your id-param being considered per default routing you should rename the param in your method from missionaryId to just id.
Function Person(ByVal id As Integer) As IPRS_Data.getPersInfoDetail_Result
Return Me.dbContext.getPersInfoDetail(id).First
End Function
And that's the way how to invoke it (don't forget to pass an Id because there is no other "GET" method in your controller which works paramless.
appbase/api/employee/15
or
appbase/api/employee?id=15
and if you insist on missionaryId
appbase/api/employee?missionaryId=15

How can I MapHttpRoute a POST to a custom action using the WebApi?

I'm trying to figure out the madness behind the Web API routing.
When I try to post data like this:
curl -v -d "test" http://localhost:8088/services/SendData
I get a 404, and the following error message:
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:8088/services/SendData'.","MessageDetail":"No action was found on the controller 'Test' that matches the request."}
Here is the code for my test server.
public class TestController : ApiController
{
[HttpPost]
public void SendData(string data)
{
Console.WriteLine(data);
}
}
class Program
{
static void Main(string[] args)
{
var config = new HttpSelfHostConfiguration("http://localhost:8088");
config.Routes.MapHttpRoute(
name: "API Default",
routeTemplate:"services/SendData",
defaults: new { controller = "Test", action = "SendData"},
constraints: null);
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
}
}
More generally, why has the ASP.NET team decided to make the MapHttpRoute method so confusing. Why does it take two anonymous objects....how is anyone supposed to know what properties these objects actually need?
MSDN gives no help: http://msdn.microsoft.com/en-us/library/hh835483(v=vs.108).aspx
All the pain of a dynamically typed language without any of the benefit if you ask me...
Agree with you, it's a hell of a madness, you need to specify that the data parameter should be bound from the POST payload, since the Web API automatically assumes that it should be part of the query string (because it is a simple type):
public void SendData([FromBody] string data)
And to make the madness even worse you need to prepend the POST payload with = (yeah, that's not a typo, it's the equal sign):
curl -v -d "=test" http://localhost:8088/services/SendData
You could read more about the madness in this article.
Or stop the madness and try ServiceStack.
Use this signature and it will work every time.
public class TestController : ApiController
{
[HttpPost]
[ActionName("SendData")]
public HttpResponseMessage SendData(HttpRequestMessage request)
{
var data = request.Content.ReadAsStringAsync().Result;
Console.WriteLine(data);
}
}
Try with the following change,
public class TestController : ApiController
{
[HttpPost]
[ActionName("SendData")]
public void SendData(string data)
{
Console.WriteLine(data);
}
}
The ActionName attribute might fix the issue. Otherwise, you can also the name convention "Post"
public void Post(string data)
{
Console.WriteLine(data);
}
And send an Http Post directly to "services" without SendData.

Resources