I'm trying to implement a web service using Jersey 2.22.2 and Jetty 9.1.1.v20140108 with exception mapping. The following class represents an Exception class with Mapper implemented.
#Provider
public class NotFoundException extends Exception implements ExceptionMapper<NotFoundException> {
private static final long serialVersionUID = 1L;
public NotFoundException() {
}
public NotFoundException(String s) {
super(s);
}
#Context
private UriInfo uriInfo;
#Override
public Response toResponse(NotFoundException e) {
Status status = Status.NOT_FOUND;
ErrorWrapper errorWrapper = new ErrorWrapper();
errorWrapper.setStatusCode(status.getStatusCode());
errorWrapper.setTitle(status.getReasonPhrase());
errorWrapper.setErrorMessage("The resource you're looking for cannot be found.");
errorWrapper.setApiPath(uriInfo.getAbsolutePath().getPath());
return Response.status(status).entity(errorWrapper).type(MediaType.APPLICATION_JSON).build();
}
}
To test, whether this is working or not, I created an endpoint that simply throws the above exception, like this:
#GET
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public Response test() throws NotFoundException {
throw new NotFoundException();
}
Calling this endpoint returns a JSON, like this:
{
"statusCode": 404,
"title": "Not Found",
"errorMessage": "The resource you're looking for cannot be found.",
"apiPath": "/users/test"
}
From that, I kinda safely assumed that the exception mapping is working.
Now, what I'm trying to do is to throw this exception, if DAO method returns a null object, for example when trying to fetch a database row that doesn't exist yet. Following are my implementation attempts:
DAO:
public User getUserById(Integer id) throws NotFoundException {
try (DSLContext ctx = new DSLContextFactory("iotrest")
.getDSLContext(getDbDataSource("iotrest"))) {
User user = queries.getUserById(ctx, id)
.fetchOne()
.into(User.class);
if (user == null
|| user.getId() == null) {
throw new NotFoundException("User with id " + id + " not found");
}
UserAccessRights userAccessRights = queries.getUserAccessRights(ctx, user.getId())
.fetchOne()
.into(UserAccessRights.class);
if (userAccessRights == null) {
throw new NotFoundException("Access rights not found for user id " + id);
}
setUserAccessRights(user, userAccessRights);
return user;
}
}
Service:
public User getUserById(Integer id) throws NotFoundException {
return userDao.getUserById(id);
}
Resource:
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
public Response getUserById(#PathParam("id") Integer id) throws NotFoundException {
User user = new UserService().getUserById(id);
return Response.ok(user).build();
}
But, when I call the endpoint using an id that doesn't exist yet(2), and get a NullPointerException, I'm still getting a HTTP 500 Request Failed from Jetty, instead of 404 from NotFoundException, like this:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1" />
<title>Error 500 </title>
</head>
<body>
<h2>HTTP ERROR: 500</h2>
<p>Problem accessing /users/2. Reason:
<pre> Request failed.</pre>
</p>
<hr /><i><small>Powered by Jetty://</small></i>
</body>
</html>
Could really use some help with this.
You are not throwing the NotFoundException.
Your code is throwing a NullPointerException.
public User getUserById(Integer id) throws NotFoundException {
try (DSLContext ctx = new DSLContextFactory("iotrest")
.getDSLContext(getDbDataSource("iotrest"))) {
User user = queries.getUserById(ctx, id)
//The NullPointerException is coming from the following line
.fetchOne()
.into(User.class);
if (user == null
|| user.getId() == null) {
throw new NotFoundException("User with id " + id + " not found");
}
UserAccessRights userAccessRights = queries.getUserAccessRights(ctx, user.getId())
.fetchOne()
.into(UserAccessRights.class);
if (userAccessRights == null) {
throw new NotFoundException("Access rights not found for user id " + id);
}
setUserAccessRights(user, userAccessRights);
return user;
}
}
You need to change your code to something like this:
public User getUserById(Integer id) throws NotFoundException {
try (DSLContext ctx = new DSLContextFactory("iotrest")
.getDSLContext(getDbDataSource("iotrest"))) {
User user = queries.getUserById(ctx, id);
if (user == null
|| user.getId() == null) {
throw new NotFoundException("User with id " + id + " not found");
}
user.fetchOne()
.into(User.class);
}
UserAccessRights userAccessRights = queries.getUserAccessRights(ctx, user.getId())
.fetchOne()
.into(UserAccessRights.class);
if (userAccessRights == null) {
throw new NotFoundException("Access rights not found for user id " + id);
}
setUserAccessRights(user, userAccessRights);
return user;
}
}
#galusben's suggestion was instrumental in finding the solution. Clearly, this line was throwing a NPE.
User user = queries.getUserById(ctx, id)
.fetchOne()
.into(User.class);
So, basically what I did was, before lodging the resultset in User, I checked whether the record itself existed or not in the table, like this.
UsersRecord usersRecord = queries.getUserById(ctx, id).fetchOne();
Then, did a null check on that object, and proceed to store record into pojo.
if (usersRecord == null) {
throw new NotFoundException("User with id " + id + " not found");
}
User user = usersRecord.into(User.class);
Tested the endpoint like this:
http://localhost:7000/users/2
The server is now finally returning NotFoundException
{
"statusCode": 404,
"title": "Not Found",
"errorMessage": "The resource you're looking for cannot be found.",
"apiPath": "/users/2"
}
Related
I am trying to test using Mockito
my class under test is
#Service
public class DynatraceAPIServiceImpl implements DynatraceAPIService {
private String apiUrl = "someurl";
private String apiToken = "sometoken";
#Override
public CreateCustomMetricResponse createCustomMetric(CreateCustomMetricRequest request) throws MonitoringException {
logger.info("Inside create custom metric");
if (request == null) {
logger.error("create metric request is null");
throw new MonitoringException("Create metric request is null");
}
String metricId = DynatraceConstants.METRIC_ID;
String displayName = request.getDisplayName();
CreateCustomMetricResponse response = httpUtils.postCustomMetric(apiUrl + "/v1/timeseries/" + metricId, apiToken, request);
if (response == null) {
logger.error("Error in creating custom metric with name : " + displayName);
throw new MonitoringException("Error in creating custom metric with name : " + displayName);
}
logger.info("Custom metric : " + displayName + " is created successfully.");
return response;
}
}
and my Test class is :
#RunWith(MockitoJUnitRunner.class)
public class DynatraceAPIServiceImplTest {
#InjectMocks
DynatraceAPIServiceImpl dynatraceAPIServiceImpl;
#Mock
DynatraceHttpUtils httpUtilsMock;
#Mock
DynatraceMonitoringUtils monitoringUtilsMock;
#Test(expected = MonitoringException.class)
public void createCustomMetricGetsNonNullResponse() throws MonitoringException {
CreateCustomMetricRequest mockRequest = CreateCustomMetricRequest.builder()
.displayName(DISPLAY_NAME)
.types(new String[] {"test-type"})
.build();
CreateCustomMetricResponse response = CreateCustomMetricResponse.builder()
.displayName(DISPLAY_NAME)
.types(new String[] {"test-type"})
.timeseriesId(TIMESERIES_ID)
.build();
boolean val = true;
when(monitoringUtilsMock.isValidMetricIdValue(anyString())).thenReturn(val);
when(httpUtilsMock.postCustomMetric(API_URL + "/v1/timeseries/" + METRIC_ID, API_TOKEN, mockRequest)).thenReturn(response);
CreateCustomMetricResponse actualRespnose = dynatraceAPIServiceImpl.createCustomMetric(mockRequest);
//verify(httpUtilsMock, times(1)).postCustomMetric(anyString(), anyString(), any(CreateCustomMetricRequest.class));
//assertEquals(actualRespnose.getDisplayName(), DISPLAY_NAME);
}
}
Here, when I execute the tests, it always end up having the response value to be null in line
CreateCustomMetricResponse response = httpUtils.postCustomMetric(apiUrl + "/v1/timeseries/" + metricId, apiToken, request);
Even if I have used when() statement to return response as I have created, it is returning null.
Really appreciate if someone can let me know what is wrong here. Thanks.
That normally happens when the params your production code uses differ from the ones that you stubbed the call with, an easy way to find out is to write the test like this
when(httpUtilsMock.postCustomMetric(any(), any(), any())).thenReturn(response);
CreateCustomMetricResponse actualRespnose = dynatraceAPIServiceImpl.createCustomMetric(mockRequest);
verify(httpUtilsMock).postCustomMetric(API_URL + "/v1/timeseries/" + METRIC_ID, API_TOKEN, mockRequest);
If you do that, you'll get a nicer error showing the difference between what your code did and what you verified it for
A better approach in general is to use 'strict stubs' so if your code does anything different to what you stubbed the mock for you'll get a nice error telling you what, where and why
I am running dotnet core 2.* and as the title mentions I have trouble getting my try catch to work when calling from API. And before anyone comments I am also running middle-ware to catch any exceptions. It too doesn't perform as expected
Addinional Information:
The Two Classes are in different namespaces/projects
Queries.Authentication is static.
They are both in the same solution
Controller:
[AllowAnonymous]
[HttpPost]
public string Login([FromBody] AuthRequest req)
{
// See if the user exists
if (Authenticate(req.username, req.password))
{
try {
// Should Fail Below
UserDetails ud = Queries.Authentication.GetUser(req.username);
} catch (RetrievalException e){ }
catch (Exception e){ } // Exception Still Comes Through
}
}
Queries.Authentication.GetUser Code:
public static class Authentication {
public static UserDetails GetUser (string username)
{
// Some Code
if (details.success)
{
// Some Code
}
else
{
throw new RetrievalException(details.errorMessage); // This is not caught propperly
}
}
}
Retrieval Exception:
public class RetrievalException : Exception
{
public RetrievalException()
{
}
public RetrievalException(String message)
: base(message)
{
}
public RetrievalException(String message, Exception inner)
: base(message, inner)
{
}
}
EDIT: Adding Middleware Code Here as per request:
public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
HttpStatusCode status = HttpStatusCode.InternalServerError;
String message = String.Empty;
var exceptionType = context.Exception.GetType();
if (exceptionType == typeof(UnauthorizedAccessException))
{
message = "Unauthorized Access";
status = HttpStatusCode.Unauthorized;
}
else if (exceptionType == typeof(NullReferenceException))
{
message = "Null Reference Exception";
status = HttpStatusCode.InternalServerError;
}
else if (exceptionType == typeof(NotImplementedException))
{
message = "A server error occurred.";
status = HttpStatusCode.NotImplemented;
}
else if (exceptionType == typeof(RSClientCore.RetrievalException))
{
message = " The User could not be found.";
status = HttpStatusCode.NotFound;
}
else
{
message = context.Exception.Message;
status = HttpStatusCode.NotFound;
}
context.ExceptionHandled = true;
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)status;
response.ContentType = "application/json";
var err = "{\"message\":\"" + message + "\",\"code\" :\""+ (int)status + "\"}";
response.WriteAsync(err);
}
}
App Config:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} else
{
app.UseExceptionHandler();
}
...
}
Service Config:
public void ConfigureServices(IServiceCollection services)
{
// Add Model View Controller Support
services.AddMvc( config =>
config.Filters.Add(typeof (CustomExceptionFilter))
);
UPDATE: After playing around with it I noticed that even though my program throws the exception, if I press continue the API controller then handles it as if the exception was never thrown (as in it catches it and does what I want). So I turned off the break on Exception setting, this fixed it in debugger mode. However this the break doesn't seem to be an issue when I build/publish the program. This makes me think it is definitely a issue with visual studio itself rather than the code.
When you set ExceptionHandled to true that means you have handled the exception and there is kind of no error anymore. So try to set it to false.
context.ExceptionHandled = false;
I agree it looks a bit confusing, but should do the trick you need.
Relevant notes:
For those who deal with different MVC and API controller make sure you implemented appropriate IExceptionFilter as there are two of them - System.Web.Mvc.IExceptionFilter (for MVC) and System.Web.Http.Filters.IExceptionFilter (for API).
There is a nice article about Error Handling and ExceptionFilter Dependency Injection for ASP.NET Core APIs you could use as a guide for implementing exception filters.
Also have a look at documentation: Filters in ASP.NET Core (note selector above the left page menu to select ASP.NET Core 1.0, ASP.NET Core 1.1,ASP.NET Core 2.0, or ASP.NET Core 2.1 RC1). It has many important notes and explanations why it works as it does.
I want to pass a json object for the update function but its doesn't accept the json object and get an error. The error is:
the code is:
(value = "/UpdateUser/", method = RequestMethod.PUT , consumes=MediaType.APPLICATION_JSON_VALUE)
public void UpdateUser(JSONObject RequiredObject)throws UnknownHostException {
// RequiredObject=new HashMap<>();
System.out.println("hello into update " + RequiredObject);
// readJSON.UpdateUser(RequiredObject);
}
you have to receive the body of your request as a #RequestBody and you can receive this json object as a User object directly
#RequestMapping(value = "/UpdateUser/", method = RequestMethod.PUT ,
consumes=MediaType.APPLICATION_JSON_VALUE)
public void UpdateUser(#RequestBody User user) throws UnknownHostException {
// RequiredObject=new HashMap<>();
System.out.println("hello into update " + RequiredObject);
//readJSON.UpdateUser(RequiredObject);
}
I have a jersey server. When I attempt to handle a POST from AngularJS (1.2.16), it is generating an error (below). When I use a java jersey client to post the message, the jersey server handles it fine.
SEVERE: null
java.lang.IllegalAccessException: Class com.sun.jersey.server.wadl.generators.WadlGeneratorJAXBGrammarGenerator$8 can not access a member of class javax.ws.rs.core.Response with modifiers "protected"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:101)
at java.lang.Class.newInstance(Class.java:427)
this is the jersey post server:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("/post")
public Response verifyAccount( Owner owner ,
#Context HttpServletRequest req)
{
LOGGER.debug("verify account " +owner.toString() );
HashMap<String, Object> results = new HashMap<String, Object>();
boolean verified = AccountManagement.verifyAccount(owner.getEmail(),
owner.getPwd());
if (verified) {
results.put("status", "OK");
} else {
results.put("status", "Fail");
}
return Response.status(200).entity(results)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
.build();
}
This is the jersey java client:
public class JsonClient {
public static void main(String[] args) {
try {
Client client = Client.create();
WebResource webResource = client
.resource("http://myserver.com:8080/restws/accountcheck/post");
String input = "{\"email\":\"fubar#gmail.com\",\"pwd\":\"hello\"}";
ClientResponse response = webResource.type("application/json")
.post(ClientResponse.class, input);
int code = response.getStatus();
if (code != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus());
}
System.out.println("Output from Server .... \n");
String output = response.getEntity(String.class);
System.out.println(output);
} catch (Exception e) {
System.out.println("exception caught.");
e.printStackTrace();
}
}
}
This is the AngularJS code to post:
$scope.ownerLoginAction = function() {
var dataObject = {
"email": $scope.myId,
"pwd": $scope.mypassword
};
var request = $http({
method: "post",
url: hostName+'/restws/accountcheck/post',
params: {
action:"verify"
},
data: dataObject
});
request.then (function(response) {
console.log(response.data);
},function(errResponse) {
console.error('Error');
} )
}
Anybody know why I cannot seem to post either with JSON from AngularJS? Is the server not set up right? Or the angularJS client is not right?
When i put a TCPMON in between, I noticed that the angularJS attempt sent an OPTION. Is that a clue that I dont understand?
How can I change/update the following REST call from Spring MVC to return a error if the user did not enter of the the two names I was coding for.. something like a not found?
#RequestMapping(value = "/{name}", method = RequestMethod.GET)
#ResponseBody
public User getName(#PathVariable String name, ModelMap model)
{
logger.debug("I am in the controller and got user name: " + name);
/*
Simulate a successful lookup for two users. This
is where your real lookup code would go.
*/
if ("name2".equals(name))
{
return new User("real name 2", name);
}
if ("name1".equals(name))
{
return new User("real name 1", name);
}
return null;
}
Define a new exception class, e.g. ResourceNotFoundException and throw an instance of this from your annotated controller method getName.
Then also define an annotated exception handler method in your Controller class to handle that exception, and return a 404 Not Found status code, potentially logging it.
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public void handleResourceNotFoundException(ResourceNotFoundException ex)
{
LOG.warn("user requested a resource which didn't exist", ex);
}
Or even returning some error message, using #ResponseBody annotation:
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ResponseBody
public String handleResourceNotFoundException(ResourceNotFoundException ex)
{
return ex.getMessage();
}
You could create an object specifically for returning error responses. That way, you can say whatever you want. For example:
#ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ResponseStatus> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex){
ResponseStatus responseStatus = new ResponseStatus("400", "Bad Request. " + ex);
responseStatus.setResponseStatusTime(timestampService.createTimestamp());
HttpStatus status = HttpStatus.BAD_REQUEST;
ResponseEntity<ResponseStatus> response = new ResponseEntity<ResponseStatus>(responseStatus, status);
return response;
}
In this example you can see that there is a ResponseStatus object. This object contains a field for a status code, status message, date and time. You may not need date and time but I find it useful for when someone sends me an error they have seen, because then it is easy to track down exactly where it happened in our server logs.