How to handle EntityNotFoundException in Abp vNext? - aspnetboilerplate

I was browsing abp's blogging module. There is a code like below in post repo.
https://github.com/abpframework/abp/blob/dev/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostRepository.cs
public async Task<Post> GetPostByUrl(Guid blogId, string url)
{
var post = await DbSet.FirstOrDefaultAsync(p => p.BlogId == blogId && p.Url == url);
if (post == null)
{
throw new EntityNotFoundException(typeof(Post), nameof(post));
}
return post;
}
I'm testing module and im getting error like blow if there is no entity.
An unhandled exception occurred while processing the request.
EntityNotFoundException: There is no such an entity. Entity type:
Volo.Blogging.Posts.Post, id: post
Volo.Blogging.Posts.EfCorePostRepository.GetPostByUrl(Guid blogId,
string url) in EfCorePostRepository.cs, line 35
It is normal to get this exception if there is no entity, but how to show 404 page or message like there is no record to user? I tried to throw UserFriendlyException but still getting and error page.

Related

(VueJS, Axios) Different way to catch errors

I'm currently building a single page application based on Laravel and VueJS.
Is there any better way then mine to handle errors with axios?
This is how I currently do it when a user clicks on login button:
VueTemplae:
methods : {
authenticateUser() {
axios.post('/api/login', this.form).then(() => {
this.$router.push({name : 'home'});
}).catch((error) => {
this.error = error.response.data.message;
});
}
}
Api route:
public function login() {
try {
// do validation
} catch(Exception) {
// validation failed
throw new Exception('login.failed');
}
// manually authentication
if(Auth::attempt(request()->only('email', 'password'))) {
return response()->json(Auth::user(), 200);
}
// something else went wrong
throw new Exception('login.failed');
}
Unfortunately, throwing an exception always prints an internal server error into the console.
If I return something else than an exception, axios always executes then().
Is there any way to prevent this or a better way to handle axios responses?
Thank you!
Your API needs to return a response with a 4XX status code in order for the catch block to fire in your Vue component.
Example:
After you catch the error on the API side, send a response with status code 400 Bad Request. It will be formatted similarly to your successful login response, but with an error message and 400 status code instead of 200.

Service failing to correctly return status code to UI

Maybe this is silly question but I'm trying to learn Spring MVC and I have everything working except for the exceptions. So I have a simple form application where the user can register, if the user already exists I'd like to send an error code to the UI so that it knows why it failed. Heres my code:
#ResponseBody
#PostMapping("users")
public ResponseEntity addUser(#RequestBody User user) {
List<User> users = usersService.addUser(user);
if(users == null) return new ResponseEntity(HttpStatus.EXPECTATION_FAILED);
else return new ResponseEntity<>(users, HttpStatus.ACCEPTED);
}
It works fine, as in it returns a status code to the UI but the exception returns it in this string format:
Error: Request failed with status code 417
at createError (createError.js:17)
at settle (settle.js:19)
at XMLHttpRequest.handleLoad (xhr.js:69)
The log above is from a console log from the UI, right after the catch below:
function register(user) {
return dispatch => {
axios.post(`${BASE_URL}/users`, user).then((response) => {
console.log(response);
dispatch(resetError());
dispatch(success(user));
}).catch((e) => {
console.log('e', e);
dispatch(error(e.status));
})
};
function success(user) { return { type: userConstants.REGISTER, payload: user } };
};
Funny enough it actually prints exactly what I'm looking for if the http call succeeds. Here's what it prints on the happy path of the promise (ACCEPTED):
Notice that it has a status property. I'd very much not like to parse a string on the UI side just to get the error code from the service. Why is the response object different? The only thing I've changed is the status code. How can I make the error status give the UI a nice object instead of a string?
If you'd like to pull the branch here is the URL: https://github.com/MatTaNg/react-form
The code snippets are in the UsersResource file
Instead of console.log('e', e) try console.log('e', e.response.status).
Source:
https://github.com/axios/axios#handling-errors

How to return instance of Error using Spring REST call

In my front end application, I have to handle the error response of the HTTP REST call.
Front end:
restservice.check().subscribe(
response => {
if (response != null) {
},
error => {
if (error instanceof Error) {
}}
});
Controller.java
public ResponseEntity updateEstablishment
{
return new ResponseEntity<>(obj, HttpStatus.OK);
}
How can I return an instance of Error here.
REST services should catch and handle internal exception. And should return meaningful error code and message back to client. Please find below link to best way to handle exception in REST
https://www.baeldung.com/rest-api-error-handling-best-practices

AspNetCore OpenIdConnect Error Handling

In my MVC Core project, after authenticating against Azure AD, I check to see if the User exists in my application database. If User does not exist I want to throw an exception and redirect to my Home/Error page.
Instead, the redirect code in OnAuthenticationFailed results in a redirect loop and finally quits with the error:
Bad Request - Request Too Long HTTP Error 400. The size of the request
headers is too long.
app.UseExceptionHandler("/Home/Error");
app.UseCookieAuthentication();
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
...
Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeReceived = async (context) =>
{
...
upn = identity.FindFirst(ClaimTypes.Upn).Value;
MyDbContext db =
new MyDbContext(Configuration.GetConnectionString("DefaultConnection"));
if (db.Users.FirstOrDefault(b => (b.UPN == upn)) == null)
{
throw new System.IdentityModel.Tokens.SecurityTokenValidationException("You are not registered to use this application.");
}
},
OnAuthenticationFailed = (context) =>
{
context.Response.Redirect("/Home/Error");
context.HandleResponse();
return Task.FromResult(0);
}
}
});
UPDATE:
This has been resolved. By default the HomeController has [Authroize] attribute set on the entire class so the redirect could not reach the Error action without being authenticated.

MVC 6 WebAPI returning html error page instead of json version of exception object

I am calling an api endpoint in an MVC 6 WebAPI:
POST http://localhost:57287/mytestapi/testentity/ HTTP/1.1
Accept: application/json
X-APIKey: 00000000-0000-0000-0000-000000000000
Content-Type: application/json; charset=utf-8
Host: localhost:57287
Content-Length: 1837
Expect: 100-continue
Connection: Keep-Alive
In the body I have json serialized test entity.
I have a bug in my entity controller code and the api is returning a 500 response 'Server Error' I know what the bug is an will fix it, however the issue I need some help with is that the API is returning HTML instead of the json serialized exception object - Json is what I expect: it's what the old webapi would return. I have ported the coded from an old test project that I know works.
So why is MVC 6 WebAPI returning html rather than json? Is there some configuration I need to do?
EDIT:
I added Accept: application/json to headers as suggested by #danludwig, however this did not resolve the issue, I still got an html error page back.
I looked at my StartUp.cs and found:
if (env.IsDevelopment())
{
//app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
in the ConfigureApp method. I tested with app.UseDeveloperExceptionPage(); commented out. This prevented the return of the html error page in the api response body, however I am still not getting the json serialised exception object.
The ExceptionHandlerMiddleware configured when using UseExceptionHandler("Home/Error") does not include any support for JSON. It will just return the error html page. The same can be said when using UseDeveloperExceptionPage.
As far as I know you will need to add yourself some piece of code that will handle errors and return a json.
One option is to use an exception filter and add it either globally or on selected controllers, although this approach would only cover exceptions coming from the controller action methods. For example the following filter will return a json object only when the request accept was application/json (Otherwise it would let the exception pass through which for example could be handled by the global error page):
public class CustomJSONExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
if (context.HttpContext.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json"))
{
var jsonResult = new JsonResult(new { error = context.Exception.Message });
jsonResult.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
context.Result = jsonResult;
}
}
}
services.AddMvc(opts =>
{
//Here it is being added globally.
//Could be used as attribute on selected controllers instead
opts.Filters.Add(new CustomJSONExceptionFilter());
});
Another option is to add your own exception handler middleware using the app.UseExceptionHandler overload that lets you specify the behavior of the alternative pipeline that will process the exception. I have quickly wrote a similar example using an inline middleware, which will return a json object only when the request accept was application/json:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseExceptionHandler(appBuilder =>
{
appBuilder.Use(async (context, next) =>
{
var excHandler = context.Features.Get<IExceptionHandlerFeature>();
if (context.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json"))
{
var jsonString = string.Format("{{\"error\":\"{0}\"}}", excHandler.Error.Message);
context.Response.ContentType = new MediaTypeHeaderValue("application/json").ToString();
await context.Response.WriteAsync(jsonString, Encoding.UTF8);
}
else
{
//I haven't figured out a better way of signally ExceptionHandlerMiddleware that we can't handle the exception
//But this will do the trick of letting the other error handlers to intervene
//as the ExceptionHandlerMiddleware class will swallow this exception and rethrow the original one
throw excHandler.Error;
}
});
});
Both approaches will let you have other error handlers that maybe provide html pages for non json requests (Another idea would be to either return a json or an html page from your custom error handler).
PS. If using the second approach, you most likely want to put that logic into its own middleware class and use a different approach to generate the json response. In that case take a look at what JsonResultExecutor does
I found a cheap hack to get what I want by adding this to the Startup Configure method:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Simple error page to avoid a repo dependency.
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
if (context.Response.HasStarted)
{
throw;
}
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
var json = JToken.FromObject(ex);
await context.Response.WriteAsync(json.ToString());
}
});
//Rest of configure method omitted for brevity.
}

Resources