I'm trying to catch an exception between two services but the catch code is unreachable. Does any one knows why?
Controller:
def controllerMethod() {
try {
service1.execute()
} catch(CustomException ce) {
// unreachable code
}
}
Service 1:
def service2
def service1Method() {
try {
service2.execute()
} catch(CustomException ce) {
// unreachable code
throw ce
}
}
Service 2:
def service2Method() {
throw new CustomException("exception")
}
Exception:
class CustomException extends Exception {
CustomException(String message){
super(message)
}
}
Running Sample: https://bitbucket.org/victor-giacomo/grails-exception-propagation/src/master/
See the project at github.com/jeffbrown/victorsoaresexception.
grails-app/services/victorsoaresexception/Service1Service.groovy
package victorsoaresexception
import grails.gorm.transactions.Transactional
#Transactional
class Service1Service {
Service2Service service2Service
def service1Method() {
try {
service2Service.service2Method()
} catch(CustomException ce) {
// unreachable code
throw ce
}
}
}
grails-app/services/victorsoaresexception/Service2Service.groovy
package victorsoaresexception
class Service2Service {
def service2Method() {
throw new CustomException("exception")
}
}
grails-app/controllers/victorsoaresexception/DemoController.groovy
package victorsoaresexception
class DemoController {
Service1Service service1Service
def controllerMethod() {
try {
service1Service.service1Method()
render "No Exception Was Thrown"
} catch(CustomException ce) {
render "CustomException was thrown, and caught by controller"
}
}
}
That all appears to work:
$ [main] http :8080/demo/controllerMethod
HTTP/1.1 200
Connection: keep-alive
Content-Type: text/html;charset=utf-8
Date: Wed, 28 Dec 2022 18:03:39 GMT
Keep-Alive: timeout=60
Transfer-Encoding: chunked
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
CustomException was thrown, and caught by controller
$
Related
i have had this bug for a long peroid now, was trying to figure it out myself but am not getting it, it's an angular/spring boot app that am trying to upload different files and also save a json data at the same time, i have gone through previous questions concerning it but it hasn't worked for me, but when i tried saving the data via postman it works perfectly but not the same with angular, please i will appreciate the assistance...
this is my component.ts file containing the function
addBeat(artWork: File,untaggedmp3:File, taggedmp3:File){
const formData:FormData = new FormData();
if(artWork){
formData.append('artWork', artWork)
}
if(untaggedmp3){
formData.append('untaggedmp3', untaggedmp3)
}
if(taggedmp3){
formData.append('taggedmp3', taggedmp3)
}
this.beat.name = this.addBeatForm.get('name').value;
this.beat.price = this.addBeatForm.get('price').value;
this.beat.tempo = this.addBeatForm.get('tempo').value;
this.beat.description = this.addBeatForm.get('description').value;
this.beat.beatKey = this.addBeatForm.get('beatKey').value;
this.beat.mood = this.addBeatForm.get('mood').value;
this.beat.genre = this.addBeatForm.get('genre').value;
formData.append('beatDto', JSON.stringify(this.beat));
this.beatService.uploadBeat(formData)
.subscribe(
(data)=>{
console.log(data);
}
);
}
And my service.ts
public uploadBeat(formData: FormData): Observable<BeatDto>{
return this.httpClient.post<BeatDto>(`${this.apiServerUrl.admin}/api/beat/single/upload`, formData,
{headers: {
'Accept':'multipart/form-data',
'mimeType': 'multipart/form-data'}}
));
}
and my springboot rest controller is this
#RestController
#RequestMapping("/api/beat")
#AllArgsConstructor
#Slf4j
public class BeatController {
private final BeatService beatService;
private final FileStorageService fileStorageService;
#PostMapping(path = "/single/upload", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<BeatDto> uploadBeat(#RequestParam("beatDto") String beatDto,
#RequestParam("artWork") MultipartFile artWork,
#RequestParam("taggedmp3") MultipartFile taggedmp3,
#RequestParam("untaggedmp3") MultipartFile untaggedmp3){
BeatDto beatJson = beatService.getJson(beatDto,artWork,taggedmp3,untaggedmp3);
return ResponseEntity.status(HttpStatus.CREATED).body(beatService.uploadBeat(beatJson,artWork,taggedmp3,untaggedmp3));
}
}
so when i test in on the browser i get a error code 400 on the browser console...
{
"headers": {
"normalizedNames": {},
"lazyUpdate": null
},
"status": 400,
"statusText": "OK",
"url": "http://localhost:8081/api/beat/single/upload",
"ok": false,
"name": "HttpErrorResponse",
"message": "Http failure response for http://localhost:8081/api/beat/single/upload: 400 OK",
"error": null
}
And also from my network tab
Request URL: http://localhost:8081/api/beat/single/upload
Request Method: POST
Status Code: 400
Remote Address: [::1]:8081
Referrer Policy: strict-origin-when-cross-origin
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:4200
Access-Control-Expose-Headers: Origin, Content-Type, Accept, Authorization
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: close
Content-Length: 0
Date: Thu, 06 Jan 2022 02:02:41 GMT
Expires: 0
Pragma: no-cache
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Accept: multipart/form-data
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 638
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFUAjEynxdkQfpcfA
Host: localhost:8081
mimeType: multipart/form-data
Origin: http://localhost:4200
Referer: http://localhost:4200/
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
main.app
#SpringBootApplication
#EnableAsync
#Import(SwaggerConfiguration.class)
public class BasicApplication {
public static void main(String[] args) {
SpringApplication.run(BasicApplication.class, args);
}
#Bean
public CorsFilter corsFilter(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
corsConfiguration.setAllowedHeaders(Arrays.asList("Origin", "Access-Control-Allow-Origin", "Content-Type",
"Accept", "Authorization", "Origin, Accept", "X-Requested-With",
"Access-Control-Request-Method", "Access-Control-Request-Headers"));
corsConfiguration.setExposedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization",
"Access-Control-Allow-Origin", "Access-Control-Allow-Origin", "Access-Control-Allow-Credentials"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
corsConfiguration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}
security config
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/resources/**", "/webjars/**", "/assests/**").permitAll()
.antMatchers("/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html")
.permitAll()
.antMatchers("/api/auth/**").permitAll()
.antMatchers(HttpMethod.GET,"/api/beat/**").permitAll()
.antMatchers(HttpMethod.POST,"/api/beat/**").permitAll()
.anyRequest()
.authenticated();
httpSecurity.addFilterBefore(jwtAuthFilter,
UsernamePasswordAuthenticationFilter.class);
}
Thanks In Advance
I am using a custom passport strategy and I would like to send to the client a custom error If the client denies access to the oauth2 application.
This is the authorization flow:
I would like to send the user a customized error message instead of the 500 error. In case of success, when the user authorizes the application everything goes well.
In the documentation NestJS official documentation there is an example of how I could do this:
https://docs.nestjs.com/security/authentication#extending-guards
import {
ExecutionContext,
Injectable,
UnauthorizedException,
} from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext) {
// Add your custom authentication logic here
// for example, call super.logIn(request) to establish a session.
return super.canActivate(context);
}
handleRequest(err, user, info) {
// You can throw an exception based on either "info" or "err" arguments
if (err || !user) {
throw err || new UnauthorizedException();
}
return user;
}
}
There is no apparent change and when I analyze the application logs, the only thing that is different is that it raises an exception:
api_1 | [Nest] 5080 - 12/10/2020, 6:13:54 PM [RouterExplorer] Mapped {/users/sign-up/email, POST} route +0ms
api_1 | [Nest] 5080 - 12/10/2020, 6:13:54 PM [RouterExplorer] Mapped {/users/sign-up/confirm-email, GET} route +0ms
api_1 | [Nest] 5080 - 12/10/2020, 6:13:54 PM [NestApplication] Nest application successfully started +3ms
api_1 | [Nest] 5080 - 12/10/2020, 6:14:00 PM [ExceptionsHandler] Object:
api_1 | {
api_1 | "name": "AuthorizationError",
api_1 | "message": "",
api_1 | "code": "access-denied",
api_1 | "status": 500
api_1 | }
api_1 | +6377ms
These are the relevant codes regarding this problem.
meli.strategy.ts
import { Strategy } from 'passport-oauth2';
import { PassportStrategy } from '#nestjs/passport';
import { Injectable } from '#nestjs/common';
import UsersService from '#/users/users.service';
import MeliService from '#/meli/meli.service';
#Injectable()
export class MeliStrategy extends PassportStrategy(Strategy, 'meli') {
constructor(
private usersService: UsersService,
private meliService: MeliService,
) {
super({
authorizationURL: `https://auth.mercadolivre.com.br/authorization?response_type=code&client_id=${process.env.MELI_APP_ID}`,
tokenURL: 'https://api.mercadolibre.com/oauth/token',
clientID: process.env.MELI_APP_ID,
clientSecret: process.env.MELI_APP_SECRET,
callbackURL: process.env.MELI_REDIRECT_URL,
scope: 'authorization_code',
});
}
async validate(accessToken: string) {
const userData = await this.meliService.getUserData(accessToken);
let user = await this.usersService.findUserByMercadoLibre(userData);
if (!user) {
user = await this.usersService.signUpByMercadoLibre(userData);
}
return user;
}
}
export default MeliStrategy;
meli-auth.guard.ts
import {
BadRequestException,
ExecutionContext,
Injectable,
} from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export default class MeliAuthGuard extends AuthGuard('meli') {
canActivate(context: ExecutionContext) {
// Add your custom authentication logic here
// for example, call super.logIn(request) to establish a session.
return super.canActivate(context);
}
handleRequest(err, user, info) {
// You can throw an exception based on either "info" or "err" arguments
if (err || !user) {
throw err || new BadRequestException('Just a custom message...');
}
return user;
}
}
I have a netcoreapp2.1 webapp which is presenting an issue in Google Chrome, but not Edge or IE, during the OIDC flow redirect. It simply stops at http://localhost:5000/signin-oidc with this response received:
General:
Request URL: http://localhost:5000/signin-oidc
Request Method: POST
Status Code: 500 Internal Server Error
Remote Address: [::1]:5000
Referrer Policy: no-referrer-when-downgrade
Response Headers:
Content-Length: 0
Date: Tue, 04 Aug 2020 09:49:32 GMT
Server: Kestrel
Request Headers:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9
Cache-Control: max-age=0
Connection: keep-alive
Content-Length: 2148
Content-Type: application/x-www-form-urlencoded
Host: localhost:5000
Origin: null
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36
Form Data:
code: Ry9Pec...rdG1U0SB
scopes: resource.WRITE+openid+resource.READ
state: CfDJ8JZ...oESbUvQ
id_token: eyJhbG...9sXMEhs
When I visit localhost:5000 in chrome (incognito or not) the code never reaches the OnTicketReceived event, whereas when fired from IE, Edge etc it does, and proceeds just fine.
This is the startup class:
public class Startup
{
private AppSettings _appSettings;
private IConfiguration _config;
public Startup(IConfiguration configuration)
{
_config = configuration;
_appSettings = _config.Get<AppSettings>();
}
// 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.Configure<AppSettings>(_config);
services.AddSingleton<IAPIRepository, APIRepository>();
services.AddSingleton<IUserRepository, UserRepository>();
services.AddSingleton<INavigationRepository, NavigationRepository>();
services.UseOpenIDConnectMiddleware(new OpenIDConnectMiddlewareOptions
{
BaseUrl = _appSettings.API.BaseUrl,
AppName = _appSettings.AppName,
ClientId = _appSettings.API.ClientId,
ClientSecret = _appSettings.API.ClientSecret,
Secure = !_appSettings.Local
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
if (!_appSettings.Local)
{
app.UseGlobalLoginMiddleware();
}
if (_appSettings.FeatureFlags["ProcessRedirectionRules"])
{
app.UseRedirectUserMiddleware(_appSettings.RedirectionRules);
}
app.UseMvc();
app.UseProtectHTMLRouteMiddleware();
if (_appSettings.Local)
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
ProjectPath = Path.Join(Directory.GetCurrentDirectory(), "../UI"),
ConfigFile = "node_modules/#vue/cli-service/webpack.config.js",
HotModuleReplacement = true
});
}
else
{
app.UseDefaultFiles();
app.UseStaticFiles();
}
app.UseJavascriptVarMiddleware(new JavascriptSettingsMiddlewareOptions
{
FileName = "env.js",
ObjectName = "__env",
Settings = new Dictionary<string, string>
{
{ "insightsKey", _appSettings.ApplicationInsights.InstrumentationKey },
{ "environment", _appSettings.Environment },
{ "gatekeeperBaseUrl", _appSettings.Gatekeeper.BaseUrl }
}
});
app.UseGlobalSignoutMiddleware(new GlobalSignoutMiddlewareOptions
{
GatekeeperLogoutUrl = _appSettings.API.LogoutUrl
});
app.UseHTMLRouteFallback(new HTMLRouteFallbackMiddlewareOptions
{
Local = _appSettings.Local,
Path = Path.Join(Directory.GetCurrentDirectory(), "../UI/dist")
});
}
}
I have cleaned the localhost and localhost:5000 cookies from the browser.
A quick guess would be that it is a SameSite cookie issue that different browsers handle differently due to various bugs.
This article might give you a starting point:
How To Prepare Your IdentityServer For Chrome's SameSite Cookie Changes - And How To Deal With Safari, Nevertheless
Also, your Configure method looks very odd. Because the ordering of the App.Use statements matter and usually you would put the app.UseMvc(); last in that method. Each incoming request will pass through those middlewares that the App.UseXXX adds and if the ordering is not correct then you will have issues.
I have a Exception middleware on ASP.NET Core application that do this:
try
{
await _next(httpContext);
}
catch (MyException exception)
{
httpContext.Response.ContentType = "application/json";
httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
await httpContext.Response.WriteAsync(exception.Message);
}
On this example, we send "Forbidden" (403 http status code) but I always receive 500. I inspect this on Swagger and Google Chrome and I don't understand the reason.
You have probably got your middleware registered in the wrong place in the pipeline. The order you place your middleware in startup.cs makes a difference. For example:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync(ex.Message);
}
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc();
}
In the code above I have registered middleware similar to yours at the start of the pipeline. ASP.NET Core will process any requests in the order you place them in, so my custom middleware will run first for a request. However, the responses are handled from the bottom upwards. So, in the example, when an exception is thrown in a controller (or anywhere) the UseDeveloperExceptionPage or UseExceptionHandler will receive any exception first, handle it and change the status code to 500.
If we change the order to this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync(ex.Message);
}
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc();
}
Now we have registered our handler to be after the built-in exception handler for a request but, more importantly, before them for a response. So, in this case, when a controller throws an exception, our handler will catch it, process it and change the status code to what we want. The other exception handlers will not see the exception (unless another middleware handler throws an exception after us).
I have an Web Api controller for access data from server:
public class ServicesController : ApiController
{
[AcceptVerbs("POST")]
public IList<TreeMenuItem> LoadMetadata()
{
List<TreeMenuItem> itemsMenu = new List<TreeMenuItem>();
TreeMenuItem dataSource = new TreeMenuItem("1", "DataSources", null);
itemsMenu.Add(dataSource);
return itemsMenu;
}
}
which is call by an angularJS controler:
angular.module('App').
controller('TreeMenuController', ["$scope", "$http", treeMenuController]);
function treeMenuController($scope, $http) {
var baseUrl = "api/Services/LoadMetadata";
$http.post(baseUrl)
.then(function (result) {
$scope.roleList = result.data;
});
};
In browser network I have:
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate
Content-Length:2
Content-Type:application/json;charset=UTF-8
Request Payload
{}
Response Headers:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Content-Length: 4
in Response tab: [{}].
What is wrong?
I make it work:
The big help was the response message when I put in browser the address for accessing api services (api/Services/LoadMetadata):
The error answer was in an xml file where I found that was problem with serialization of the object TreeMenuItem. The advice was to decorate with DataContract the class and DataMember the class properties - like in WCF. After I did that (was need to add reference in project to System.Runtime.Serialization), everything was perfect.