ASP.NET WebAPI with MVC - Unity Controller Injection Fails for HTTPPOST - asp.net-mvc-4

I have a simple Login Controller that looks like this:
public class LoginController : Controller
{
private IAccountService _accountService = null;
private IFormsAuthenticationService _formsAuthenticationService = null;
public LoginController() {}
[InjectionConstructor]
public LoginController(IAccountService accountService, IFormsAuthenticationService formsAuthenticationService)
{
_accountService = accountService;
_formsAuthenticationService = formsAuthenticationService;
}
//
// GET: /Login/
public ActionResult Index()
{
return View();
}
//
// POST: /Login/
[HttpPost]
public ActionResult Index(Credentials credentials)
{
try
{
if (_accountService.ValidateUser(credentials) != null)
{
_formsAuthenticationService.SignIn(credentials.UserName, true);
return RedirectToAction("Index", "Home");
}
else
return RedirectToAction("Index", new { failed = true });
}
catch
{
throw;
}
}
}
Locally, everything works fine. When I put the application on my web server (shared hosting, ASP.NET 4.0) though, constructor injection works fine for the GET, but does not fire for the POST.
Essentially for POST, the default constructor is fired instead of the injection constructor. I'm not sure if Unity is even kicking in for the POST verb (how can I test this?).
I am using Boostrapper.cs and as I say, everything works fine on my development PC, so presumably there is an issue at the web server. I might be able to get some server settings changed. Any suggestions?
Cheers -

Bollocks - looks like the problem was due to me trying to load the wrong version of the EntityFramework DLL (v6 instead of 4.1). That wasn't coming up in the stack trace, but when I discovered this and replaced the DLL things started working OK again.

Related

Blazor WASM Http call is not hitting some API endpoints. Receiving index.html instead

I have a Blazor WASM page that need to make a call to get some data from an API. The Blazor app is ASPNetCore hosted, and the hosting app contains the API.
Some of my endpoints work, but some calls throw a Json serialization exception.
Unhandled exception rendering component: '<' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
If I look at the actual response from the server, it looks like it returns the content of index.html from my WASM app.
Example Controller
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class CompanyController : ControllerBase
{
private readonly ApplicationDbContext _context;
public CompanyController(ApplicationDbContext context)
{
_context = context;
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(long id)
{
Company? company = await _context.Companies.FindAsync(id);
if (company == null)
{
return NotFound();
}
return Ok(company);
}
}
Example Blazor Page
#page "/companies/{id:long}"
#attribute [Authorize]
#inject HttpClient Http
#inject NavigationManager Nav
#if (company != null)
{
<div>#company.Name</div>
}
else
{
<div>Loading Company...</div>
}
#code {
private Company? company;
[Parameter]
public long Id { get; set; }
protected override async Task OnInitializedAsync()
{
try
{
company = await Http.GetFromJsonAsync<Company>($"/api/company/{Id}");
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
}
In the example above, everything works as expected. But if I make the following two changes, I'll get the Json Exception mentioned above.
Create an identical controller named WorkOrderController. Everything else is identical including pulling the Company data from the database. Only the name of the controller is different.
Change the Http request to company = await Http.GetFromJsonAsync<Company>($"/api/workOrder/{Id}"); in the Blazor page.
Why would some endpoints work, and some wouldn't?
So, the requestUri passed to GetFromJsonAsync must be lowercase. My request was failing because I had a capital "O" in "workOrder".
I am not sure why this is a requirement of the request parameter, but alas, making the path lowercase fixed the issue.

Using nameof() with Url.Action() and async methods in ASP.NET Core 3.x MVC

Let's say I have a ASP.NET Core 3.0 MVC application, which features a simple controller containing two actions and using attribute based routing:
[Route("home")]
public class HomeController : Controller
{
public static string ControllerName { get; } = "Home";
public HomeController()
{
}
string GenerateUrls()
{
string url1 = Url.Action(nameof(Action1), ControllerName);
string url2 = Url.Action(nameof(Action2Async), ControllerName);
return $"Action1: '{url1}'\nAction2: '{url2}'";
}
[HttpGet("a1")]
public IActionResult Action1()
{
return Ok(GenerateUrls());
}
[HttpGet("a2")]
public async Task<IActionResult> Action2Async()
{
await Task.CompletedTask;
return Ok(GenerateUrls());
}
}
So calling either action should just yield a page showing URLs for both actions.
Opening /home/a1 and /home/a2 correctly calls the respective actions, but the output is kind of unexpected:
Action1: '/home/a1'
Action2: ''
This indicates that Url.Action() returned an empty string for the second action, while it worked perfectly fine for the first action.
After debugging this for quite a while, I found a blog post tracking down this very problem to a breaking change in ASP.NET Core 3.0, where the Async suffix is somehow ignored by Url.Action().
The author fixed this problem by hard-coding strings as action names ("Action1" und "Action2" in my case). He also uploaded some example code reproducing this behavior.
However, I would really prefer to keep the nameof, to avoid later problems with renaming/refactoring.
Is there a clean way to use nameof or other type-safe constructs to supply a method with Async suffix to the Url.Action function?
The described behavior is caused by a breaking change introduced with ASP.NET Core 3.0.
You can go back to the old behaviour by disabling SuppressAsyncSuffixInActionNames:
Gets or sets a value that determines if MVC will remove the suffix "Async" applied to controller action names.
Disable this switch in your AddControllers call while configuring the application services:
services.AddControllers(options => {
options.SuppressAsyncSuffixInActionNames = false;
});
You can find more information about this change in the official announcement and in the docs.
If you change your method name Action2Async to Action2 the problem will be solved.
From the linked blog post:
In ASP.NET Core 3, if you have Action methods suffixed with Async but a route path that does not include Async, refer to them without the Async suffix when resolving a URL through like Url.Action(). This seems to be a breaking change from ASP.NET Core 2.2 which is not officially documented.
public static string ControllerName { get; } = "Home";
string GenerateUrls()
{
string url1 = Url.Action(nameof(Action1), ControllerName);
string url2 = Url.Action(nameof(Action2), ControllerName);
return $"Action1: '{url1}'\nAction2: '{url2}'";
}
[HttpGet("action1")]
public IActionResult Action1()
{
return Ok(GenerateUrls());
}
[HttpGet("action2")]
public async Task<IActionResult> Action2()
{
await Task.CompletedTask;
return Ok(GenerateUrls());
}

HTTP Error 403.14 - Forbidden The Web server is configured to not list the contents of this directory Visual Studio Debugging MVC 4 solution

I am trying to debug a visual studio MVC 4 solution with the following route config content.
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(name: "RegistrationForm", url: "{RecruitmentRegistration}/{Registration}/{id}",
defaults: new { controller = "RecruitmentRegistration", action = "Registration", id = UrlParameter.Optional });
}
But while trying to access the controller Action method, I am facing the below listed error.
HTTP Error 403.14 - Forbidden
The Web server is configured to not list the contents of this directory.
But when I try with the default Index action method it is working fine with the Index view getting loaded.
Below are the Controller Class contents
public class RecruitmentRegistrationController : Controller
{
//
// GET: /RecruitmentRegistration/
public ActionResult Index()
{
return View("Index");
}
public ActionResult Registration()
{
return View("RegistrationForm");
}
}
When I checked for solutions, I could find that most of the solutions are related to IIS related setting changes.But here I have not used IIS site setup and I am directly browsing from the VS2012.
Please advise.

Implementing Elmah in web api project

I am trying to implement ELMAH in a webapi project,since I am new to this elmah technique,I am not able to implement the entire thing.I even tried to follow the sample codes in web but still I am not getting.
can someone please help me to achieve a proper working solution with elmah.
I will be very thankful if a working solution is provided for demo purpose which will be really helpful for me to understand
Here are the steps to send error emails using Elmah
Install Elmah Nuget Package
Update config file to use proper SMTP settings. Here is the example of config file settings
< security allowRemoteAccess="false" />
< errorMail subject="Production Error - {1}: {0}" smtpServer="server address" from="me#you.com" to="you#me.com" />
Create ExceptionLogger class. Here is the example of it
public class ElmahExceptionLogger : ExceptionLogger
{
private const string HttpContextBaseKey = "MS_HttpContext";
public override void Log(ExceptionLoggerContext context)
{
// Retrieve the current HttpContext instance for this request.
HttpContext httpContext = GetHttpContext(context.Request);
// Wrap the exception in an HttpUnhandledException so that ELMAH can capture the original error page.
Exception exceptionToRaise = new HttpUnhandledException(message: null, innerException: context.Exception);
ErrorSignal signal;
if (httpContext == null)
{
signal = ErrorSignal.FromCurrentContext();
// Send the exception to ELMAH (for logging, mailing, filtering, etc.).
signal.Raise(exceptionToRaise);
}
else
{
signal = ErrorSignal.FromContext(httpContext);
signal.Raise(exceptionToRaise);
}
}
private static HttpContext GetHttpContext(HttpRequestMessage request)
{
HttpContextBase contextBase = GetHttpContextBase(request);
if (contextBase == null)
{
return null;
}
return ToHttpContext(contextBase);
}
private static HttpContextBase GetHttpContextBase(HttpRequestMessage request)
{
if (request == null)
{
return null;
}
object value;
if (!request.Properties.TryGetValue(HttpContextBaseKey, out value))
{
return null;
}
return value as HttpContextBase;
}
private static HttpContext ToHttpContext(HttpContextBase contextBase){return contextBase.ApplicationInstance.Context; } }
Register ElmahExceptionLogger class with Web API in startup.cs
config.Services.Add(typeof(IExceptionLogger), new ElmahExceptionLogger());
Even though the answer by #Paresh works, you should use the Elmah.Contrib.WebApi package, since that includes everything needed to use ELMAH with Web API.
I've written a guide to install ELMAH with Web API. Basically you will install the ELMAH and Elmah.Contrib.WebApi packages and then configure it like this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...
config.Services.Add(typeof(IExceptionLogger), new ElmahExceptionLogger());
...
}
}
Regarding the mail configuration, you can validate your web.config using the ELMAH Configuration Validator.

web api odata: $select not working

Trying to get this to work. The query returns all rows even though I am specifying only one (?$select=title). On the webapiconfig I have:
var queryAttribute = new QueryableAttribute()
{
AllowedQueryOptions = AllowedQueryOptions.All
};
config.EnableQuerySupport(queryAttribute);
The controller looks like this:
public override IQueryable<_person_TITLE> Get()
{
return db.personTitle.AsQueryable();
}
protected override _person_TITLE GetEntityByKey(int key)
{
return db.personTitle.FirstOrDefault(p => p.person_TITLE_ID == key);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
EDIT:
After doing some more digging, I found that $select has never been officially implemented. That is why it is not turned on in the AllowedQueryOptions by default.
So, what does that mean??? having this option is critical. Putting odata web api into production would be silly without this option. Coding in your own handlers would defeat the purpose of employing web api in the first place.
We are working on it right now. Support for $select and $expand should show up in the nightly builds very soon.