I would like to add a custom response when an endpoint is being accessed without access token. I added this line of code into my Handler.php
public function render($request, Exception $exception)
{
if ($exception instanceof AuthorizationException) {
return response()->json([
'message' => 'Unauthorized'
],401);
}
return parent::render($request, $exception);
}
Instead of getting the json response, I got a blank response with 500 error code on postman. What am I doing wrong?
The Response:
I figured it out by making sure that the server's response is in JSON. So I made a middleware file called ForceJsonResponse.php. Here is the code:
public function handle(Request $request, Closure $next)
{
$request->headers->set('Accept', 'application/json');
return $next($request);
}
Registered it in Kernel.php on $middleware as
\App\Http\Middleware\ForceJsonResponse::class,
And in $routeMiddleware as
'json.response' => \App\Http\Middleware\ForceJsonResponse::class,
Related
I have created an API using laravel to check if an email existed in database or not.
/**
*
* #param string $email
*/
public function checkUserEmail($email)
{
$userCount = User::where('email', $email);
if($userCount->count()){
return response()->json("true");
}else{
return response()->json("false");
}
}
I have tested this API in postman and it works as expected
Test using Postman
But, when I have use it in frontend it return an object not string!!
Frontend:
checkUserEmail(email : string){
return this.http.get<any>("http://127.0.0.1:8000/api/auth/user-email/"+email);
}
Browser console
In order to get the returned string using HttpClient in Angular you have to subscribe to Observable that returns.
Or you can do the async way using firstValueFrom from RxJS. Notice that response is in string because contains quotes ("false"), is better to just send true or false as boolean.
Here's the example:
public checkUserEmail(email: string): Observable<string> {
return this.http.get("http://127.0.0.1:8000/api/auth/user-email/" + email)
.pipe(
map((response: string) => response as string),
catchError( (errorObject) => {
return throwError(() => errorObject);
}
)
);
}
And now you can call this function subscribing or using async way:
Subscribe way (don't forget to unsubscribe):
checkIfEmailIsInDatabase(email: string): void {
this.checkUserEmail("some#email.com").subscribe(
(response: string) => {
// Do your stuff here, like setting variables or whatever
console.log("Response: ", response);
}
)
}
Async way:
async checkIfEmailIsInDatabase(email: string): Promise<void> {
let responseFromServer: string = await firstValueFrom(this.checkUserEmail(email));
console.log("Response from server: ", responseFromServer);
}
Hope it helps.
In Angular the return type of the httpClient's methods are Observables. So in your client code you have to subscribe to the observable. I also suggest so change "any" to string
I have a control that calls a service.
If the service returns an empty payload from the db I want to throw an exception.
at the moment I am doing that in the service:
this is the service I have at the moment with the exception.
async getPreferences(eUserId: string): Promise<UserPreferences> {
const userPreferences = await this.userPreferencesModel.findOne({
eUserId,
});
if (!userPreferences) {
throw new NotFoundException("We couldn't find your user preferences");
}
return userPreferences;
}
I want the controller to handle the exception, The issue is that the controller response is a Promise.
How can I handle that?
This is what I shave done:
#Get()
async getPreferences(
#Headers('x-e-user-id') eUserId: string,
): Promise<UserPreferences> {
const userPreferences = this.userPreferencesService.getPreferences(eUserId);
console.log('userPreferences: ', userPreferences);
// Here is what I am trying to monitor...
if (userPreferences) {
throw new NotFoundException("We couldn't find your user preferences");
}
return userPreferences;
}
Ther console.log in the controller returns:
userPreferences: Promise { <pending> }
Now, if the service response is empty no exception is thrown.
How can I monitor the service result in order to throw an exception
Multiple ways you can solve this. Here's one.
Don't throw an error in your service, just return the result or null.
async getPreferences(eUserId: string): Promise<UserPreferences | null> {
return this.userPreferencesModel.findOne({
eUserId,
});
}
Then in your controller await for the result, you forgot this. That's why you are seeing a pending promise. After the result has been resolved, check if any user preferences were returned and throw the NotFoundException if not.
#Get()
async getPreferences(#Headers('x-e-user-id') eUserId: string): Promise<UserPreferences> {
const userPreferences = await this.userPreferencesService.getPreferences(eUserId);
if (!userPreferences) {
throw new NotFoundException("We couldn't find your user preferences");
}
return userPreferences;
}
I would not throw NotFoundException or any other HTTP related error from your service. Leave that responsibility up to your controller, don't tie your service (logic) to HTTP error codes. Throw errors here that are not aware of the context (HTTP) they are being used in.
PS: You might also consider passing the user ID via the query string or as a route parameter instead of via the headers.
I have an application with both MVC and 'new' ApiController endpoints in ASP.NET Core 2.2 co-existing together.
Prior to adding the API endpoints, I have been using a global exception handler registered as middleware using app.UseExceptionHandler((x) => { ... } which would redirect to an error page.
Of course, that does not work for an API response and I would like to return an ObjectResult (negotiated) 500 result with a ProblemDetails formatted result.
The problem is, I'm not sure how to reliably determine in my 'UseExceptionHandler' lambda if I am dealing with an MVC or a API request. I could use some kind of request URL matching (eg. /api/... prefix) but I would like a more robust solution that won't come back to bite me in the future.
Rough psuedo-code version of what I'm trying to implement is:
app.UseExceptionHandler(x =>
{
x.Run(async context =>
{
// extract the exception that was thrown
var ex = context.Features.Get<IExceptionHandlerFeature>()?.Error;
try
{
// generically handle the exception regardless of what our response needs to look like by logging it
// NOTE: ExceptionHandlerMiddleware itself will log the exception
// TODO: need to find a way to see if we have run with negotiation turned on (in which case we are API not MVC!! see below extensions for clues?)
// TODO: ... could just use "/api/" prefix but that seems rubbish
if (true)
{
// return a 500 with object (in RFC 7807 form) negotiated to the right content type (eg. json)
}
else
{
// otherwise, we handle the response as a 500 error page redirect
}
}
catch (Exception exofex)
{
// NOTE: absolutely terrible if we get into here
log.Fatal($"Unhandled exception in global error handler!", exofex);
log.Fatal($"Handling exception: ", ex);
}
});
});
}
Any ideas?
Cheers!
This might be a bit different than what you expect, but you could just check if the request is an AJAX request.
You can use this extension:
public static class HttpRequestExtensions
{
public static bool IsAjaxRequest(this HttpRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.Headers == null)
return false;
return request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
}
And then middleware with an invoke method that looks like:
public async Task Invoke(HttpContext context)
{
if (context.Request.IsAjaxRequest())
{
try
{
await _next(context);
}
catch (Exception ex)
{
//Handle the exception
await HandleExceptionAsync(context, ex);
}
}
else
{
await _next(context);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
//you can do more complex logic here, but a basic example would be:
var result = JsonConvert.SerializeObject(new { error = "An unexpected error occurred." });
context.Response.ContentType = "application/json";
context.Response.StatusCode = 500;
return context.Response.WriteAsync(result);
}
see this SO answer for a more detailed version.
If you want to check whether the request is routed to ApiController, you could try IExceptionFilter to hanlde the exceptions.
public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (IsApi(context))
{
HttpStatusCode status = HttpStatusCode.InternalServerError;
var message = context.Result;
//You can enable logging error
context.ExceptionHandled = true;
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)status;
response.ContentType = "application/json";
context.Result = new ObjectResult(new { ErrorMsg = message });
}
else
{
}
}
private bool IsApi(ExceptionContext context)
{
var controllerActionDesc = context.ActionDescriptor as ControllerActionDescriptor;
var attribute = controllerActionDesc
.ControllerTypeInfo
.CustomAttributes
.FirstOrDefault(c => c.AttributeType == typeof(ApiControllerAttribute));
return attribute == null ? false : true;
}
}
Thanks to all of the advice from others, but I have realised after some more thought and ideas from here that my approach wasn't right in the first place - and that I should be handling most exceptions locally in the controller and responding from there.
I have basically kept my error handling middleware the same as if it was handling MVC unhandled exceptions. The client will get a 500 with a HTML response, but at that point there isn't much the client can do anyway so no harm.
Thanks for your help!
I am doing http client request
export class MapjsonService{
theUrl = 'http://localhost:4200/api/Lat_Long.json';
constructor(private http: HttpClient) { }
fetchNews(): Observable<any>{
return this.http.get(this.theUrl)
}
It is working about 99.99% of the time sadly this is running so often that is fails like once every 10 mins with
HttpErrorResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "http://localhost:4200/api/Lat_Long.json", ok: false, …}
and
"Http failure during parsing for http://localhost:4200/api/Lat_Long.json"
Now I figured out for some reason my nrql query from newrelic (which is what is being stored in '/api/lat_long.json' does not have the final closing '}' once every orange moon. and this is what is throwing this error. my question is there any whay for me to check if the returned value is valid json and if it is not try the GET request again without terminating the process that called it. Thx
Your code is throwing an error because the json is not correct, therefore it can't be parsed, and therefore the observable throws an error:
fetchNews(): Observable<any>{
return this.http.get(this.theUrl)
}
By default, the http client expect json because that's usually what users expect from it. It's not always the case, like the situation you are in right now.
We can tell the http client not to parse the json on its own by specifying what we want from it using the {responseType: 'text'} parameter.
fetchNews(): Observable<any>{
return this.http.get(this.theUrl, {responseType: 'text'})
}
But then you need to parse the json when possible. So we will map the observable and parse the content here if possible.
fetchNews(): Observable<any>{
return this.http.get(this.theUrl, {responseType: 'text'}).map(res => {
try{
return JSON.parse(res);
} catch {
return null;
}
})
}
Then do whatever you want, the value returned by the observable will be null if it can't be parsed.
RXJS 6 syntax:
fetchNews(): Observable<any>{
return this.http.get(this.theUrl, {responseType: 'text'}).pipe(
map(res => {
try{
return JSON.parse(res);
} catch {
return null;
}
})
)
}
Razor:
#Html.TextBoxFor(kod => kod.Name)
#Html.ValidationMessage("Name","Client Error Message")
Controller:
[HttpPost]
public JsonResult JsonAddCustomer(Customers customer, string returnUrl)
{
if (customer.Name.Trim().Length == 0)
{
ModelState.AddModelError("Name", "Server Error Message");
}
//Eğer hata yoksa veri tabanına kayıt yapılıyor.
if (ModelState.IsValid)
{
try
{
CusOpp.InsertCustomer(customer);
return Json(new { success = true, redirect = returnUrl });
}
catch (Exception e)
{
ModelState.AddModelError("", "Error");
}
}
return Json(new { errors = GetErrorsFromModelState() });
}
I want to write validation error message. I did this like above, but #Html.ValidationMessage("Name","Client Error Message") does not work. In fact, I was already expecting it.
I want to show like this statement's result: #Html.ValidationMessageFor(m => m.name) ,but I cant use this, because I used entity-data-model.
Should I add [Required] statement to data-model classes or any way that I do this. Sorry for bad explanation.
Thanks.
You should return PartialViews instead of JSON in this case. Only in the case of success you could return JSON:
[HttpPost]
public ActionResult JsonAddCustomer(Customers customer, string returnUrl)
{
// Warning: the following line is something horrible =>
// please decorate your view model with data annotations or use
// FluentValidation.NET to validate it.
// Never write such code in a controller action.
if (customer.Name.Trim().Length == 0)
{
ModelState.AddModelError("Name", "Server Error Message");
}
//Eğer hata yoksa veri tabanına kayıt yapılıyor.
if (ModelState.IsValid)
{
try
{
CusOpp.InsertCustomer(customer);
return Json(new { success = true, redirect = returnUrl });
}
catch (Exception e)
{
ModelState.AddModelError("", "Error");
}
}
return PartialView(customer);
}
Now inside the success callback of your AJAX request you can test whether the POST action succeeded or not:
success: function(result) {
if (result.redirect) {
// we are in the success case => redirect
window.location.href = result.redirect;
} else {
// a partial view with the errors was returned => we must refresh the DOM
$('#some_container').html(result);
// TODO: if you are using unobtrusive client side validation here's
// the place to call the $.validator.unobtrusive.parse("form"); method in order
// to register the unobtrusive validators on the newly added contents
}
}
Here's a similar post that you might also read through.
Your Idea with the Required annotation on the model is a good approach. You can set a Error Message on the Required annotation.
[Required(ErrorMessage = "Please enter a name")]
and remove your if in your action..this:
if (customer.Name.Trim().Length == 0)
{
ModelState.AddModelError("Name", "Server Error Message");
}
the ModelState.IsValid will do the job for you on the client and server side.
And use your #Html.ValidationMessageFor(m => m.name) in your view