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 :-)
Related
Im using ASP.NET Core 3.1.5 and I have the following code:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "UserAccessRoutes",
pattern: "UserAccess/GetUsers/{id?}",
defaults: new { controller = "UserAccess", action = "GetUsers" }
).RequireAuthorization(new AuthoriseAttribute("Test"));
endpoints.MapControllerRoute("default", "{controller}/{action}/{id?}");
});
public class UserAccessController : ControllerBase
{
private readonly IUsersService usersService;
public UserAccessController(IUsersService usersService)
{
this.usersService = usersService;
}
[HttpGet]
public async Task<UserServerDto> GetUsers(long serverId)
{
...somecode
}
}
When I call http://localhost:5000/useraccess/getusers?id=10 the GetUsers is called but it skips the custom authorisation attribute. Why it doesn't match the first map route?
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.
In my Swagger API I am getting this message constantly even when it picks up the Api-Key specified in the header,Why is this occuring? any help would be great
Request URL
https://localhost:44338/api/Accounts/CreateRole?api_key=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6ImRhdGFseXR5eCIsInJvbGUiOiJTeXN0ZW1BZG1pbmlzdHJhdG9yIiwiaXNzIjoiRnJhbmtIaXJ0aCIsImF1ZCI6IkNsaWVudEFjY2VzcyIsImV4cCI6MTUyODMyMDI4NX0.w6eYfa4YSyJEwqVovdBUhQJkuHDf1IvG-YZk1rf6SVU
Response Body
{
"message": "Authorization has been denied for this request."
}
Startup.cs
[assembly: OwinStartup(typeof(ProjectScavengerAPI.Web.Startup))]
namespace ProjectScavengerAPI.Web
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
this.ConfigureOAuthTokenConsumption(app);
HttpConfiguration config = new HttpConfiguration();
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false;
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
WebApiConfig.Register(config);
app.UseWebApi(config);
}
}
}
WebApiConfig
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Startup_Auth
public partial class Startup
{
// For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = ConfigurationManager.AppSettings["Issuer"];
var audienceId = ConfigurationManager.AppSettings["AudienceId"];
var clientAudienceId = ConfigurationManager.AppSettings["ClientAudienceId"];
var audienceSecret = ConfigurationManager.AppSettings["AudienceSecret"];
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId, clientAudienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
}
CreateRole Function (SystemAdministrator already exists)
[HttpPost]
[Authorize(Roles = "SystemAdministrator")]
[Route("CreateRole")]
public IHttpActionResult CreateRole(string roleName)
{
return TryAction(() => _CreateRole(roleName));
}
private object _CreateRole(string roleName)
{
try
{
if (!Roles.RoleExists(roleName))
{
Roles.CreateRole(roleName);
}
return $"{roleName} created";
}
catch
{
if (Roles.RoleExists(roleName))
{
return $"{roleName} exists";
}
}
return "";
}
Postman Response working
After spending hours trying to figure out why it wasn't working it turns out I had a spelling error while injecting the javascript file that appends "bearer" to the token so it was never being injected.
I also had to add ApiKeySupport in the SwaggerConfig.EnableSwaggerUI
.EnableSwaggerUi(c =>
{
c.InjectJavaScript(thisAssembly, "ProjectScavengerAPI.Web.Scripts.Swagger.jwt-auth.js");
c.EnableApiKeySupport("Authorization", "header");
});
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.
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.