I'm currently using a Spring Boot application I'm tinkering around the the error page and the messages given to it. Currently I can change the HTTP Status Number and Message, but I'm not sure how to change the "Unknown reason" or Description without changing it to something besides 418. Is there a way to customize those as well, or am I stuck with the embedded code provide?
Current Code Tinkering
for(String serialNo : serialNoList) {
if(serialNo.length() < MIN_SERIALNO_SIZE ) {
response.sendError(401, "Serial Number Length Exceeded: " + serialNo);
}
if(serialNo.length() > MAX_SERIALNO_SIZE) {
response.sendError(403, "Serial Number Legth Too Short: " + serialNo);
}
}
First, you need to disable whiteLabel error pages.
server.error.whitelabel.enabled=false
or
// adding this on your main class
#EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})
Now, create a html page (error.html), which you want to display and place it in resources/templates directory, it will be picked automatically.
To customize, differently for each error you can implement ErrorController.
#Controller
public class CustomErrorController implements ErrorController {
// override this error path to custom error path
#Override
public String getErrorPath() {
return "/custom-error";
}
#GetMapping("/custom-error")
public String customHandling(HttpServletRequest request){
// you can use request to get different error codes
// request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)
// you can return different `view` based on error codes.
// return 'error-404' or 'error-500' based on errors
}
}
Related
I use an external rest api in my spring application, I can send json post requests to create objects but when a field is incorrect or if there is a duplicate it returns a 400 bad request error, and a body saying what the problem is.
I use Spring 5 with #PostExchange in the following code:
This is used to point spring into the right direction of the external api
public interface HardwareClient {
#PostExchange("/assetmgmt/assets/templateId/C04DBCC3-5FD3-45A2-BD34-8A84CE2EAC20")
String addMonitor(#RequestBody Monitor monitor);
}
This is the helper that is autowired into the class where I have the data that needs to be sent.
#Component
public class HardwareHelper {
private Logger logger = Logger.getLogger(getClass().getName());
#Autowired
HardwareClient hardwareClient;
#Async
public Future<String> addMonitor(MonitorForm monitorForm){
try {
Monitor monitor = new Monitor(monitorForm.objectID(), monitorForm.model(), monitorForm.make(),monitorForm.serialNumber(), monitorForm.orderNumber(),monitorForm.budgetholder(),monitorForm.ownership());
hardwareClient.addMonitor(monitor);
return new AsyncResult<String>("Success");
} catch (Exception e){
logger.info("HardwareHelper.addMonitor error: " + e.getMessage());
//todo error handling
}
return null;
}
}
When an error occurs the logger will print the error but I need to be able to control what happens after based on the response. So I need to see the body of the post request that is returned after. If everything goes well an ID is returned that I can read by printing the results of the addMonitor() method, but this is obviously not possible when it throws an exception as it skips to the catch part. How do I scan the request body when an error is thrown and handle this appropriately
I'm using mysql for my db, spring for my backend and angular for my frontend. my frontend is throwing this weird bug when its routed proper: click here to see it
as you can see, the path at the end is %7Bid%7D (looks like {id} from the backend)
the http error code is always one of 3: 400,400 or 500
the backend looks okay and I've only really ever gotten this error code:
2022-02-04 15:30:31.465 WARN 15200 --- [nio-8081-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "{id}"]
here is the controller in question(a get request):
#CrossOrigin
#RestController
#RequestMapping(path = "/api/patient")
public class PatientPortalController {
#Autowired
private PatientPortalRepo patientPortalRepo;
#PostMapping("/patientportal")
public PatientPortal createPatientPortal(#RequestBody PatientPortal patientportal) {
return patientPortalRepo.save(patientportal);
}
#GetMapping("/patientportal/{id}")
public ResponseEntity<PatientPortal> getpatientPortal(#PathVariable Long id){
PatientPortal patientportal = patientPortalRepo.findByPatientPortalId(id);
if(patientportal.getId()>0 && patientportal!=null)
return new ResponseEntity<PatientPortal>(patientportal, HttpStatus.OK);
return new ResponseEntity<PatientPortal>(patientportal, HttpStatus.BAD_REQUEST);
}}
Some things worth of note that I've tried with the backend
Tried changing response entity to of type long and returning id, tried refactoring the controller numerous times, tried changing decorators/paths around, 20x checked the types are correct, checked if any types other than the id are throwing it, checked if I had any security implemented that was denying access, checked if adding a onetoone would get it to pop up on the front end. It works fine on the backend(returns a list of what I'd assume is patientportal object) but I am either routing incorrectly, there is some security I'm missing, there is some type error, or there is some logic errors. I think however the issue lies in the front end.
here's the code where I call the front end method hard coded a value to test:
this.patientloginservice.loginPatient(this.patient).subscribe(data=>(this.route.navigate(['api/patient/patientportal/1'])),error=>console.log('error'));
and here is where that code is serviced:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http'
import { Observable } from 'rxjs'
import { PatientPortal } from './patientportal';
#Injectable({
providedIn: 'root'
})
export class PatientService {
private baseURL = "http://localhost:8081/api/patient/patientportal";
constructor(private httpClient: HttpClient) { }
getPatientPortalList(): Observable<PatientPortal[]> {
return this.httpClient.get<PatientPortal[]>(`${this.baseURL}`);
}
createPatientPortal(patientportal: PatientPortal): Observable<Object>{
return this.httpClient.post<Object>(`${this.baseURL}`, patientportal);
}
getPatientPortalById(id: number): Observable<PatientPortal>{
return this.httpClient.get<PatientPortal>(`${this.baseURL}/{id}`);
}
updatePatientPortal(id: number, patientportal: PatientPortal): Observable<Object>{
return this.httpClient.put(`${this.baseURL}/{id}`, patientportal);
}
deletePatientPortal(id: number): Observable<Object>{
return this.httpClient.delete(`${this.baseURL}/{id}`);
}
}
any help will be much appreciated, thank you. again like I said the route routes correctly as far as I can tell, but the rendered table does not fill data and it throws that error. I am using a login that is to redirect/route to a patient's details.
You're using Template literals incorrectly.
Instead of just {id} it should be ${id} just like what you did with ${this.baseUrl}
Hope that solves your issue.
I have pojo classes that handle my backend connections. I want to encapsulate my (backend) error handling in these classes by catching backend exceptions inside.
Is there any way to access the current wicket page (or any component for that matter) to enable me to give feedback to the user from outside the wicket component hierarchy?
class MyService {
...
public void doBackEndThing(){
try {
backEndService.doRemoteCall();
} catch (BackendException e) {
//we're not inside the component hierarchy! so no getPage() available
WebPage page = getCurrentPage();
page.error("Backend is currently not available");
}
}
I've tried the PageManager, but I have no idea how to retrieve the correct version and so I do not know if would work at all:
int version = ?;
WebPage page = (WebPage )Session.get().getPageManager().getPage(version);
There isn't a nice way and it doesn't seem to be a good idea to do this. Your frontend should call your backend not the other way. So the easiest way to do this would be to store the errors inside your service and have your page get these.
class MyService {
private String error;
public void doBackEndThing(){
try {
backEndService.doRemoteCall();
} catch (BackendException e) {
error ="Backend is currently not available";
}
}
}
and
class MyPage extends WebPage {
private MySerivce service;
public void doSomethingFrontendy() {
error = service.getError();
}
}
or you could return an error from your backend method or throw an Exception and handle this in your WebPage or use IRequestCycleListener#onException() like #svenmeier pointed out.
IRequestCycleListener#onException() is a better place for this - you can get access to the current page via RequestCycle#getActiveRequestHandler().
Is there any way to do that?
I heard something about implemented RequestCycle, how to acomplish that?
Tried How can I get the responsePage from a RequestCycle in Wicket 1.5? this, but doesnt work.
The reason why you get a PageExpiredException in Wicket is because Wicket is unable to find the page. There is no way of determining the type of the page that is no longer available, because, well, the page actually is no longer there. It ceased to exist, met its maker, bereft of life, rests in peace, its lifecycle are now 'istory, kicked the bucket. It is an ex-page.
So Wicket's only recourse is to serve the PageExpiredException, and there is no way (in Wicket itself) to retrieve the page that was attempted to resurrect.
Now what you can try to do is to store the class of the last rendered page in the session, and use that in your RequestCycleListener implementation of onException() and return the appropriate request handler. In code:
#Override
public void onRequestHandlerExecuted(RequestCycle cycle, IRequestHandler handler) {
Class<? extends Page> p = null;
if(handler instanceof IPageRequestHandler)
p = ((IPageRequestHandler)handler).getPageClass();
else if(handler instanceof IComponentRequestHandler)
p = ((IComponentRequestHandler)handler).getComponent().getPage().getClass();
MySession.get().setLastPageClass(p);
}
#Override
public IRequestHandler onException(RequestCycle cycle, Exception ex) {
Class<? extends Page> pageClass MySession.get().getLastPageClass();
... return some handler based on your logic
}
You might want to check for more IRequestHandler implementations in onRequestHandlerExecuted.
If I understand correctly you want to redirect user only if pageExpired happened from specific page? You can try something like this in you implementation of Application:
getRequestCycleListeners().add(new AbstractRequestCycleListener() {
#Override
public IRequestHandler onException(RequestCycle cycle, Exception e) {
if(e.getClass().equals(PageExpiredException.class)) {
//check if url in request responds to correct mounted page type
if(isPageUrl(cycle.getRequest().getUrl()))) {
return new RenderPageRequestHandler(new PageProvider(MyPage.class));
} else {
return super.onException(cycle, e);
}
} else {
return super.onException(cycle, e);
}
}
}
This assumes few things - that the page at which you got the exception has been mounted, and that you will be able to parse request url to be sure it is it.
I haven't tested it but we are doing something similar.
I am developing a struts2 application (Struts 2.3.14, java 1.7, on a Tomee plus 1.5.1 server).
I have a bunch of "detail" actions, and all of them contain the following code:
private Long modelId;
public Long getModelId() {
return modelId;
}
public void setModelId(Long modelId) throws Exception {
this.modelId = modelId;
(...some other stuff...)
}
In every action I also have a "persist" action that I use to save data, like this one:
#Action(value = "persistEntity", results = {
#Result(name = "success", location = "entityDetail",
type = "redirectAction", params = {"modelId", "%{modelId}"})
})
public String persist() throws Exception {
this.modelId = [save method invocation]
return "success";
}
After saving I try to redirect the user back to the detail page, but I get the following error:
Unexpected Exception caught setting 'modelId' on 'class classpath.DetailAction':
Error setting expression 'modelId' with value '[Ljava.lang.String;#43b5d2fe'
So, it seesm like Struts is handling my masterId as a String array... I had a look at this question, but it was no help for me.
What is most strange to me, after I get this error I get the same error every time I try to enter an existing entity, but if I restart my application I can enter existing entities without any error.
Thank you!
Well, turns out I was misleaded by Struts2 error message... The framework was correctly trying to set the parameter in my action, but I had an exception thrown by the
(...some other stuff...)
section. I went through the whole log file, and finally found the real issue.
Thank you anyway.