How to add web API to an existing MVC Hottowel project - asp.net-mvc-4

I have one Hottowel project created using it's template from Visual Studio. I want to add the Web API feature in that project. I have created a Web Api controller to the controller folder and tries to access like "http://localhost:53397/api/Values" But I get an error saying The resource cannot be found error.
My controller code looks like below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace MvcApplication8.Controllers
{
public class ValuesController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
// POST api/<controller>
public void Post([FromBody]string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
I have the cs file in APP_start folder called BreezeWebApiConfig.cs which contains the logic to map the route like below.
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "api/{controller}/{action}"
);
Let me know If I am missing any configuration setting for Web APi.

Try to decorate your ApiController like bellow :
[BreezeController]
public class NorthwindIBModelController : System.Web.Http.ApiController {
readonly EFContextProvider<NorthwindIBContext> ContextProvider =
new EFContextProvider<NorthwindIBContext>();
[HttpGet]
public String Metadata() {
return ContextProvider.Metadata();
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle) {
return ContextProvider.SaveChanges(saveBundle);
}
[HttpGet]
public IQueryable<Customer> Customers() {
return ContextProvider.Context.Customers;
}
For more information have a look to breeze documentation here.

Its seems like you are making a wrong Url Request. Look at your breeze route configuration for WebApi. You need to Pass like that http://localhost:53397/api/Values/Get as breeze is using Controller action based routing.
Hope this will help.

Related

Asp.net Core + Swagger : How to show APIs of type GET

How can I show only APIs of type GET in Swagger page and hide others?
I found that the attribute [ApiExplorerSettings(IgnoreApi = true)]
can hide the API from Swagger page, but I have lot of APIs to hide and I need an approach to hide the APIs depending on its HTTP type.
I've tried this approach :
public class SwaggerFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var nonGetPaths = swaggerDoc.Paths.Where(x => x.Value.Operations.First().Key != OperationType.Get);
var count=nonGetPaths.Count();
foreach (var item in nonGetPaths)
{
swaggerDoc.Paths.Remove(item.Key);
}
}
}
but it didn't work
Write a custom filter like this:
public class SwaggerFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var path in swaggerDoc.Paths)
{
foreach (var key in path.Value.Operations.Keys )
{
if (key != OperationType.Get)
{
swaggerDoc.Paths.Remove(path.Key);
}
}
}
}
}
Then configure in program.cs(.Net 6)
//.......
builder.Services.AddSwaggerGen(x=>x.DocumentFilter<SwaggerFilter>());
//......
I don't add [ApiExplorerSettings(IgnoreApi = true)] in my apicontroller and it works all fine.
But, Make sure Get endpoint and other type of endpoint have different route in the same controller, You can add attribute route like [HttpGet("/get")] on Get endpoint. If you just write like this in the same controller:
[HttpPost]
public IActionResult Post()
{
return Ok();
}
[HttpGet]
public IActionResult Get()
{
return NotFound();
}
Get and Post endpoint will have the same path. swaggerDoc.Paths.Remove(xxx); will remove all of them.
Reuslt:
Before
After

Resolve routes by named parameters

I have ASP Core 2.2 app. I defined controller:
using Microsoft.AspNetCore.Mvc;
namespace Web.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok();
}
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
return Ok();
}
}
}
When I request with url /api/users/3 everything works fine, method GetById is called. But if I try to request /api/users?id=3 method Get is called and I don't know how to fix that. Moreover I would like to create two similar method different only by parameter name. For example public IActionResult GetById(int id) and public IActionResult GetByAge(int age) so I need strict routing by named parameters if possible. I don't want to implement custom middleware to resolve routes myself I wanna try to find ASP feature for that.
The url /api/users/3 : "3" is used as part of the route value .
The url /api/users?id=3: "3" is used as a query string in the url .
Attribute routing with Http[Verb] attributes is the value of which is part of the route value
You could change the Route attribute above the controller to specify action name like below :
[Route("api/[controller]/[action]")]
[ApiController]
public class UsersController : ControllerBase
{
// Get api/users/get
[HttpGet]
public IActionResult Get()
{
return Ok();
}
//Get api/users/GetById/3
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
return Ok();
}
}
Reference :https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-2.2

Usage of HttpContext in dotnet core 2.2

I would like get key from sessions, but the compiler is complaining that the class is static when it is not. Can anyone please help me out?
using Microsoft.AspNetCore.Mvc;
using LitOnline_V1.Models;
using Microsoft.AspNetCore.Http;
namespace Test{
public class GetValidateUer{
public int GetUserValidation(){
var isValidated = HttpContext.Session.GetInt32("isValidated");
return isValidated;
}
}
}
Severity Code Description Project File Line Suppression State
Error CS0120 An object reference is required for the non-static field, method, or property 'HttpContext.Session'
hope it help
public class HomeController : Controller
{
public IActionResult Index()
{
MyMethod(HttpContext);
// Some Code
}
}
public void MyMethod(Microsoft.AspNetCore.Http.HttpContext context)
{
var host = $"{context.Request.Scheme}://{context.Request.Host}";
// Some Code
}

ASP.NET Core Web API Error: Model 1[TContext] violates the Constraint of type 'TContext'

I have a Solution in Visual Studio 2017 that contains the following Projects:
CredentialManager.API (ASP.NET Core 2.1 Web API project)
CredentialManager.Models (Class Library that contains the Domain Model and Data Context Class)
The Domain Model Class is coded as follows:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace CredentialManager.Models.Entities
{
public class Credential
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long CredentialId { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string Application { get; set; }
}
}
The Data Context Class is as follows:
using System;
using System.Collections.Generic;
using System.Text;
using CredentialManager.Models.Entities;
using Microsoft.EntityFrameworkCore;
namespace CredentialManager.Models.Context
{
public class CredentialManagerContext : DbContext
{
public CredentialManagerContext(DbContextOptions options)
: base(options)
{ }
public DbSet<Credential> Credentials { get; set; }
}
}
The appsettings.json file looks like the following:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": {
"i.": null,
"CredentialManagerDB": "server=.\\SQLEXPRESS;database=CredentialManagerDB;Trusted_Connection=true;"
},
"AllowedHosts": "*"
}
The Startup.CS file looks like this:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<CredentialManagerContext>(o => o.UseSqlServer(Configuration["ConnectionStrings:CredentialManagerDB"]));
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
I then build the Solution and Added Migrations. But when I run update-database, I get the following error:
GenericArguments[0], 'CredentialManager.Models.Migrations.CredentialManagerContext', on 'Microsoft.EntityFrameworkCore.Design.IDesignTimeDbContextFactory`1[TContext]' violates the constraint of type 'TContext'.
Can someone here throw some light on this error ? If I include the classes and data context in the same folder as the API project, then everything works.. But I want these classes to be part of a separate Class Library Project. Any help would be much appreciated.
Thanks.
Update context file to have the following:
public CredentialManagerContext(DbContextOptions<CredentialManagerContext> options)
: base(options)
{ }
As outlined in the documentation:
This requires adding a constructor argument to your DbContext type that accepts :
DbContextOptions<TContext>
This should resolve your issue.
Thank you for all the suggestions. I found a Solution as well. The Startup.cs needs to be informed about the Project that contains the Migrations:
services.AddDbContext<CredManagerContext>(options => options.UseSqlServer(Configuration.GetConnectionString("CredentialManagerDB"), x => x.MigrationsAssembly("CredManager.Models")));
Everything works perfectly after this.

structuremap configuration asp.net mvc 4

I have a problem with MVC4 StructureMap configuration, when I run the project the compiler fires this error
No Default Instance defined for PluginFamily Mace_CrmSystem.Controllers.HomeController
this is my code
global.aspx code
namespace Mace_CrmSystem
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteTable.Routes.MapRoute("Oqla", "Oqla", new { controller = "Home", action = "index" });
RouteConfig.RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new MyCustomeFactory());
ObjectFactory.Initialize(x => x.For<string>().Use<string>());
}
}
}
MycustomeFactory class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using StructureMap;
namespace Mace_CrmSystem
{
public class MyCustomeFactory : System.Web.Mvc.DefaultControllerFactory
{
protected override System.Web.Mvc.IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
return StructureMap.ObjectFactory.GetInstance(controllerType) as System.Web.Mvc.IController;
}
}
}
Controller class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Mace_CrmSystem.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public HomeController(string parameter)
{
TempData["Hi"] = "Hi";
}
public ActionResult Index()
{
return View();
}
}
public class logger
{
public void log()
{
}
}
}
what I noticed that when I add a parameter of type object like
public HomeController(logger parameter)
instead of
public HomeController(string parameter)
and
ObjectFactory.Initialize(x => x.For<logger>().Use<logger>());
instead of
ObjectFactory.Initialize(x => x.For<string>().Use<string>());
it works probably but with the string parameter it does not work .
so please couold anyone explain that for me.
From my understanding of StructureMap (and someone please correct me if I'm wrong) the reason you're seeing the behaviour that you're seeing is because StructureMap will use the longest constructor by default and attempt to fill in the parameters with the default instance registered with StructureMap.
In your instance you're not providing a default instance of string so StructureMap doesn't know how to resolve it.
If you wish to do what you're trying to do then your best bet is to look at creating a custom convention (see this answer for more information), however these do rely on knowing the name of the property your constructor is expecting.
Generally though, when dealing with strings your best bet is to move the string to an intermediate type and inject that instead.