web api2 RESTful GET not returning get by id - asp.net-web-api

not quite sure what I am missing
standard webapiconfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace MROVendorWebAPI
{
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 }
);
}
}
}
pretty basic controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using MROVendorWebAPI.Models;
namespace MROVendorWebAPI.Controllers
{
public class VSMRICSController : ApiController
{
public IEnumerable<DB2VsmRicRecord> GetDB2VsmRicRecords()
{
string ErrorDescription = "";
DB2VsmRicCollection myDB2VsmRicCollection = new DB2VsmRicCollection();
if (myDB2VsmRicCollection.CreateCollection(ref ErrorDescription))
{
return myDB2VsmRicCollection.oDB2VsmRicList;
}
return null;
}
public DB2VsmRicRecord GetDB2VsmRicRecord(string ric)
{
string ErrorDescription = "";
DB2VsmRicCollection myDB2VsmRicCollection = new DB2VsmRicCollection();
if (myDB2VsmRicCollection.CreateCollection(ref ErrorDescription))
{
return myDB2VsmRicCollection.oDB2VsmRicList.FirstOrDefault(p => p.Ric == ric);
}
return null;
}
}
}
using /api/VSMRICS works correctly and returns the entire collection
and using /api/VSMRICS?RIC=SAH works correctly and returns just one record however /api/VSMRICS/SAH returns the entire collection. not sure why that is do I need to add some sort of routing to this method in the controller?

duh never mind it needs to be
public DB2VsmRicRecord GetDB2VsmRicRecord(string id)

In conventional routing it search for the RouteParameter which is mentioned in route as id.
Here you can also change the route like below:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{ric}",
defaults: new { id = RouteParameter.Optional }
);
For supporting your API:
public DB2VsmRicRecord GetDB2VsmRicRecord(string ric)
{
string ErrorDescription = "";
DB2VsmRicCollection myDB2VsmRicCollection = new DB2VsmRicCollection();
if (myDB2VsmRicCollection.CreateCollection(ref ErrorDescription))
{
return myDB2VsmRicCollection.oDB2VsmRicList.FirstOrDefault(p => p.Ric == ric);
}
return null;
}

Related

Why MapFallbackToController is not working: HTTP 401 UNAUTHORIZED Asp.net Core

I finished my app and i want to publish it into web.
Working on core 3.1.1 + angular2.
I created Fallback controller for routes.
It seems MapFallbackToController is not working. I don't have acceses to these files because i'm unauthorized, but why?
When i do "dotnet run" my page is blank and in the console:
Everything worked perfect until i moved angular files(wwwroot) into API proj.
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using RecipesApp.API.Controllers.Models.Data;
using RecipesApp.API.Data;
using RecipesApp.API.Helpers;
namespace RecipesApp.API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>( x=> x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllers().AddNewtonsoftJson(opt => {
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
services.AddCors();
services.Configure<CloudinarySettings>(Configuration.GetSection("CloudinarySettings"));
services.AddAutoMapper(typeof(RecipesRepository).Assembly);
services.AddScoped<IAuthRepository, AuthRepository>();
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IRecipesRepository, RecipesRepository>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII
.GetBytes(Configuration
.GetSection("AppSettings:Token").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddScoped<LogUserActivity>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
context.Response.AddApplicationError(error.Error.Message);
await context.Response.WriteAsync(error.Error.Message);
}
});
});
}
// app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapFallbackToController("Index", "Fallback");
});
}
}
}
My fallback class
public class Fallback : Controller
{
public IActionResult Index()
{
return PhysicalFile(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "index.html"), "text/HTML");
}
}
}
Somewhere in your startup file, i think you have specified that before a request gets to a controller, an authorization is needed, hence the error. In your case, can you try putting the AllowAnonymous attribute in your Fallback file as this tells netcore to map back to the fallback controller in search for angular index.html file. So, something like this;
[AllowAnonymous]
public class Fallback : ControllerBase
{
public IActionResult Index()
{
return PhysicalFile(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "index.html"), "text/HTML");
}
}
In your startup.cs file, inside your Configure() method, out the app.UseStaticFiles() after app.UseRouting(). That's all.

I am converting a .Net Framework 4.7.2 WebApi to asp.net Core3.0 and need to use Http.Sys with its listener set to an item in appsettings.json

I have seen a number of solutions for .Net Core 2 that I cannot seem to translate to 3.0. In most cases they do not use the Templated Program and Startup, but are purpose-built.
I use the WebApi template supplied in Visual Studio 2019 Community (latest patches).
In Core 3.0, the Configuration appsettings.json is automatically loaded and via DI in startup, is available to the rest of my code.
I need a couple of the items from appsettings.json, Configuration.GetSection("OrAppSettings")["OrSvrBaseUrl"]
Configuration.GetSection("OrAppSettings")["OrSvrUrlPort"] to be available in order to set the Http.sys Options.UrlPrefixes variable serverName in the Program.cs file.
I use Http.sys, as the Apis will be running in internal servers, as self-hosted, and using Kestrel, would only run at localhost. Therefore they will be unreachable unless Kestrel is run behind IIS. Http.sys can run as a self-hosted service, reachable across the LAN.
The apis etc are part of a larger solution offered to many of our customers and must fit into a variety of scenarios over which we have no real control.
I probably also need the same appsettings items for configuring Swagger, but that will be done in Startup.
I also need to use appsettings.json to provide configuration in the WebAPI client asp.net programs, but I have that covered, since DI from Startup is all I need there.
My appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"OrAppSettings": {
"ClientName": "some customer name",
"AsoAka": "mwp",
"AsoLocation": "shadow-10",
"AsoRouting": "unauthenticated",
"AsoClientType": "3",
"AsoClientUserId": "0",
"AsoClientUserName": "shadow-10\\ingres",
"AsoClientIpAddress": "",
"ORSvrBaseUrl": "http://shadow-10",
"ORSvrUrlPort": "5003",
"EnableSwaggerUI": "true"
},
"AllowedHosts": "*"
}
My Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.HttpSys;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.EventLog;
namespace MwpOrApiCore30
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static string serverName = "http://localhost:5005";
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
// Services
.ConfigureServices(services =>
{
services.Configure<EventLogSettings>(config =>
{
config.LogName = "Mwp Or Api";
config.SourceName = "MwpOrApi Source";
});
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseHttpSys(options =>
{
options.AllowSynchronousIO = true;
options.Authentication.Schemes = AuthenticationSchemes.None;
options.Authentication.AllowAnonymous = true;
options.MaxConnections = null;
options.MaxRequestBodySize = 30000000;
//options.UrlPrefixes.Add("http://shadow-10:5005");
options.UrlPrefixes.Add(serverName);
});
webBuilder.UseStartup<Startup>();
})
//.ConfigureWebHost(config =>
//{
// config.UseKestrel().UseUrls("http://0.0.0.0:5003");
//})
.UseWindowsService();
}
}
My MODIFIED Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.HttpSys;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.EventLog;
namespace MwpOrApiCore30
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static string serverName = "http://localhost:5005";
public static IHostBuilder CreateHostBuilder(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
serverName = configuration.GetSection("OrAppSettings")["ORSvrBaseUrl"];
serverName += ":" + configuration.GetSection("OrAppSettings")["ORSvrUrlPort"];
return Host.CreateDefaultBuilder(args)
.UseWindowsService()
. ConfigureServices(services =>
{
// services.AddHostedService<worker>();
})
// Services
.ConfigureServices(services =>
{
services.Configure<EventLogSettings>(config =>
{
config.LogName = "Mwp Or Api";
config.SourceName = "MwpOrApi Source";
});
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseHttpSys(options =>
{
options.AllowSynchronousIO = true;
options.Authentication.Schemes = AuthenticationSchemes.None;
options.Authentication.AllowAnonymous = true;
options.MaxConnections = null;
options.MaxRequestBodySize = 30000000;
//options.UrlPrefixes.Add("http://shadow-10:5005");
options.UrlPrefixes.Add(serverName);
});
webBuilder.UseStartup<Startup>();
});
}
}
}
My Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
namespace MwpOrApiCore30
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Mwp OR WebApi",
Version = "v1" ,
Description = "An ASP.NET CORE 3.0 Web Api for MWP OpenRoad Server",
Contact = new OpenApiContact
{
Name = "xxxxxx",
Email = "someone#someplace.com.au"
});
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
string enableSwaggerUi = Configuration.GetSection("OrAppSettings").GetValue<string>("EnableSwaggerUI");
if (enableSwaggerUi == "true")
{
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Mwp OR API V1");
});
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

Web API error - Server Error in '/api' Application

After publishing Web API to IIS, which is a child of an AngularJs IIS site, I can reach 'https://localhost/api' and see all endpoints; but when I try to reach some specific endpoint with a GET request, I get an error
Server Error in '/' Application
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
[RoutePrefix("api/branches")]
public class BranchesController : ApiBaseController
{
[HttpGet]
[Route("getBranches")]
public async Task<JsonResult<List<BranchDto>>> GetActiveBranches()
{
var branches = new List<BranchDto>();
var models = await WarehouseUnitOfWork.BranchRepository.GetActiveBranches();
if (models != null && models.Count > 0)
{
branches.AddRange(AutoMapper.Mapper.Map<List<Branch>, List<BranchDto>>(models));
}
return Json(branches, new JsonSerializerSettings
{
ContractResolver = new WarehouseCustomContractResolver()
});
}
}
Any ideas how to solve this?
The solution for my case was to deploy the Frontend into the main IIS site, and inside it create an application called v1 for the Backend.
Then within my angularJS I defined the Production app to make the http requests to /v1/api instead of /api.

Web api call works locally but not on Azure

I have same issue as per following questions and tried answer but never resolve issue
Web api interface works locally but not on Azure
Web API interface works locally but gets 404 after deployed to Azure Website
and many more similar type of...
When i tried to call api it says 404 Not Found
my WebApi.config file
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "versionApi",
routeTemplate: "api/{version}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
API Controller
[Authorize]
[RequiresSSL]
[RoutePrefix("api/v2/Configuration")]
public class ConfigurationAPIv2Controller : ApiController
{
[Dependency]
public IConfigurationServicev2 configurationService { get; set; }
[Dependency]
public IAccountService accountService { get; set; }
#region testapi
[Route("getstring")]
[HttpGet]
public IHttpActionResult getstring()
{
return Ok("Success");
}
[Route("putstring")]
[HttpPut]
public IHttpActionResult putstring()
{
return Ok("Success");
}
#endregion
And Folder Structure is like :
i got follwowing issue for both get and Put method
404 error might caused by route issue. Since you are using route attribute for your Web API. Please make sure GlobalConfiguration.Configure(WebApiConfig.Register); is above other code.
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
And config.MapHttpAttributeRoutes(); code is above other routes configuration.
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "versionApi",
routeTemplate: "api/{version}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
In addition, try to delete following code in your Controller to test whether it is related to the dependent injection module.
[Dependency]
public IConfigurationServicev2 configurationService { get; set; }
[Dependency]
public IAccountService accountService { get; set; }
If it also can't work for you. You could get the detail error message from web server after setting IncludeErrorDetailPolicy property in WebApiConfig class.
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
Visual Studio does not add a default.html page to wwwroot and IIS does.
Just add default.html page to wwwroot in your project, then re-deploy to Azure.

WebApi Controller Get Action Doesn't Work

I'm having trouble with one of my WebApi2 controllers. While writing tests I've discovered the Get() is never hit instead it returns a 301 followed by 403. Oddly if I hit Get(id) the second action speaks up and does its job, but I can never hit the Get() action. If I rename the controller it works properly, but sadly, I cannot rename ModelsController to ModelController as I would like since there is an existing user base expecting that name. This is a rewrite of an existing Api that was done in MVC2 I think. All the other controllers work great, just not this one.
Any thoughts as to how to debug this? Or what I may have missed?
Other notes:
The routing configuration is default.
There is no other ModelsController to be found
Below is a simplified version of my code, and the problem remains...
using System.Collections.Generic;
using System.Web.Http;
using TW.Api.Business.Services.Models;
namespace TW.Api.Business.Services.Controllers
{
public class ModelsController : ApiController
{
public string Get()
{
return null;
}
public string Get(string id)
{
return null;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Routing;
namespace tw.api.business
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "Api_GetWithChild",
routeTemplate: "{controller}/{id}/{action}/{childId}",
defaults: new { id = RouteParameter.Optional, action = "Get", childId = RouteParameter.Optional },
constraints: new { httpMethod = new HttpMethodConstraint("GET") });
config.Routes.MapHttpRoute(
name: "Api_Post",
routeTemplate: "{controller}/{id}/{action}/{childId}",
defaults: new { id = RouteParameter.Optional, action = "Post", childId = RouteParameter.Optional },
constraints: new { httpMethod = new HttpMethodConstraint("POST") });
config.Routes.MapHttpRoute(
name: "Api_Put",
routeTemplate: "{controller}/{id}/{action}/{childId}",
defaults: new { id = RouteParameter.Optional, action = "Put", childId = RouteParameter.Optional },
constraints: new { httpMethod = new HttpMethodConstraint("PUT") });
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// To disable tracing in your application, please comment out or remove the following line of code
// For more information, refer to: http://www.asp.net/web-api
//config.EnableSystemDiagnosticsTracing();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace tw.api.business
{
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 }
);
}
}
}
Looking at your route config, there are a few issues
The constrained routes are not really necessary
The id being optional in a middle of a route pattern doesn't really work, your optional items should come at the end.
When creating methods for web-API end-points, you must follow some naming conventions for automatically mapping of method-to-api. Or you can override that using HTTP action and Route attributes
Follow either of the following arrangements (note the 'Model' and 'Models' in method names):
public class ModelsController : ApiController
{
public string GetModels()
{
return null;
}
public string GetModel(int id)
{
return null;
}
}
Or, use the route attributes:
public class ModelsController : ApiController
{
[HttpGet]
[Route("api/Models")]
public string GetModels()
{
return null;
}
[HttpGet]
[Route("api/Models/{id}")]
public string GetModel(string id)
{
return null;
}
}
Ref: Routing and Action Selection

Resources