HttpResponseException is not handled in a webapi controller in area - asp.net-web-api2

I have two webapi controllers in my MVC 5 project, one in the main domain:
namespace VincConsultancy.Controllers
{
public class QuestionGroupController : ApiController
{
//...
public IEnumerable<SAQQuestionGroup> Get(int id = 0)
{
var groups = (from g in repository.SAQQuestionGroups
where g.RequirementId == id
select g).ToList();
if (groups.Count == 0)
{
string message = string.Format("Groups with Req id = {0} not found", id);
throw new HttpResponseException(
Request.CreateErrorResponse(HttpStatusCode.NotFound, message)
);
}
return groups;
}
}
}
The other one in the area domain:
namespace VincConsultancy.Areas.admin.Controllers
{
public class SAQGroupsController : ApiController
{
//...
public IEnumerable<SAQGroup> Get(int id = 0)
{
var groups = (from g in this.dbCnxt.SAQQustnGroups
where g.RequirementId == id
select g).ToList();
if (groups.Count == 0)
{
string message = string.Format("Groups with Req id = {0} not found", id);
throw new HttpResponseException(
Request.CreateErrorResponse(HttpStatusCode.NotFound, message)
);
}
return groups;
}
}
}
In both controllers, I return error messages by throwing HttpResponseException. Visual Studio pauses the execution at the line of 'throw new HttpResponseException' only in the controller in the admin area, saying an exception occurs but was not handled by user code, that doesnt affect the functionality though, i just need to press the continue button every time. However it doesnt happen to the other controller. So, I am wondering if there is some mechanism handling the exceptions in main domain automatically. The throw-exception line is not supposed to be caught by my code, how should I get rid of visual studio breaking there?

Related

Upgrade Solution to use FluentValidation Ver 10 Exception Issue

Please I need your help to solve FluentValidation issue. I have an old desktop application which I wrote a few years ago. I used FluentValidation Ver 4 and Now I'm trying to upgrade this application to use .Net framework 4.8 and FluentValidation Ver 10, but unfortunately, I couldn't continue because of an exception that I still cannot fix.
I have this customer class:
class Customer : MyClassBase
{
string _CustomerName = string.Empty;
public string CustomerName
{
get { return _CustomerName; }
set
{
if (_CustomerName == value)
return;
_CustomerName = value;
}
}
class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(obj => obj.CustomerName).NotEmpty().WithMessage("{PropertyName} is Empty");
}
}
protected override IValidator GetValidator()
{
return new CustomerValidator();
}
}
This is my base class:
class MyClassBase
{
public MyClassBase()
{
_Validator = GetValidator();
Validate();
}
protected IValidator _Validator = null;
protected IEnumerable<ValidationFailure> _ValidationErrors = null;
protected virtual IValidator GetValidator()
{
return null;
}
public IEnumerable<ValidationFailure> ValidationErrors
{
get { return _ValidationErrors; }
set { }
}
public void Validate()
{
if (_Validator != null)
{
var context = new ValidationContext<Object>(_Validator);
var results = _Validator.Validate(context); **// <======= Exception is here in this line**
_ValidationErrors = results.Errors;
}
}
public virtual bool IsValid
{
get
{
if (_ValidationErrors != null && _ValidationErrors.Count() > 0)
return false;
else
return true;
}
}
}
When I run the application test I get the below exception:
System.InvalidOperationException HResult=0x80131509 Message=Cannot
validate instances of type 'CustomerValidator'. This validator can
only validate instances of type 'Customer'. Source=FluentValidation
StackTrace: at
FluentValidation.ValidationContext1.GetFromNonGenericContext(IValidationContext context) in C:\Projects\FluentValidation\src\FluentValidation\IValidationContext.cs:line 211 at FluentValidation.AbstractValidator1.FluentValidation.IValidator.Validate(IValidationContext
context)
Please, what is the issue here and How can I fix it?
Thank you
Your overall implementation isn't what I'd consider normal usage however the problem is that you're asking FV to validate the validator instance, rather than the customer instance:
var context = new ValidationContext<Object>(_Validator);
var results = _Validator.Validate(context);
It should start working if you change it to:
var context = new ValidationContext<object>(this);
var results = _Validator.Validate(context);
You're stuck with using the object argument for the validation context unless you introduce a generic argument to the base class, or create it using reflection.

How to return a Json object error on ASP.Net Core Restful Cotroller?

I have a ASP.NET controller that controls a schedule (as I'm Brazilian, schedule in Portuguese means Agendamento).
The thing is, I can't allow scheduling the same room (in Portuguese Sala) being taken twice at the same time.
So in the POST request I check the DB to see if that room has already being taken and if it has I want to return only a Json object { "error": "You can't do that." }.
If the request does not have any problem then the insert should be done and the inserted object has to be returned.
[HttpPost]
public async Task<ActionResult<Agendamento>> PostAgendamento(Agendamento agendamento)
{
var agendamentosJaExistentes = await _context.Agendamentos.Include(ag => ag.Sala)
.Where(ag =>
ag.SalaId == agendamento.SalaId &&
(
(agendamento.PeriodoInicial >= ag.PeriodoInicial && agendamento.PeriodoInicial <= ag.PeriodoFinal)
||
(agendamento.PeriodoFinal >= ag.PeriodoInicial && agendamento.PeriodoFinal <= ag.PeriodoFinal)
))
.ToListAsync();
if (agendamentosJaExistentes != null)
{
return ??? JSON OBJECT ???
}
_context.Agendamentos.Add(agendamento);
await _context.SaveChangesAsync();
return CreatedAtAction("GetAgendamento", new { id = agendamento.Id }, agendamento);
}
Can you guys help me?
Add NewtonsoftJson support
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson();
}
Return JsonObject
if (agendamentosJaExistentes != null)
{
return new ObjectResult(Error("You can't do that.")); //???JSON OBJECT???
}
400 Bad Request response status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error.
public class ReturnJson
{
public string Status { get; set; }
public string Message { get; set; }
}
public static ReturnJson Error(string responseMessage, string responseCode = "400")
{
ReturnJson returnJson = new ReturnJson()
{
Status = responseCode,
Message = responseMessage ?? string.Empty
};
return returnJson;
}
Test Result:
For RESTful api one of the best practice is to return errors aligning with HTTP status code. For this specific case probably HTTP 500 (instead of 200 success with an error body) makes more sense.
You can do so by this:
var result = new
{
Error = "Room already booked",
};
return this.StatusCode(StatusCodes.Status500InternalServerError, result);
This is a simple way that am using (DoteNet Core 3.x)
return Json(new { error = "Your error message " , status = 405 });

Using Custom Entities with OpenIddict

I'm trying to extend the entities used by OpenIddict based on the example provided here:
https://github.com/openiddict/openiddict-core/issues/360#issuecomment-280268525
This includes creating entity classes extending those given by OpenIddict:
public class CustomApplication : OpenIddictApplication<Guid, CustomAuthorization, CustomToken>
{
public string ImpersonateUser { get; set; }
}
public class CustomAuthorization : OpenIddictAuthorization<Guid, CustomApplication, CustomToken>
{
}
public class CustomScope : OpenIddictScope<Guid>
{
}
public class CustomToken : OpenIddictToken<Guid, CustomApplication, CustomAuthorization>
{
}
Updating AddDbContext:
services.AddDbContext<ApplicationContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("ConnectionString"));
options.UseOpenIddict<CustomApplication, CustomAuthorization, CustomScope, CustomToken, Guid>();
});
And a modified version of the call to register the services, to match the newer API:
services.AddOpenIddict()
.AddCore(options =>
{
options.UseEntityFrameworkCore().UseDbContext<ApplicationContext>()
.ReplaceDefaultEntities<CustomApplication, CustomAuthorization, CustomScope, CustomToken, Guid>();
})
Everything appears fine up to this point. Then, as part of application startup, I check for an existing client, and create it if it's not found:
var manager = serviceScope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<CustomApplication>>();
if (await manager.FindByClientIdAsync("Application") == null) // Throws Here
{
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "Application",
ClientSecret = "application-default-secret",
DisplayName = "Application",
RedirectUris = { new Uri("http://localhost:5000/signin-oidc")},
Permissions = { OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.Endpoints.Logout,
OpenIddictConstants.Permissions.Endpoints.Token,
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken,
OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Scopes.Roles}
};
await manager.CreateAsync(descriptor);
}
As noted by the "Throws Here" comment, an exception is thrown which is a SqlException:
fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
Failed executing DbCommand (9ms) [Parameters=[#__identifier='Application' (Size = 100)], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [application].[Id], [application].[ClientId], [application].[ClientSecret], [application].[ConcurrencyToken], [application].[ConsentType], [application].[DisplayName], [application].[ImpersonateUser], [application].[Permissions], [application].[PostLogoutRedirectUris], [application].[Properties], [application].[RedirectUris], [application].[Type]
FROM [OpenIddictApplications] AS [application]
WHERE [application].[ClientId] = #__identifier
System.Data.SqlClient.SqlException (0x80131904): Invalid column name 'ImpersonateUser'.
It appears somewhere along the way, it's trying to query the old tables instead of the newer ones that I'm specifying. The only things on the callstack for OpenIddict between my seed method, and the SQL exception are OpenIddict.Core.OpenIddictApplicationManager`1.FindByClientIdAsync(String identifier, CancellationToken cancellationToken) and OpenIddict.Core.OpenIddictApplicationCache`1.<>c__DisplayClass6_0.<<FindByClientIdAsync>g__ExecuteAsync|0>d.MoveNext()

HttpContext.Current null when making a function Asynchronous in MVC4

I am currently working on MVC4 in VS2010-SP1. I made one of the function in
the controller class Asynchronous. As part of that I made the controller class
derived from AsyncController and added the below two methods ( see code section 1 and
2 below). one method ending with Async(See Code Section 1 ) and another method ending
with Completed ( See Code Section 2 ). The problem is in the model class I am trying
to access my webservice with credentials from HttpContext ( See Code below 3 ). The
context is going null when making an asynchronous call. ie, In the new thread
httpcontext is not available. How to pass the context from main thread to new threads
created.
Code Section 1
public void SendPlotDataNewAsync(string fromDate, string toDate, string item)
{
AsyncManager.OutstandingOperations.Increment();
var highChartModel = new HighChartViewModel();
Task.Factory.StartNew(() =>
{
AsyncManager.Parameters["dataPlot"] =
highChartModel.GetGraphPlotPointsNew(fromDate, toDate, item);
AsyncManager.OutstandingOperations.Decrement();
});
}
Code Section 2
public JsonResult SendPlotDataNewCompleted(Dictionary<string, List<ChartData>>
dataPlot)
{
return Json(new { Data = dataPlot });
}
Code Section 3
public List<MeterReportData> GetMeterDataPointReading(MeterReadingRequestDto
meterPlotData)
{
var client = WcfClient.OpenWebServiceConnection<ReportReadingClient,
IReportReading>(null, (string)HttpContext.Current.Session["WebserviceCredentials"] ??
string.Empty);
try
{
return
ReadReportMapper.MeterReportReadMap(client.GetMeterDataPointReading(meterPlotData));
}
catch (Exception ex)
{
Log.Error("MetaData Exception:{0},{1},{2},{3}",
ex.GetType().ToString(), ex.Message, (ex.InnerException != null) ?
ex.InnerException.Message : String.Empty, " ");
throw;
}
finally
{
WcfClient.CloseWebServiceConnection<ReportReadingClient,
IReportReading> (client);
}
}
HttpContext.Current is null because your task is executed on a pool thread without AspNetSynchronizationContext synchronization context.
Use TaskScheduler.FromCurrentSynchronizationContext():
Task.Factory.StartNew(() =>
{
AsyncManager.Parameters["dataPlot"] =
highChartModel.GetGraphPlotPointsNew(fromDate, toDate, item);
AsyncManager.OutstandingOperations.Decrement();
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());

Struts2 more than one action in one class

I'm using Struts2. I have two web forms that have the same code. I would like to eliminate one form. Here is the structure of my Struts project.
\Web Pages
form.jsp
\WEB-INF
\Content
error.jsp
form.jsp
success.jsp
\Source Packages
\action
MyAction.java
MyAction.java
package action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.*;
public class MyAction extends ActionSupport {
#Action(value = "foo", results = {
#Result(name = "input", location = "form.jsp"),
#Result(name = "success", location = "success.jsp"),
#Result(name = "error", location = "error.jsp")
})
public String execute() throws Exception {
if (user.length() == 1) {
return "success";
} else {
return "error";
}
}
private String user = "";
public void validate() {
if (user.length() == 0) {
addFieldError("user", getText("user required"));
}
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
}
I tried to eliminate form.jsp under \Web Pages by adding a new action method to MyAction.java.
#Action(value="bar", results = {
#Result(name = "success", location = "form.jsp"),
})
public String another() {
return "success";
}
But I got the following error when I go to http : //localhost .../bar.action
HTTP Status 404 - No result defined for action action.MyAction and result input
Your MyAction has an implementation of validate(), which means it is validation aware.
What's happening is that you're calling another, but validate() is kicking in (as it's in the interceptor stack). Validation is failing, and therefore sending to INPUT result, which is not defined in another.
You should
Add #SkipValidation to the another method if you don't want validation there
Add the INPUT result to another() if you want a default input result
On a more general note, when you get that kind of error (No result defined for action X and result input) it usually means you're either having validation errors, parameter population errors (eg: an exception in preparable).