Two Controllers on the same Web API application - c#

I am trying to add one more controller to our existing Web API. The controllers are like
public class TDataController : ApiController
{
[HttpGet]
public HttpResponseMessage Getdetails(string ROOM, DateTime DOB_GT, DateTime DOB_LT, string STATUS_TYPE)
{
// Code for the controller
}
}
and this is the controller I am trying to add in the same Application
public class TDataSubDateController : ApiController
{
[HttpGet]
public HttpResponseMessage Getdetails(string ROOM, string STATUS_TYPE, DateTime? SUBDATE_GT = null, DateTime? SUBDATE_LT = null)
{
//Code for the controller
}
}
When I am trying to call the second controller like
http://localhost:33823/api/TDataSubDate?ROOM=xxx&STATUS_TYPE=xxx&SUBDATE_GT=xxxx&SUBDATE_LT=xxxx
But it throws the HTTP 404 Page Not Founderror. Do I have to create a different route in the WebConfig.cs. The RouteConfig.cs currently looks like
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}

You can use attribute routing if you are using web api 2. For more details please visit https://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
Here is an example with single controller and multiple actions
[RoutePrefix("api/tdata")]
public class TDataController : ApiController
{
[HttpGet]
[Route("{ROOM}/preview")]
public IHttpActionResult Getdetails(string ROOM, [FromUri]DateTime DOB_GT, [FromUri]DateTime DOB_LT, [FromUri]string STATUS_TYPE)
{
return Ok(string.Format("Room {0} Preview", ROOM));
}
[HttpGet]
[Route("{ROOM}/details")]
public IHttpActionResult Getdetails(string ROOM, [FromUri]string STATUS_TYPE, [FromUri]DateTime? SUBDATE_GT = null, [FromUri]DateTime? SUBDATE_LT = null)
{
return Ok(string.Format("Room {0} Details", ROOM));
}
}
OR into a separate controller
[RoutePrefix("api/tdatasubdate")]
public class TDataSubDateController : ApiController
{
[HttpGet]
public IHttpActionResult Getdetails([FromUri]string ROOM, [FromUri]string STATUS_TYPE, [FromUri]DateTime? SUBDATE_GT = null, [FromUri]DateTime? SUBDATE_LT = null)
{
return Ok(string.Format("Room {0} Details", ROOM));
}
}
And here is how webapiconfig.cs looks like
public static class WebApiConfig
{
/// <summary>
/// configure global routes
/// </summary>
/// <param name="config"></param>
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}

Related

ApiController is not showing actions

I have an API controller where I'll code some POST and GET actions, but I don't know why they're not appearing on Swagger/Help.. I can't use them.
public class ApoliceController : ApiController
{
private GV db = new GV();
[HttpGet]
public IHttpActionResult ListStatus()
{
Ok(db.EthereumStatus.ToList());
}
[HttpGet]
public IHttpActionResult ListApolices()
{
Ok(db.ApoliceEthereum.ToList());
}
}
And my route wasn't customized:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Can someone help with that?
Regards,
These methods routes to the same endpoint url api/apolice which makes no sense
Instead use different routes
[RoutePrefix("api/apolice")]
public class ApoliceController : ApiController
{
private GV db = new GV();
[HttpGet]
[Route("statuses")] //route will be api/apolice/status
public void ListStatus()
{
Ok(db.EthereumStatus.ToList());
}
[HttpGet]
[Route("")] //route will be api/apolice
public void ListApolices()
{
Ok(db.ApoliceEthereum.ToList());
}
}

How to pass multiple params to WebAPI?

I have the following URL:
http://localhost/api/values/100/some+string+here
In the WebAPI app ValuesController, I have this:
[HttpGet]
[Route("api/values/{p1}/{p2}")]
public HttpResponseMessage Get (string p1, string p2) {
...
}
For the caller, it never hits the web api. Instead, it comes back with a 404.
Any idea what is wrong?
You are using Attribute Routing in ASP.NET Web API 2. Make sure you configure your web api to use Attribute routing with MapHttpAttributeRoutes.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Attribute routing.
config.MapHttpAttributeRoutes();
// Convention-based routing.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Next make sure you defined your controller properly
public class ValuesController : ApiController {
//GET api/values/100/some-string-here
[HttpGet]
[Route("api/values/{p1}/{p2}")]
public HttpResponseMessage Get (string p1, string p2) {
...
}
}
You could even use RoutePrefix
[RoutePrefix("api/values")]
public class ValuesController : ApiController {
//GET api/values/100/some-string-here
[HttpGet]
[Route("{p1}/{p2}")]
public HttpResponseMessage Get (string p1, string p2) {
...
}
}
Also if like in your example you want the first parameter to be an integer. then you can use a route constraint and update method.
[RoutePrefix("api/values")]
public class ValuesController : ApiController {
//GET api/values/100/some-string-here
[HttpGet]
[Route("{p1:int}/{p2}")]
public HttpResponseMessage Get (int p1, string p2) {
...
}
}
UPDATE:
Created an integration test for the Values Controller and was able to confirm that the action was called
[TestMethod]
public async Task HttpClient_Should_Get_OKStatus_From_Action_With_Multiple_Parameters() {
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
using (var server = new HttpServer(config)) {
var client = new HttpClient(server);
string url = "http://localhost/api/values/100/some+string+here";
using (var response = await client.GetAsync(url)) {
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
}

Routing to different WebAPI controller's action where optional route param exists

I have my MVC 4 Project API Routing configured as follow:
WebApiConfig.cs:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var company = System.Configuration.ConfigurationManager.AppSettings["DbCompany"];
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "MyApp/"+ company +"/{id}",
defaults: new { controller = "main" , id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
}
}
and MainController.cs contains the following methods:
public JToken Get(string id)
{
...
}
public JToken Get()
{
...
}
[HttpPost]
public JToken DoQuery([FromBody] String query)
{
...
}
public void Post([FromBody] JObject JsonObject)
{
...
}
What I would like to achieve is for any route that is not :
route: /MyApp/MyComp/DoQuery
method: POST
ContextType: text/plain
Returns: JToken
To use normal Get/Post of the main controller
Otherwise use DoQuery in the main controller.
Seems like all you are missing is the special case route to map to DoQuery.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var company = System.Configuration.ConfigurationManager.AppSettings["DbCompany"];
config.Routes.MapHttpRoute(
name: "DoQuery",
routeTemplate: "MyApp/"+ company +"/DoQuery",
defaults: new { controller = "main", action = "DoQuery" }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "MyApp/"+ company +"/{id}",
defaults: new { controller = "main" , id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
}
}

I can't reach route api/v2/ExternalCodes using RouteAttribute

I'm developing an ASP.NET MVC website with Web Api 2 also, C# and .NET Framework 4.5.1.
I'm trying to do versioning but I'm doing something wrong.
I have created a new version of my controller in a new namespace:
namespace MyProject.Web.API.Controllers.v2
{
[AllowAnonymous]
[ActionLogFilter]
public class ExternalCodesController : ApiController
{
public ExternalCodesController()
{
}
[HttpGet]
[Route("api/v2/ExternalCodes")]
public HttpResponseMessage Get()
{
[ ... ]
}
[HttpGet]
[Route("api/v2/ExternalCodes")]
public HttpResponseMessage Get(
byte? codeLevel,
int batchId,
int? lineId,
int productId,
string startingCode,
int? quantity)
{
[ ... ]
}
}
}
But, when I do a GET using this URI: http://myHost:53827/api/v2/ExternalCodes?codeLevel=&batchId=5&lineId=&productId=7&startingCode=&quantity= I get a NotFound HTTP status code. But I have also tested this URI: http://myHost:53827/api/v2/ExternalCodes with the same status code.
What am I doing wrong?
My WebApiConfig.cs is:
namespace MyProject.Web.API
{
/// <summary>
/// Class to config Web API routes and filters.
/// </summary>
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Json configuration
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
// Remove formatting to make json smaller.
json.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.None;
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Filters.
config.Filters.Add(new ExceptionFilter());
config.Filters.Add(new UnhandledExceptionFilter());
}
}
}
This has actually happened to me before. It was due to having two controllers with the same name inside the root 'Controllers' folder, regardless of sub-directories and namespaces (this behavior is different inside areas). Try renaming the controllers to something like:
ExternalCodesV1Controller
and
ExternalCodesV2Controller
You should be able to keep the namespaces and even attribute routes the same.
A nasty solution for your reference. The idea is to change the default controller selector to your custom one.
file 'ValuesController.cs':
namespace WebApplication4.Controllers
{
public class ValuesController : ApiController
{
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
}
file 'ValuesV2Controller.cs':
namespace WebApplication4.Controllers.V2
{
public class ValuesController : ApiController
{
public IEnumerable<string> Get()
{
return new string[] { "value3", "value4" };
}
}
}
create a custom controller selector:
public class MyHttpControllerSelector : DefaultHttpControllerSelector
{
private HttpConfiguration _config;
public MyHttpControllerSelector(HttpConfiguration configuration) : base(configuration)
{
_config = configuration;
}
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
var routeData = request.GetRouteData();
var routeTemplate = routeData.Route.RouteTemplate;
if (routeTemplate.IndexOf("v2/values") != -1)
{
return new HttpControllerDescriptor(
_config, "Values",
typeof(WebApplication4.Controllers.V2.ValuesController));
} else if (routeTemplate.IndexOf("values") != -1)
{
return new HttpControllerDescriptor(
_config, "Values",
typeof(WebApplication4.Controllers.ValuesController));
}
return base.SelectController(request);
}
}
Register the custom controller selector in the file 'WebApiConfig.cs':
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "route1",
routeTemplate: "values"
);
config.Routes.MapHttpRoute(
name: "route2",
routeTemplate: "v2/values");
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Services.Replace(
typeof(IHttpControllerSelector),
new MyHttpControllerSelector(config));
}
}
So when you use url like 'http://localhost:13839/values', you get value1 and value2. And value3 and value4 for url 'http://localhost:13839/v2/values'.

C# WEB (RESTful)API 4.5 Routing the proper/smartest way for Oauth2.0

I am developping a RESTful WEB API using asp.net web API. When i came to implement the Oauth2.0 service i found a small routing problem.
Example:
I have 3 controllers:
public class A_Controller : ApiController
{
public string get()
{
return "call A controller";
}
}
public class B_Controller : ApiController
{
public string get()
{
return "call B controller";
}
}
public class C_Controller : ApiController
{
public string get()
{
return "cal lC controller";
}
}
All off them return some information.
What i want to do is:
For default call:
.../api/{Controller}
Proceed with basic authentication.
For this call:
.../api/oauth/{Controller}
Proceed with my Oauth implementation.
This have to work for all Controllers.
What i tried:
config.Routes.MapHttpRoute(
name: "OauthApi",
routeTemplate: "api/oauth/{Controller}",
defaults: new
{
token_num = RouteParameter.Optional,
action = "Oauth"
}
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}",
defaults: new
{
action = "Default"
}
);
public class A_Controller : ApiController
{
[ActionName("Default")]
public string get()
{
return "call withouth Oauth";
}
[ActionName("OauthAction")]
public string getOauth()
{
//do some Oauth check
return "Oauth with token in header!!!!";
}
Here is what I would do, assuming the user is already authorized.
Create your routes:
config.Routes.MapHttpRoute(
name: "OauthApi",
routeTemplate: "api/oauth/{Controller}/{Action}",
defaults: new
{
action = "Default"
}
);
// or if you have the token in the path..
config.Routes.MapHttpRoute(
name: "OauthApi",
routeTemplate: "api/oauth/{OAuthToken}/{Controller}/{Action}",
defaults: new
{
action = "Default"
}
); // This is really just for URL match, as the Token shouldn't be used
// by the controller or action
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{Controller}/{Action}",
defaults: new
{
action = "Default"
}
);
Controllers:
public class A_Controller : ApiController
{
[ActionName("Default")]
[CustomAuthorize()]
public string get()
{
return "Requested Data";
}
}
Custom Authorization
public CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// do OAuth checking
if (HttpContext.Current.Request.Path
or HttpContext.Current.Request.QueryString["OAuthToken"].Equals())
{
return true;
}
return base.AuthorizeCore(httpContext);
}
}
Now your controllers do only what they are suppose to, return data. And you have a reusable way to authorize all requests.
If you are using MVC 3 or higher, you could just global authorize everything with OAuth.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomAuthorizeAttribute());
}

Categories

Resources