SwaggerUI not working with API versioning - asp.net-web-api

I'm trying to use SwaggerUI, but I'm having some problems.
When I call http://mysite.com/api/swagger I get this:
{
"apiVersion":"4.0.0.0",
"swaggerVersion":"2.0",
"basePath":"http://mysite.com",
"resourcePath":null,
"apis":
[
{
"path":"/api/docs/V1.Foo",
"description":"Foo V1.",
"operations":[]
},
{
"path":"/api/docs/V2.Foo",
"description":"Foo V2.",
"operations":[]
}
]
}
But, when I call http://mysite.com/api/docs/V1.Foo or http://mysite.com/api/docs/V2.Foo I get this:
<Error>
<Message>
The requested resource does not support http method 'GET'.
</Message>
</Error>
It look like I'm calling my API, but I'm trying to get the API documentation.
All my controllers implement System.Web.Http.ApiController.
This is my WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
config.Routes.MapHttpRoute(
name: "SwaggerApi",
routeTemplate: "api/docs/{controller}",
defaults: new { swagger = true }
);
config.Routes.MapHttpRoute(
name: "Swagger",
routeTemplate: "api/swagger",
defaults: new { controller = "swagger" }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{version}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional, version = "v1" }
);
config.Filters.Add(new SwaggerActionFilter());
try
{
config.Services.Replace(typeof(IDocumentationProvider),
new XmlCommentDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/XmlDocument.XML")));
}
catch (FileNotFoundException)
{ }
//My version selector
config.Services.Replace(typeof(IHttpControllerSelector), new VersionControllerSelector(config));
config.Filters.Add(new VersionNoHeaderAttribute());
}
}
And this is my IHttpControllerSelector implementation (VersionControllerSelector):
...
public HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
var routeData = request.GetRouteData();
if (routeData == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
string version;
if (GetRouteVariable<bool>(routeData, "swagger"))
{
version = ""; // Here I have the version and controller name.
}
else if (request.RequestUri.ToString().ToLower().EndsWith("swagger"))
{
version = "net."; // Net.Swagger
}
else
{
version = GetRouteVariable<string>(routeData, VersaoKey);
if (version == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
version = string.Format(CultureInfo.InvariantCulture, "{0}.", version); // V1.MyControler
}
var controllerName = GetRouteVariable<string>(routeData, ControllerKey);
if (controllerName == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var key = version + controllerName;
HttpControllerDescriptor controllerDescriptor;
if (_controllers.Value.TryGetValue(key, out controllerDescriptor))
{
return controllerDescriptor;
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
...
SwaggerActionFilter doesn't get my request when I call http://mysite.com/api/docs/"some-controller".
If I use SwaggerUI in another Solution without versioning, I'm able to get all documentations.
I know that maybe my versioning selector is wrong, but I dont't know what is wrong.

Related

IdentityServer4 Net Core 2 not calling custom iProfileService

I've upgraded my Identity Server project to Net Core 2 and now I am not able to get the iProfileService object to be called to add in custom user claims. It did work in Net Core 1.
Startup.cs ConfigureServices function
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddTransient<IProfileService, M25ProfileService>();
//Load certificate
var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath,
"m25id-cert.pfx"), "mypassword");
services.AddIdentityServer()
.AddSigningCredential(cert)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
//options.EnableTokenCleanup = true;
//options.TokenCleanupInterval = 30;
})
.AddProfileService<M25ProfileService>();
.AddAspNetIdentity<ApplicationUser>();
M25ProfileService.cs
public class M25ProfileService : IProfileService
{
public M25ProfileService(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var user = _userManager.GetUserAsync(context.Subject).Result;
var claims = new List<Claim>
{
new Claim(JwtClaimTypes.GivenName, user.FirstName),
new Claim(JwtClaimTypes.FamilyName, user.LastName),
new Claim(IdentityServerConstants.StandardScopes.Email, user.Email),
new Claim("uid", user.Id),
new Claim(JwtClaimTypes.ZoneInfo, user.TimeZone)
};
if (user.UserType != null)
claims.Add(new Claim("mut", ((int)user.UserType).ToString()));
context.IssuedClaims.AddRange(claims);
return Task.FromResult(0);
}
public Task IsActiveAsync(IsActiveContext context)
{
var user = _userManager.GetUserAsync(context.Subject).Result;
context.IsActive = user != null;
return Task.FromResult(0);
}
}
}
Config.cs
public class Config
{
// try adding claims to id token
public static IEnumerable<IdentityResource> GetIdentityResources()
{
var m25Profile = new IdentityResource(
"m25.profile",
"m25 Profile",
new[]
{
ClaimTypes.Name,
ClaimTypes.Email,
IdentityServerConstants.StandardScopes.OpenId,
JwtClaimTypes.GivenName,
JwtClaimTypes.FamilyName,
IdentityServerConstants.StandardScopes.Email,
"uid",
JwtClaimTypes.ZoneInfo
}
);
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
m25Profile
};
}
public static IEnumerable<ApiResource> GetApiResources()
{
//Try adding claims to access token
return new List<ApiResource>
{
new ApiResource(
"m25api",
"message25 API",
new[]
{
ClaimTypes.Name,
ClaimTypes.Email,
IdentityServerConstants.StandardScopes.OpenId,
JwtClaimTypes.GivenName,
JwtClaimTypes.FamilyName,
IdentityServerConstants.StandardScopes.Email,
"uid",
JwtClaimTypes.ZoneInfo
}
)
};
}
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
new Client
{
ClientId = "client",
ClientName = "Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"m25api"
}
},
// Local Development Client
new Client
{
ClientId = "m25AppDev",
ClientName = "me25",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
RedirectUris = { "http://localhost:4200/authorize.html" },
PostLogoutRedirectUris = { "http://localhost:4200/index.html" },
AllowedCorsOrigins = { "http://localhost:4200" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
JwtClaimTypes.GivenName,
"mut",
"m25api"
},
AllowOfflineAccess = true,
IdentityTokenLifetime = 300,
AccessTokenLifetime = 86400
}
};
}
}
The first thing I'm trying is just to get the identity server to allow me to login and show the user claims similar to the id4 samples. When I login, the standard claims are listed but none of the custom claims. I've put break points in the M25ProfileService class but they never get hit. It seems that ID4 is never using the customer ProfileService class but I do have it in my startup.cs.
I've also tried from my test JS Client and get the same results. Here's a snippet from my JS Client:
var config = {
authority: "http://localhost:5000",
client_id: "m25AppDev",
redirect_uri: "http://localhost:4200/authorize.html",
response_type: "id_token token",
scope:"openid profile m25api",
post_logout_redirect_uri : "http://localhost:4200/index.html"
};
var mgr = new Oidc.UserManager(config);
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
document.getElementById("accessToken").innerHTML = "Bearer " + user.access_token + "\r\n";
}
else {
log("User not logged in");
}
});
function login() {
mgr.signinRedirect();
}
At this point, I'm not sure what to try. I thought if I added the claims to the id token (GetIdentityResources() function from what I understand) and even the access token (GetApiResources() function from what I understand), I'd see the claims but nothing seems to work. Please help! Thanks in advance!
Also, I used to be able to get the custom claims from my client as well as from the Identity Server's own index page that renders after log
Change the order of these lines of code:
.AddProfileService<M25ProfileService>()
.AddAspNetIdentity<ApplicationUser>();
One if overwriting the other.
I figured it out. Thanks to some code on GitHub, I was able to figure out what I was missing. I just needed to add these 2 lines to each client's config in config.cs and all worked perfect!
AlwaysSendClientClaims = true,
AlwaysIncludeUserClaimsInIdToken = true
This works for remote clients. However, I still can't get it to work when I'm on the ID Server itself logging in (not from a client). That's not a big deal for now but could be something in the future. If/When I figure that piece out, I'll try to remember to update my answer. Meanwhile, I hope this helps others.
In addition to the answers above (and beside the fact that the Startup.cs shown in the question already contained the relevant line of code) I'd like to add another, yet very simple cause for why the Profile Service might not be called:
Don't forget to register the service with the dependency injection container!
As having just .AddProfileService<ProfileService>() is not enough.
You would also need:
services.AddScoped<IProfileService, ProfileService>();
Or:
services.AddTransient<IProfileService, ProfileService>();

How to debug JWT Bearer Error "invalid_token"

I'm trying to secure an existing AspNet Core 2.0 / angular 4 app using jwt. I'm using angular2-jwt for the client part and it works just fine. However when it comes to my WebApi, my token is always rejected(using AuthHttp from angular2-jwt to launch my requests or even with postman). The only response I get is 401 Bearer error="invalid_token". I've checked it with the jwt.io chrome extension and it seems just fine(signature, audience, issuer). I can't find anything in the IIS logs either as to why it is deemed invalid. So my question is how can I get more information on what is wrong with the token ?
Any help will be much appreciated.
For reference here's my startup.cs
public class Startup
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
IConfigurationSection jwtConf = this.Configuration.GetSection("jwt");
services.Configure<Controls.JWTConf>(Configuration.GetSection("jwt"));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters =
new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtConf.GetValue<string>("issuer"),
ValidAudience = jwtConf.GetValue<string>("audience"),
IssuerSigningKey = Security.JwtSecurityKey.Create(jwtConf.GetValue<string>("keyBase"))
};
});
services.AddMvc(
config =>
{
var policy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireClaim(ClaimTypes.Name)
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
}
).AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
services.AddNodeServices();
string conn = this.Configuration.GetConnectionString("optimumDB");
services.AddDbContext<TracDbContext>(options =>
options.UseSqlServer(conn));
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "Angular 4.0 Universal & ASP.NET Core advanced starter-kit web API", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, TracDbContext context)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseStaticFiles();
app.UseAuthentication();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true,
HotModuleReplacementEndpoint = "/dist/__webpack_hmr"
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
// Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.MapWhen(x => !x.Request.Path.Value.StartsWith("/swagger", StringComparison.OrdinalIgnoreCase), builder =>
{
builder.UseMvc(routes =>
{
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
});
}
else
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
"Sitemap",
"sitemap.xml",
new { controller = "Home", action = "SitemapXml" });
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
app.UseExceptionHandler("/Home/Error");
}
}
}
My token generating controller
[Route("api/token")]
[AllowAnonymous]
public class TokenController : Controller
{
private IOptions<JWTConf> jwt;
public TokenController(IOptions<JWTConf> jwtConf)
{
this.jwt = jwtConf;
}
[HttpPost]
public IActionResult Create([FromBody]string userCode)
{
Model.Entities.Utilisateur user = new Model.Entities.Utilisateur { ID_UTILISATEUR = 6 };
JwtToken token = new JwtTokenBuilder()
.AddSecurityKey(JwtSecurityKey.Create(this.jwt.Value.keyBase))
.AddSubject("User")
.AddIssuer(this.jwt.Value.issuer)
.AddAudience(this.jwt.Value.audience)
.AddClaim(ClaimTypes.Name,user.ID_UTILISATEUR.ToString())
.AddExpiry(1440)
.Build();
var tok = new { token = token.Value };
//return Ok(token);
return Ok(JsonConvert.SerializeObject(tok));
}
}
And finally the controller that rejects the token :
[Produces("application/json")]
public class JobsController : BaseController
{
public JobsController(IConfiguration config, TracDbContext db) : base(config, db)
{
}
// GET: api/Jobs
[HttpGet]
[Route("api/Jobs")]
public IEnumerable<Departement> Get()
{
return new GroupedJobs(Db.GetJobs().ToList());
}
[HttpGet]
[Route("api/Jobs/{id}")]
public JOB_CLIENT Get(int id)
{
return Db.GetDetailsJob(id);
}
}
Found the problem ... turns out I was storing my token with quotes around it. So The authorization header that was being sent looked like this
Bearer "TOKEN"
instead of
Bearer TOKEN
Being new to the whole thing I tought the quotes were being added by the AuthHtpp and were part of the protocol.

IdentityServer4 on Core 2.0 Getting 401 (Unauthorized) from the API

I'm all of a sudden getting 401 errors from my web API project after trying to migrate to the latest version of IdentityServer4 and .NET Core 2. Over the last week, I've made so many changes, I don't know what's right or wrong anymore.
I have a small 3 project solution:
IDSRV: https://localhost:44300
WEB App: https://localhost:44301
API App: https://localhost:44302
Here is my Code to configure IDSV which is stored in my database:
public class Config
{
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource(){
Name = "api.oc.com",
Description = "OC Api",
Scopes = new[] {new Scope("api.oc.com")}
}
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "oc.com",
ClientName = "OC Website",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
AlwaysIncludeUserClaimsInIdToken = true,
ClientSecrets =
{
new Secret("SomeReallyStrongPassword1!".Sha256())
},
RedirectUris = { "https://localhost:44301/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:44301/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api.oc.com",
"offline_access"
},
AllowOfflineAccess = true
}
};
}
}
In my WEB API Startup.cs. I have this in ConfigureServices. I believe my Configure method is ok.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.Authority = "https://localhost:44300";
o.RequireHttpsMetadata = false;
o.Audience = "api.oc.com";
});
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
This brings me to the WEB Front End trying to authenticate to the WEB API.
I've tried it both with Access Tokens & ID Tokens, but none seem to work.
With Access Token:
var accessToken = await HttpContext.GetTokenAsync("access_token");
var client = new HttpClient();
client.SetBearerToken(accessToken);
var result = await client.GetStringAsync("https://localhost:44302/api/text/welcome");
Or with the client credentials flow:
var disco = await DiscoveryClient.GetAsync("https://localhost:44300");
var tokenClient = new TokenClient(disco.TokenEndpoint, "oc.com", "SomeReallyStrongPassword1!");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync();
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var result = await client.GetStringAsync("https://localhost:44302/api/text/welcome");
I'm sincerely thankful if anyone has any incite on this. It's been so long looking at security code that my eyeballs are going explode! LOL.
Thanks,
Mike Kushner
After some meddling.. And adding a real certificate, this seems to be working after I created some policies instead of just using the [Authorize] attribute only. Note, that I'm also using IdentityServer4.AccessTokenValidation again.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization((options) => {
options.AddPolicy("MustBeValidUser", policybuilder =>
{
policybuilder.RequireAuthenticatedUser();
policybuilder.Requirements = new[] { new MustBeValidUserRequirement() };
});
});
services.AddSingleton<IAuthorizationHandler, MustBeValidUserHandler>();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = "https://localhost:44300";
options.RequireHttpsMetadata = true;
options.ApiName = "api.oc.com";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
app.UseDeveloperExceptionPage();
app.UseAuthentication();
app.UseMvc();
}
}

Multiple actions were found that match the request - WebApi

I have added WebApi to an existing MVC app and create a controller with a post method. I have one route configured in the webapiconfig. When I use Fiddler to post to the controller, I am receiving the "Multiple Actions" error. my ApiConfig and Controller are posted below. There is some Ioc and DI going on with Ninject. Do I need to add different routes definitions, or is this about the data being posted?
webapiconfig.cs
public static void Register(HttpConfiguration config)
{
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Routes.MapHttpRoute(
name: "Materials",
routeTemplate: "api/materials/{id}",
defaults: new { controller = "materials", id = RouteParameter.Optional }
);
}
MaterialController.
using ????.Info.DAL;
using ????.Info.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using IdentitySample.Models;
namespace ????.Info.Controllers.Api
{
public class MaterialsController : BaseApiController
{
public MaterialsController(I????Repository repo)
:base(repo)
{
}
[Route("api/materials/")]
public IEnumerable<MaterialModel> Get()
{
IQueryable<MaterialEntities.Materials> query;
query = TheRepository.GetAllMaterials();
var results = query
.ToList()
.Select(s => TheModelFactory.Create(s));
return results;
}
[Route("api/materials/{id:int}")]
public HttpResponseMessage GetMaterial(int id)
{
try
{
var material = TheRepository.GetMaterial(id);
if (material != null)
{
return Request.CreateResponse(HttpStatusCode.OK, TheModelFactory.Create(material));
}
else
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
[HttpPost]
public HttpResponseMessage Post([FromBody] MaterialModel materialModel)
{
try
{
var entity = TheModelFactory.Parse(materialModel);
if (entity == null) Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Could not read MaterialType/Organization from body");
if (TheRepository.Insert(entity) && TheRepository.SaveAll())
{
return Request.CreateResponse(HttpStatusCode.Created, TheModelFactory.Create(entity));
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Could not save to the database.");
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
public MaterialEntities.Materials Parse(MaterialModel materialmodel)
{
try
{
var material = new MaterialEntities.Materials()
{
Name = materialmodel.Name,
Description = materialmodel.Description,
DateCreated = materialmodel.DateCreated,
};
return material;
}
catch (Exception)
{
return null;
}
}
}
}
Ok, thanks for all the help on this. This was fixed by adding config.MapHttpAttributeRoutes(); to the WebApiConfig. I removed the default route definitions since I am going to use AttributeRouting and it's working now.
update: It looks like it is possible to mix Attribute and Convention routing in the WebApiConfig as discussed here So my problem was solved by adding config.MapHttpAttributeRoutes(); to WebApiConfig

Url.RouteUrl returning empty

I´m trying to get full URL but the RouteUrl is returning empty.
In the View, I´m calling like that:
alert('#Url.RouteUrl("Api", new { controller = "Parametros", id = "" })');
Here is my routes configurations:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "Api",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Usuario",
action = "Login", id = UrlParameter.Optional }
);
}
and my controller:
public class ParametrosController : ApiController
{
ISistemaService _sistemaService;
public ParametrosController(Empresa empresa, ISistemaService sistemaService)
{
_sistemaService = sistemaService;
}
public PagedDataModel<ParametroDTO> Get(
[FromUri]ParametroFiltro filter, int page, int pageSize)
{
int total = 0;
var list = _sistemaService.Paging(filter, page, pageSize, out total);
return new PagedDataModel<ParametroDTO>(page, pageSize, total, list);
}
public ParametroDTO Get(string codigo)
{
return _sistemaService.GetParametroPorCodigo(codigo);
}
}
Add httproute = "" to the routeValues:
alert('#Url.RouteUrl("Api",
new { httproute = "", controller = "Parametros", id = "" })');

Resources