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( ... );
Related
I can't reach any methods from my ApiController in anyway, the routing does appear if i try to reach it by a browser but no methods are shown.
My Controller:
namespace AgroRiego.Controllers
{
public class datacontrol : ApiController
{
[HttpGet, Route("api/get")]
public string Get([FromUri]string user, string pass)
{
string check = SQL.Reader("SELECT * FROM users WHERE username='" + user + "' AND password='" + pass + "'");
if (String.IsNullOrWhiteSpace(check))
{
return "error en credenciales";
}
DataTable horarios = SQL.table_read("SELECT * FROM horario_riego");
string json = Utils.ConvertDataTabletoJSON(horarios);
return json;
}
[HttpPost, Route("api/post")]
public void Post([FromBody]string value)
{
string i = value;
}
}
}
my global asax:
namespace AgroRiego
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
}
and my webapiconfig:
namespace AgroRiego
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// ConfiguraciĆ³n y servicios de API web
// Rutas de API web
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
i have more webforms inside the project (originally it was just html pages with serverside code, but i need to add a couple methods to retrieve and send data, help much appreciated!
EDIT1: i managed to reach HTTP 200 changing the URL but i can't reach the methods anyway (in debug mode it does not stop on the breakpoints) how can i route correctly the Api (so it is not Login.aspx) and how do i fix the methods reaching?
EDIT2: i read in documentation that i need this line in global:
RouteConfig.RegisterRoutes(RouteTable.Routes);
but im not using MVC does that matter? i tried reaching the routes with a brand new MVC Web Api and it yields "No Response"
use a routerprefix with your controller. So you access the URL as
http://localhost/routerprefix/router
HttpClient class can be use to send and receive HTTP requests and responses. Since you are trying to consume a WebApi from a aspx page, better way is to create a HttpClient instance
Below is a very simple implementation. Please check this url for further information
MSDN sample
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://localhost:49342/api/get");
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync();
}
By the look of your set up, it seems correct
you have got:
config.MapHttpAttributeRoutes(); - setup the attribute route
config.Routes.MapHttpRoute( - setup the default route
GlobalConfiguration.Configure(WebApiConfig.Register); - to register at startup
so it should work.
I think the problem you are having is the way you are calling it
WebAPI routing work a little different to MVC
for example:
In you get method, the route is set as below
[HttpGet, Route("api/get")]
so you should call it {host}/api/get using a GET http method
in the screen shot, you are calling using {host}/api/get/Get - which would not have work, because no route would match
Same for your POST method
So give it another try and you should be able to reach it
The URL to add in the rest testing tool is
http://localhost:49342/api/get
Method type is GET
If you are calling this web api from aspx page use the httpClient class.
I have a WebApi project and I got myself in this situation. Let's say that I have a Route :
[Authorize]
[RoutePrefix("api/v1/GG")]
public class StorkUserController : ApiController
{
private IAuthenticationManager Authentication
{
get { return HttpContext.Current.GetOwinContext().Authentication; }
}
[Route("UpdateUser")]
[HttpPost]
So When I start the application, if i directly type in my browser this route :
http://localhost:52494/api/
I will get this Error with some details :
Or if I navigate to http://localhost:52494/api/v1/GG I get :
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:52494/api/v1/GG/'.","MessageDetail":"No type was found that matches the controller named 'v1'."}
How to prevent for this Happen since these routes could be easily to be found, and instead of it, show something like "ERROR 404"! Thanks!
I am using web api in my mvc application. I have method in web api which returns user detail using userId (which is in session["userID"])
public object getUserDetail()
{
//here is need of session["userID"]
// return somthing
}
so what is best way to access this web api method from jquery . Should i access this directly or first i should call my controller method and from there i should call this web api method.
You can directly call WebApi from jquery for performing operations(like insert/update/delete)other than returning JSON for processing back. For the scenarios where you require manipulating your view, call mvc controller which calls the Webapi.
So, for your case, the getUserDetail() method returns data. If these return values needs to be used in your view, then call it from mvc controller
WebApi is already an exposed endpoint for you to access your data from. Going to your controller, and calling the method from there diminishes the intent of having exposed the method as an Api in the first place. Try making a call to the route of the Api method, and you should be fine.
On a side note, try exposing a strongly typed object instead of just returning an object.
what is best way to access this web api method from jquery
Simply make an ajax call.
var url = www.example.com/api/user;
$.ajax({
type: 'GET',
url: url,
success: function(userValue) {
// Do something with your user info...
},
error: function(error) {
// Something went wrong. Handle error.
}
});
And have your controller return the value.
public class UserController : ApiController
{
[HttpGet] // For clarity only
public object Get()
{
// return your object.
return session["userID"];
}
}
And to get your url for the controller, you can use this in your view.
Url.HttpRouteUrl("DefaultApi", new {controller = "UserController "})})
Where DefaultApi is the route name defined in your route table (usually in RouteConfig.cs).
Edit:
Regarding access to session there's a number of ways to get around it. Take a look at this question and I think you will solve it. Accessing Session Using ASP.NET Web API
Or this tutorial:
http://www.codeproject.com/Tips/513522/Providing-session-state-in-ASP-NET-WebAPI
public class SessionableControllerHandler : HttpControllerHandler, IRequiresSessionState
{
public SessionableControllerHandler(RouteData routeData)
: base(routeData)
{}
}
public class SessionStateRouteHandler : IRouteHandler
{
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
{
return new SessionableControllerHandler(requestContext.RouteData);
}
}
And lastly register it with your route:
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
).RouteHandler = new SessionStateRouteHandler();
Or add this to your Global.asax.cs
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
Feels like you might want to step back and rethink the basics. The main question here is: does it sound right that one view layer (MVC) calls another view layer (web api)? And simple answer is: no.
Usual setup is that your ajax calls target your Web Api controller methods directly. But if for whatever reason you find yourself thinking that you really need your MVC to call WebApi then that looks for extracting business logic to separate layer/tier so what you end up with is both, MVC and Web API, calling same method in separate class/layer (whatever your methods actually do).
So, instead of:
//this is in your MVC controller
public ActionResult SomeMVCAction(){
MyWebApiMethod();
}
//This is in your web api controller
public SomeStrongType MyWebApiMethod(){
var sum = 2+2;
}
you might want to have something like:
//this is in your MVC controller
public ActionResult SomeMVCAction(){
DoSum();
}
//This is in your web api controller
public SomeStrongType MyWebApiMethod(){
DoSum()
}
///This function is defined in separate layer/project which is your business layer
public static int DoSum(){
return 2+2;
}
PS.
Regarding session...There is a reason why session is not (easily) accessible in WebApi. REST Api should be stateless so you might want to rethink your design where you need session in web api controller.
You can describe a problem you're trying to solve by accessing session in web api controller and then we can try to give opinion on that.
I am trying to catch 404 errors which are returned by the Asp.net Web API server.
However, Application_Error from inside Global.asax is not catching them.
Is there a way to handle these errors?
You might want to take a look at Handling HTTP 404 Error in ASP.NET Web API which has a step by step example
I know this is old, but I was also just looking for this, and found a very easy way that seems to work, so thought I'd add incase this can help someone else.
The solution I found, that works for me, is here. Also, this can be mixed with attribute routing (which I use).
So, in my (Owin) Startup class I just add something like..
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpConfig = new HttpConfiguration();
//.. other config
app.UseWebApi(httpConfig);
//...
// The I added this to the end as suggested in the linked post
httpConfig.Routes.MapHttpRoute(
name: "ResourceNotFound",
routeTemplate: "{*uri}",
defaults: new { controller = "Default", uri = RouteParameter.Optional });
// ...
}
// Add the controller and any verbs we want to trap
public class DefaultController : ApiController
{
public IHttpActionResult Get(string uri)
{
return this.NotFound();
}
public HttpResponseMessage Post(string uri)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.NotFound, "I am not found");
return response;
}
}
Above you can then return any error object (in this example I am just returning a string "I am not found" for my POST.
I tried the xxyyzz (no named controller prefix) as suggested by #Catalin and this worked as well.
I'm pretty comfortable with how Asp.NET MVC controllers worked when designing services.
However the new WebAPI controllers. how am I supposed to design my services here?
Lets say we have 3 different ways to list e.g. Users.
Get 10 latest , Get all, Get inactive or whatever.
none of these might need parameters. so how would you solve this in WebAPI
IEnumerable<User> Get10Latest()
IEnumerable<User> GetAll()
IEnumerable<User> GetInactive()
That won't work since they have the same param signature.
So what is the correct way to design this here?
You can support multiple methods in one controller for a single HTTP method by using the action parameter.
E.g.
public class UsersController : ApiController
{
[ActionName("All")]
public HttpResponseMessage GetAll()
{
return new HttpResponseMessage();
}
[ActionName("MostIQ")]
public HttpResponseMessage GetMostIQ()
{
return new HttpResponseMessage();
}
[ActionName("TenLatest")]
public HttpResponseMessage GetTenLatest()
{
return new HttpResponseMessage();
}
}
Unfortunately, I have not found a way to get a single controller to handle both with and without the action at the same time.
e.g.
public class UsersController : ApiController
{
[ActionName("")] // Removing this attribute doesn't help
public HttpResponseMessage Get()
{
return new HttpResponseMessage();
}
[ActionName("All")]
public HttpResponseMessage GetAll()
{
return new HttpResponseMessage();
}
[ActionName("MostIQ")]
public HttpResponseMessage GetMostIQ()
{
return new HttpResponseMessage();
}
[ActionName("TenLatest")]
public HttpResponseMessage GetTenLatest()
{
return new HttpResponseMessage();
}
}
Being able to use a single controller for a collection resource and all of its subsets would be nice.
Someone will probably be along and wrap me on the knuckles for this, but you need to configure your routing to handle the Gets. This is how I got it working with the above operations:
config.Routes.MapHttpRoute(
name: "CustomApi",
routeTemplate: "api/{controller}/{action}",
defaults: new { id = RouteParameter.Optional }
);
So now your requests are mapped to the correct controller -> action via the route template. Note that the new route needs to be registered first in WebApiConfig.cs. If you keep the old, default one.
EDIT
Having re-read the question I realize I wasn't quite answering the design question. I would think that one way to go about it, from a REST perspective, would be to use a separate resource to expose the proper collections (Get10Latest for example) since I assume that there is a business reason for exposing that exact subset of data through the service. In that case you'd expose that resource though a single Get in its own Controller (if that is the desired behaviour).
Well why not have urls like this:
GET /users
GET /users/latest
GET /users/inactive
Using routing you could route them to
public classs UserController : ApiController
{
public IEnumerable<User> Get(string mode)
{
// mode is in routing restricted to be either empty, latest, or inactive
}
}
Otherwise use multiple controllers. The use of action names in Web API is kind of a wrong way to about it.