FluentValidation Doesn't Work When Using WebApi [Route] Attribute - c#

I successfully implemented FluentValidation in my WebApi project controller that only had one HttpGet method. When I added another HttpGet method, I added route attribute to both methods. i.e. [Route("Method1")] and [Route("Method2")].
Now the ModelState comes back as true regardless of whether I enter any data or not.
Here is my code.
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new ValidateModelStateFilter());
//FluentValidation
FluentValidationModelValidatorProvider.Configure(config);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{action}/{id}",
defaults: new { controller = "Menu", id = RouteParameter.Optional}
);
}
}
ValidateModelStateFilter
public class ValidateModelStateFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
Controller
[HttpGet]
[Route("Method1")]
public IHttpActionResult ReadAllMenusByApplication([FromUri] ReadAllMenusByApplicationInput input)
{
var result = new List<ApplicationMenu>();
...
}
Input Object
using FluentValidation;
using FluentValidation.Attributes;
namespace MenuService.Models
{
[Validator(typeof(ReadAllMenusByApplicationInputValidator))]
public class ReadAllMenusByApplicationInput
{
public ReadAllMenusByApplicationInput() {
this.ApplicationName = string.Empty;
}
/// <summary>
/// The MenuSystem name of the application
/// </summary>
public string ApplicationName { get; set; }
}
public class ReadAllMenusByApplicationInputValidator : AbstractValidator<ReadAllMenusByApplicationInput>
{
public ReadAllMenusByApplicationInputValidator()
{
RuleFor(x => x.ApplicationName).NotEmpty();
}
}
}

Using this article for reference
Custom Validation in ASP.NET Web API with FluentValidation
You seem to have most of what is done in the referenced article.
Check your configuration order.
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
// Web API configuration and services
config.Filters.Add(new ValidateModelStateFilter());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{action}/{id}",
defaults: new { controller = "Menu", id = RouteParameter.Optional}
);
//FluentValidation
FluentValidationModelValidatorProvider.Configure(config);
}
}
FluentValidation automatically inserts its errors into the ModelState. You should include an error message.
public class ReadAllMenusByApplicationInputValidator : AbstractValidator<ReadAllMenusByApplicationInput> {
public ReadAllMenusByApplicationInputValidator() {
RuleFor(x => x.ApplicationName).NotEmpty()
.WithMessage("The Application Name cannot be blank.");
}
}
The article has some content that is outside of the scope of your question. mainly wrapping the responses but everything else should work for you.

Related

resource api not found in mvc application

In existing MVC application I have added api support.
Added api controller but when i hit url it shows error -
The resource cannot be found.
I tried url -
https://localhost:44316/api/getdata
https://localhost:44316/Test/api/getdata
TestController.cs
public class TestController : ApiController
{
[HttpGet]
[Route("api/getdata")]
public IEnumerable<string> GetData()
{
return new string[] { "value1", "value2" };
}
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
RouteConfig.cs
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
If this is an existing MVC application, then you need to register your web api routes. You need to add this line of code to Global.asax.cs to register your web api routes in Application_Start method:
GlobalConfiguration.Configure(WebApiConfig.Register);
Once you do that, your URLs should then work.
The Route attribute is in wrong place, it should decorate the Controller class.
Try this:
[Route("api/[controller]")]
public class TestController : ApiController
{
[HttpGet]
public IEnumerable<string> GetData()
{
return new string[] { "value1", "value2" };
}
}
and call Get http://localhost:XXXX/api/test.

Most basic controller doesn't work

I'm breaking my head over a problem that I don't understand. I tried creating an API controller to do some stuff, but nothing worked , so I went back step by step to a point that my controller is the same as in a tutorial I'm following:
public class CityController : Controller
{
public CityController()
{
}
[HttpGet("city")]
public JsonResult Get()
{
return new JsonResult(new List<object>()
{
new { id = 1, Name ="asd"},
new { id = 2, Name ="dsa"}
});
}
In my startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseMvc(config =>
{
config.MapRoute(
name: "Default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "City", action = "Index" }
);
}
);
}
It still doesn't return anything to me via Postman ...... I can't understand why?!
What am I doing wrong?
It is my understanding that In postman you will need to set the appropriate content type in your headers such as:
Content-Type:application/json
The URL would then be something like:-
http://xxx:999/City
UPDATE
This works for me.
URL
http://localhost:57909/api/values
Controller
[Route("api/[controller]")]
public class ValuesController : Controller
{
[HttpGet]
public JsonResult Get()
{
return new JsonResult(new List<object>()
{
new { id = 1, Name ="asd"},
new { id = 2, Name ="dsa"}
});
}
}
[HttpPost] //whichever you prefer, I am fond of HttpPost for a couple of reasons so I'd recommend using that.
public IHttpActionResult City() //add string city or any input class variable if you're taking any inputs
{
return Ok(new List<object>()
{
new { id = 1, Name ="asd"},
new { id = 2, Name ="dsa"}
}); //This will return a JSON serialized result
}
WebApiConfig.cs may look like this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.Formatting = Newtonsoft.Json.Formatting.Indented;
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}",
defaults: new { id = RouteParameter.Optional }
);
}
}
If you go for HttpPost then be sure to configure Postman accordingly.

Cannot see my controllers actions in the Swagger UI

I am trying to setup my web api project to use Swagger.
I installed Swashbuckle and while the Swagger UI works when I go to http://localhost:55010/swagger, I see none of my controllers actions.
I am using this kind of path for my actions: http://localhost:55010/api/v1/Role
I currently have only one version of my api, but I am planning to have more than one so I am using v1 in my URL paths (it is set up by using subfolders in my Controllers folder).
Here is what I see when I go to http://localhost:55010/swagger/docs/v1:
{"swagger":"2.0","info":{"version":"v1","title":"Swashbuckle Dummy API V1"},"host":"localhost:55010","schemes":["http"],"paths":{},"definitions":{}}
This is the configuration that I am using:
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.MultipleApiVersions(
(apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
(vc) =>
{
//vc.Version("v2", "Swashbuckle Dummy API V2");
vc.Version("v1", "Swashbuckle Dummy API V1");
});
})
.EnableSwaggerUi(c =>
{
});
}
private static bool ResolveVersionSupportByRouteConstraint(ApiDescription apiDesc, string targetApiVersion)
{
// I don't know how I am supposed to use this
return true;
}
}
My Route config :
config.Routes.MapHttpRoute(
name: "WithActionApi",
routeTemplate: "api/{folder}/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { action = #"[A-Za-z]+" }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{folder}/{controller}/{id}",
defaults: new { action = "DefaultAction", id = RouteParameter.Optional }
);
and one example of a controller :
public class GridTemplateController : BaseController
{
GridTemplateLogic logic;
public GridTemplateController(IPermissionValidator permissionValidator, IRolePermissionLogic logicRolePermission)
: base(permissionValidator, logicRolePermission)
{
logic = new GridTemplateLogic(new GridTemplateRepository(ConnectionString, CurrentUser), permissionValidator);
}
// GET: api/v1/GridTemplate/ForGrid/5
[HttpGet]
public IHttpActionResult ForGrid(int id)
{
try
{
var entityList = logic.GetAllByGridId(id);
return Ok(new ApiResponse(entityList));
}
catch (UnauthorizedAccessException)
{
return Unauthorized();
}
}
...........
Change swagger configuration to this:
public class SwaggerConfig
{
public static void Register(HttpConfiguration config)
...
}
And configure it after all the other configurations:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
...
SwaggerConfig.Register(config);
}
I removed the [AcceptVerbs("xxx")] on my methods and they appeared in my Swagger :-)

Asp.net Web API Attribute Routing 404 Error

I cannot figure out for the life of me why my attribute routing isn't working.
Here is my setup:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Attribute routing
config.MapHttpAttributeRoutes();
// Convention-based routing
config.Routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Here is my controller with my routing attributes:
[Route("api/v1.0/orders")]
public class OrdersV1Controller
{
[APIAuthentication(RequireAuthentication = true)]
[HttpGet]
[Route("{id:int}")]
public GetOrderResponse GetOrder(int id)
{
.....
}
}
Here is my global asax file:
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Populate;
}
}
Here is the URL i'm testing which is returning a 404 not found:
http://localhost:60105/api/v1.0/orders/111111
your controller needs to be an API Controller :
public class OrdersV1Controller : ApiController

ASP.NET Web API returns code 404 with newly created ApiController

I just recently added a new ApiController to my ASP.NET MVC4 project (I already had one ApiController also in the Controllers folder) called ServerInfoController. I created this using the CRUD template option; here's what it looks like:
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using WebFrontend.Models;
namespace WebFrontend.Controllers
{
public class ServerInfoController : ApiController
{
// GET api/serverinfo
public Dictionary<string, string> Get()
{
var settings = GlobalStaticVars.StaticCore.LoadServerSettings("Last", "Server");
if (settings != null)
{
return new Dictionary<string, string>
{
{"ServerIP", settings.Address},
{"ServerPort", settings.Port.ToString()}
};
}
return new Dictionary<string, string>
{
{"ServerIP", ""},
{"ServerPort", ""}
};
}
}
}
However, every time that I attempt to navigate to that resource in the browser, I receive a 404 error. I know that my routes are registered correctly as my other controller is still reachable at the /api endpoint.
The following is the route that is configured in WebApiConfig.cs (it's the default route):
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
You don't need {action} part in the route. This is default routing:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}

Categories