RxJava - error handling in subjects - mvvm

I'm trying to implement a ViewModel in MVVM, providing observables as "input stream" and observer as "output stream" for view to bind.
The code below seems to work if getUser() call succeeds, but throws an uncaught exception if fails:
public class ViewModel {
private final BehaviorSubject<String> query;
private final BehaviorSubject<User> result;
public ViewModel() {
result = BehaviorSubject.create();
query = BehaviorSubject.create();
query.flatMap(s -> getUser(s)).subscribe(result);
// result = result.doOnError(e -> onError(e)); // does not work
}
Observable<String> login() {
return result.map(user -> user.getLogin());
}
Observable<String> blog() {
return result.map(user -> user.getBlog());
}
public Observer<String> queryObserver() {
return query;
}
private void onError(Throwable e) {
Log.e(TAG, e.getMessage());
}
}
How can I get onError() method executed in case of error, without pushing anything to login and blog observables?

doOnError() is a "side-action" operator, it doesn't catch the error. Should be something like:
query.flatMap(s -> getUser(s)).subscribe(result::onNext, this::onError);

Related

RxJava Problem with reading a file with Observable and take operator

My working environment is JDK 1.6 and RxJava 2
I want to make an Observable which emits an item that is a file line string read via BufferedReader as follows:
...
Observable<String> fileLineObservable = Observable.defer(new Callable<String>(){
return new ObservableSource<String> call() throws Exception {
return new ObservableSource<String>() {
public void subscribe(Observer<String> observer) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(filePath));
String line = null;
while ((line = reader.readLine()) != null) {
observer.onNext(line);
}
observer.onComplete();
... catching exception and close reader
}
}
}
}
});
I also want to make an Observer that observes the above Observable with one take(count) operator as follows:
fileLineObservable.take(2)
.subscribe(new Consumer<String>() {
public void onNext(String line) {
... do something with the file line string
}
});
I meet NullPointerException when executing the above code and I know why. The NPE is caused by that the second call of onNext leads to execute onComplete on the TakeObserver instance and inside the onComplete method, upstream.dispose that is not set(null) is called. The upstream variable of TakeObserver is supposed to be set with onSubscribe(Disposable disposable) when it subscribes an Observable.
How can I solve this problem? Should I implement my own Disposable class to set the upstream of TakeObserver?
What about this solution?
Observable<String> observableFile2(Path path) {
return Observable.using(
() -> Files.newBufferedReader(path),
reader -> {
return Observable.fromIterable(() -> {
return new Iterator<>() {
private String nextLine = null;
#Override
public boolean hasNext() {
try {
nextLine = reader.readLine();
return nextLine != null;
} catch (Exception ex) {
return false;
}
}
#Override
public String next() {
if (nextLine != null) {
return nextLine;
}
throw new IllegalStateException("nextLine can not be null.");
}
};
});
},
BufferedReader::close
);
}
Observable#using makes sure, that the BufferedReader is closed properly on disposable / onError
Observable#fromIterable wraps the readLine calls and handles onComplete for us.
Testing
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.6.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.2")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.6.2")
testImplementation("com.google.jimfs:jimfs:1.1")
Tests
#Test
void name() {
observableFile2(hello).take(2)
.test()
.assertValues("line0", "line1")
.assertComplete();
}
#Test
void name2() {
observableFile2(hello).take(10)
.test()
.assertValues("line0", "line1", "line2", "line3")
.assertComplete();
}
#Test
void name3() {
observableFile2(hello2)
.test()
.assertComplete();
}

Can we throw an exception in fallback or fallbackFactory of #FeignClient

I'm use the #FeignClient and want to do some logic(like record the exception information) when Feign throw Exception and then reply the result to front end.
I noticed Feign will throw FeignException when connection fail or http status not expect.
So I defined a #ExceptionHandler to caught FeignException after the callback method was invoked.
#ExceptionHandler(value = FeignException.class)
#ResponseBody
public ResponseResult feignException(FeignException exception){
String message = exception.getMessage();
byte[] content = exception.content();
int status = exception.status();
if(content!=null){
String response=new String(content);
message=String.format("%s response message : %s",message,response);
}
log.warn("{} : {} , cause by : {}",exception.getClass().getSimpleName(),message,exception.getCause());
return ResponseResult.fail(HttpStatus.valueOf(status),String.format("9%s00",status),message);
But it can't caught when I set the callback or callbackFactory of #FeignClient.
#FeignClient(url = "${onboardingcase.uri}",name = "OnBoardingCaseService",
fallbackFactory = OnBoardingCaseServiceFallBack.class)
#Component
#Slf4j
public class OnBoardingCaseServiceFallBack implements FallbackFactory<OnBoardingCaseService> {
#Override
public OnBoardingCaseService create(Throwable throwable) {
return new OnBoardingCaseService() {
#Override
public OnBoardingCaseVo query(String coid) {
if(throwable instanceof FeignException){
throw (FeignException)throwable;
}
return null;
}
};
}
}
I noticed because hystrix took over this method.And will catch exception in HystrixInvocationHandler.
try {
Object fallback = HystrixInvocationHandler.this.fallbackFactory.create(this.getExecutionException());
Object result = ((Method)HystrixInvocationHandler.this.fallbackMethodMap.get(method)).invoke(fallback, args);
if (HystrixInvocationHandler.this.isReturnsHystrixCommand(method)) {
return ((HystrixCommand)result).execute();
} else if (HystrixInvocationHandler.this.isReturnsObservable(method)) {
return ((Observable)result).toBlocking().first();
} else if (HystrixInvocationHandler.this.isReturnsSingle(method)) {
return ((Single)result).toObservable().toBlocking().first();
} else if (HystrixInvocationHandler.this.isReturnsCompletable(method)) {
((Completable)result).await();
return null;
} else {
return HystrixInvocationHandler.this.isReturnsCompletableFuture(method) ? ((Future)result).get() : result;
}
} catch (IllegalAccessException var3) {
throw new AssertionError(var3);
} catch (ExecutionException | InvocationTargetException var4) {
throw new AssertionError(var4.getCause());
} catch (InterruptedException var5) {
Thread.currentThread().interrupt();
throw new AssertionError(var5.getCause());
}
So I want to know how can I throw an exception when I using callback / callbackFactory or there is another way to instead callbackFactory to do the "call back"?
Many Thanks
I found a solution to this problem.
public class OnBoardingCaseServiceFallBack implements FallbackFactory<OnBoardingCaseService> {
#Override
public OnBoardingCaseService create(Throwable throwable) {
return new OnBoardingCaseService() {
#Override
public OnBoardingCaseVo query(String coid) {
log.error("OnBoardingCaseService#query fallback , exception",throwable);
if(throwable instanceof FeignException){
throw (FeignException)throwable;
}
return null;
}
};
}
}
And then caught the HystrixRuntimeException and get the cause of exception in ExceptionHandler for get the realException that was wrapped by Hystrix.
#ExceptionHandler(value = HystrixRuntimeException.class)
#ResponseBody
public ResponseResult hystrixRuntimeException(HystrixRuntimeException exception){
Throwable fallbackException = exception.getFallbackException();
Throwable assertError = fallbackException.getCause();
Throwable realException = assertError.getCause();
if(realException instanceof FeignException){
FeignException feignException= (FeignException) realException;
String message = feignException.getMessage();
byte[] content = feignException.content();
int status = feignException.status();
if(content!=null){
String response=new String(content);
message=String.format("%s response message : %s",message,response);
}
return ResponseResult.fail(HttpStatus.valueOf(status),String.format("9%s00",status),message);
}
String message = exception.getMessage();
log.warn("{} : {} , cause by : {}",exception.getClass().getSimpleName(),message,exception.getCause());
return ResponseResult.fail(ResultCode.FAIL.httpStatus(),ResultCode.FAIL.code(),message);
}
But I don't think that's a good way~
I have never done this in fallback, I have implemented custom error decoder(“CustomFeignErrorDecoder”) class and extended feign.codec.ErrorDecoder, every time an error occurs it comes to this class.
In decode function throw a custom exception and catch it in the controller or service layer to show your message to the frontend.
Example:
#Component
public class CustomFeignErrorDecoder implements ErrorDecoder {
#Override
public Exception decode(String methodKey, Response response) {
throw new CustomFeignErrorDecoderException(methodKey +" response status "+ response.status() +" request "+ response.request()+ " method "+ response.request().httpMethod());
}
}

Migrating EJB 2.0 to EJB 3.x Web app forgets lower tier value

I'm currently trying to migrate an EAR Project
-The old Project-
EJB 2.0
Jboss 5.0.1
-New Project-
EJB 3.0
Wildfly 13.0.0Final
Its session beans which I have managed to create and I can call upon it. the logic in it self seems to work. The issues I'm having is one of the follow ups seems to forget what it was doing.
the current issue I'm seeing is the following:
In the Web application in an class we are creating an object of Another class which we then trigger its parent method. this parent method then calls upon a class with it self as an argument which then checks which type it is and then stars a session bean depending on what type. which then calls on the arguments function performExecute() in this function we call upon a Query and actually get the correct results, we then add the resultsets values to a private dto member. and then performExecute is done. and we are back at the web applications class and we then try to access the same dto member with a get function. this returns a nullpointer. I'm wondering if I've forgotten something in my session beans?
Old sessionbean:
public class TxNotSupportedCommandServerBean implements SessionBean {
SessionContext sessionContext;
public void ejbCreate() throws CreateException {}
public void ejbRemove() {
sessionContext = null;
}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sessionContext) {
this.sessionContext = sessionContext;
}
public void executeCommand(TargetableCommand cmd) throws CommandException {
try {
cmd.performExecute();
}
catch (CommandException ex) {
throw ex;
}
}
}
The new one:
#Stateless
#Remote
#TransactionManagement(value=TransactionManagementType.CONTAINER)
#TransactionAttribute(value=REQUIRED)
public class TxNotSupportedCmdServerBean implements TxNotSupportedCmdServerRemote{
/**
* Default constructor.
*/
public TxNotSupportedCmdServerBean() {
// TODO Auto-generated constructor stub
}
public void executeCommand(TargetableCommand cmd) throws CommandException {
try {
cmd.performExecute();
}
catch (CommandException ex) {
throw ex;
}
}
}
Both these are in the EJB.Jar
the interface is implemented in the EJBClient.jar
The old Interface:
public interface TxNotSupportedCommandServerLocal extends EJBLocalObject {
public void executeCommand(TargetableCommand cmd) throws CommandException;
}
The new Interface:
public interface TxNotSupportedCmdServerRemote {
public void executeCommand(TargetableCommand cmd) throws CommandException;
}
Now here come s the next set of files that is also in the EJBClient.jar
TargetableCommand:
public abstract class TargetableCommand implements Command {
private boolean constraintViolated;
protected RequestContext requestContext;
protected String dataSourceName;
public TargetableCommand(RequestContext requestContext, String dataSourceName) {
this.requestContext = requestContext;
this.dataSourceName = dataSourceName;
}
public TargetableCommand(RequestContext requestContext) {
this.requestContext = requestContext;
}
public TargetableCommand(String dataSourceName) {
this.dataSourceName = dataSourceName;
}
public TargetableCommand() {
}
public void setConstraintViolated(boolean constraintViolated) {
this.constraintViolated = constraintViolated;
}
public boolean isConstraintViolated() {
return constraintViolated;
}
public abstract void performExecute() throws CommandException;
public void execute() throws CommandException {
CommandTarget.executeCommand(this);
}
}
Command:
public interface Command extends Serializable {
public void execute() throws CommandException;
}
The commented code is the old session beans.
CommandTarget:
public class CommandTarget {
public CommandTarget() {
}
/**
* Exekverar ett kommando i rätt miljö, t.ex. med eller utan transaktionshantering
* #param cmd TargetableCommand Kommandot som ska utföras
* #throws CommandException
*/
public static void executeCommand(TargetableCommand cmd) throws CommandException {
Context context = null;
try {
ServiceLocator sl = ServiceLocator.getInstance();
// if (cmd instanceof TxRequired) {
// TxRequiredCommandServerLocalHome cmdSrvHome = (TxRequiredCommandServerLocalHome) sl.getEJBLocalHome("TxRequiredCommandServer");
// TxRequiredCommandServerLocal cmdSrv = cmdSrvHome.create();
// cmdSrv.executeCommand(cmd);
// }
// else if(cmd instanceof TxNotSupported) {
// TxNotSupportedCommandServerLocalHome cmdSrvHome = (TxNotSupportedCommandServerLocalHome) sl.getEJBLocalHome("TxNotSupportedCommandServer");
// TxNotSupportedCommandServerLocal cmdSrv = cmdSrvHome.create();
// cmdSrv.executeCommand(cmd);
// }
// else {
// throw new CommandException("Cannot instanciate command server");
// }
//
System.out.println("CT: Inside commandTarget. about to diffrientate what instance");
context = JNDILookupClass.getInitialContext();
if (cmd instanceof TxRequired) {
System.out.println("CT: TxRequired");
TxRequiredCmdServerRemote cmdSrv = (TxRequiredCmdServerRemote)context.lookup(JNDILookupClass.getLookupName("TxRequiredCmdServerRemoteBean", TxRequiredCmdServerRemote.class.getName()));
cmdSrv.executeCommand(cmd);
}
else if(cmd instanceof TxNotSupported) {
System.out.println("CT: TxNotSupported");
System.out.println("CT: cmd: " + cmd.getClass());
TxNotSupportedCmdServerRemote cmdSrv = (TxNotSupportedCmdServerRemote)context.lookup(JNDILookupClass.getLookupName("TxNotSupportedCmdServerBean", TxNotSupportedCmdServerRemote.class.getName()));
cmdSrv.executeCommand(cmd);
}
else {
throw new CommandException("Cannot instanciate command server");
}
}
catch (CommandException ex) {
throw ex;
}
// catch (CreateException ex) {
// throw new CommandException(ex);
// }
//new catch
catch(NamingException ex) {
throw new CommandException(ex);
}
catch (ServiceLocatorException ex) {
throw new CommandException(ex);
}
}
}
Phew ...
Ok now that's the important parts from EJBClient. now onwards to the Web.war
I'm only pasting the part that actually runs and were it returns a nullpoint
public class ActionIdentitetKonsultCommand implements Command {
private static Logger logger = Logger.getLogger(ActionIdentitetKonsultCommand.class);
public ActionIdentitetKonsultCommand() {
}
public String execute(RequestContext requestContext) throws CommandException {
GetPersonByPersnrEJBCommand personCmd;
logger.info("execute()");
try {
UserBean user = (UserBean) requestContext.getSession().getAttribute("user");
String kstnr = requestContext.getParameter("kstnr");
//Tilldela konsultuppgifter
personCmd = new GetPersonByPersnrEJBCommand();
personCmd.setPersnr(user.getPersnr());
System.out.println("AI: Before execute DTO " + personCmd.dto);
personCmd.execute();
System.out.println("AI: After execute DTO " + personCmd.dto);
logger.info("person hamtad med personnummer (EJB):");
logger.info(personCmd.getPerson().toString());
So the personCmd.getPerson().tostring() is what causes the nullpointer. GetPersonByPersnrEJBCommand():
public class GetPersonByPersnrEJBCommand extends TargetableCommand implements TxNotSupported {
public PersonDTO dto;
private long persnr;
public GetPersonByPersnrEJBCommand() {
}
public void setPersnr(long persnr) {
this.persnr = persnr;
}
public PersonDTO getPerson() {
return this.dto;
}
public void performExecute() throws CommandException {
try {
QueryPersonByPersnrCommand cmd = new QueryPersonByPersnrCommand();
cmd.setPersnr(persnr);
cmd.execute();
if(cmd.next()){
this.dto = new PersonDTO();
System.out.println("GP: inside PerformExecute DTO: " + dto);
dto.setPersnr(cmd.getPersnr());
dto.setEfternamn(cmd.getEfternamn());
dto.setFornamn(cmd.getFornamn());
dto.setEpostAdress(cmd.getEpostAdress());
dto.setKonsult((cmd.getKonsult() == 1));
dto.setAnsvarig((cmd.getAnsvarig() == 1));
System.out.println("GP: Inside Perform Execute DTO: " + dto);
}
}
catch (DataAccessCommandException ex) {
System.out.println("GetPersonByPersnrEJBCommand.performExecute misslyckades " + ex.getMessage());
throw new CommandException(ex);
}
}
}
So that's it; I don't understand why it forgets it. when we do sysouts inside the last class we see that both the dto and the cmd has data in them, but once the function ends and we are back in the class that called on this the data is empty.
I'm suspecting its something to do with my session beans, I'm missing an property or something. because this code works with the old beans in the old JBOSS server. Hopefully someone can help me and others can learn from this as well as me.
I managed to solve this issue. Since the scope of the project is to get this to work. Its not a beautiful solution and with more time rewriting this would have been better. so onwards to the solution:
We need to change in both the bean, targetableCommand, CommandTarget and in the GetPersonByPersnrEJBCommand
TargetableCommand - add method:
public TargetableCommand execute(TargetableCommand cmd) throws CommandException
{
return CommandTarget.executeCommand(cmd);
}
CommandTarget - We change the method executeCommand to return a TargetableCommand, and make sure that after the bean is done we return that cmd.
public static TargetableCommand executeCommand(TargetableCommand cmd) throws CommandException {
Context context = null;
try {
context = JNDILookupClass.getInitialContext();
if (cmd instanceof TxRequired) {
TxRequiredCmdServerRemote cmdSrv = (TxRequiredCmdServerRemote)context.lookup(JNDILookupClass.getLookupName("TxRequiredCmdServerRemoteBean", TxRequiredCmdServerRemote.class.getName()));
cmd = cmdSrv.executeCommand(cmd);
}
else if(cmd instanceof TxNotSupported) {
TxNotSupportedCmdServerRemote cmdSrv = (TxNotSupportedCmdServerRemote)context.lookup(JNDILookupClass.getLookupName("TxNotSupportedCmdServerBean", TxNotSupportedCmdServerRemote.class.getName()));
cmd = cmdSrv.executeCommand(cmd);
}
else {
throw new CommandException("Cannot instanciate command server");
}
}
catch (CommandException ex) {
throw ex;
}
catch(NamingException ex) {
throw new CommandException(ex);
}
}
return cmd;
}
The bean - cange the method Execute command to return Targetablecommand
public TargetableCommand executeCommand(TargetableCommand cmd) throws CommandException {
try {
cmd = cmd.performExecute();
}
catch (CommandException ex) {
throw ex;
}
return cmd;
}
Then lastly to get it all to work I had to create a new method in the classes that needed to do the perform execute so in the GetPersonByPersnrEJBCommand class i created the method wf13Layer(); wich is a just an extra step:
public void wf13Layer() throws CommandException
{
GetPersonByPersnrEJBCommand tmp;
try{
tmp = (GetPersonByPersnrEJBCommand) execute(this);
dto = tmp.getPerson();
} catch (Exception ex) {
throw new CommandException(ex);
}
}
This is what i did to make it work. as i said its not a beautiful solution but it works. IT seems to be a combination that once we cross between the projects the scope vanishes. and to obtain it futher we need to layer it like this. I really hope this helps someone at some point since theres till alot of old code running around out there.
Kind regards
VeryTired

Test exception of a method which contains try catch in junit

I have code snippet below.
What I want is if getNames() method catch an exception
( ex. InterruptedException ),
want to check if Got InterruptedException !!! prints out or not.
There are some examples of testing exception for a method
which throws an exception in its method ( ex. String method1() throws InterruptedException {...} ) in the Internet.
But not this case. Does anyone have some thought or idea?
public class A {
public List<String> getNames()
{
String addess = "address1";
int age = 17;
List<String> names = null;
try {
names = getSomeNames(address, sex);
}
catch (InterruptedException | ExecutionException e) {
throw new MyCustomException(e);
}
catch(Exception e) {
throw new MyCustomException(e);
}
return names;
}
List<String> getSomeNames(String address, int sex) throws InterruptedException, ExecutionException
{
// ...
// throw exceptions... at some point
//
return names;
}
}
public class MyCustomException extends Exception {
public MyCustomException(Throwable e) {
if (e.getCause() instanceof InterruptedException) {
// write log
System.out.println("Got InterruptedException !!!");
}
else if (e.getCause() instanceof ExecutionException) {
// write log
System.out.println("Got ExecutionException!!!");
}
else {
// write log
}
}
}
I tried this but the test failed and got NullPointerException in catch block.
#Test
public void testException() {
A objA = spy(new A());
try {
doThrow(MyCustomException.class).when(objA).getNames();
objA.getNnames();
}
catch (Exception e) {
System.out.println(e.getCause().toString()); // ==> throws java.lang.NullPointerException here.
}
}
There are several ways to test it.
First solution is to replace System.out with different stream and read from it later. ( I don't like this approach )
#Test
void whenSayHi_thenPrintlnCalled() throws IOException {
PrintStream normalOutput = System.out;
String result;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream temporalOutput = new PrintStream(baos)) {
System.setOut(temporalOutput);
ThatGuy thatGuy = new ThatGuy();
thatGuy.sayHi();
result = new String(baos.toByteArray(), StandardCharsets.UTF_8);
} finally {
System.setOut(normalOutput);
}
assertEquals("Hi", result.trim());
}
Second one is to use logger instead of just System.out. I consider this approach better not only from testing, but from code design perspective as well. Using this one you can just replace logger with Mockito.mock and user Mockito.verify to check what was called on your logger.
#Test
void whenSayHi_thenCallLogger() {
Logger logger = Mockito.mock(Logger.class);
ThatGuy thatGuy = new ThatGuy();
ReflectionTestUtils.setField(thatGuy, "logger", logger);
thatGuy.sayHiToLog();
verify(logger).error("Hi");
}
Class under testing looks like this:
class ThatGuy {
private static Logger logger = LoggerFactory.getLogger(ThatGuy.class);
void sayHi() {
System.out.println("Hi");
}
void sayHiToLog() {
logger.error("Hi");
}
}

MVC 2.0 - Custom handling of all errors to return json

I have an MVC 2 app that I want all requests to return json. I have overridden a HandleErrorAttribute and an AuthorizeAttribute. My goal is that all errors (even 403 and 404) are returned as json.
Here is my error handler. ExceptionModel is a simple class defining any error returned by my application. The Exception handler is a class that translates the error details into a formatted e-mail and sends it to me.
public class HandleErrorJsonAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
context.ExceptionHandled = true;
RaiseErrorSignal(context.Exception);
context.RequestContext.HttpContext.Response.ContentType = "application/json";
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(context.HttpContext.Response.Output, new ExceptionModel(context.Exception));
}
private static void RaiseErrorSignal(Exception ex)
{
IExceptionHandler handler = Resolve();
handler.HandleError(ex.GetBaseException());
}
private static IExceptionHandler Resolve()
{
return ServiceLocator.Locate<IExceptionHandler>();
}
}
Here is the Exception model for clarification
public class ExceptionModel
{
public int ErrorCode { get; set; }
public string Message { get; set; }
public ExceptionModel() : this(null)
{
}
public ExceptionModel(Exception exception)
{
ErrorCode = 500;
Message = "An unknown error ocurred";
if (exception != null)
{
if (exception is HttpException)
ErrorCode = ((HttpException)exception).GetHttpCode();
Message = exception.Message;
}
}
public ExceptionModel(int errorCode, string message)
{
ErrorCode = errorCode;
Message = message;
}
}
and finally, my custom authorize attribute. I an using forms auth, but I did not want any of the automatic redirection. I simply want the error to show on the screen and stop any further processing.
public class AuthorizeTokenAttribute : System.Web.Mvc.AuthorizeAttribute
{
public bool SuperAdminOnly { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool authorized = base.AuthorizeCore(httpContext);
if(!SuperAdminOnly)
return authorized;
if(!authorized)
return authorized;
return SessionHelper.UserIsSuperAdmin(httpContext.User.Identity.Name);
}
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
throw new HttpException(403, "Access Denied");
}
}
This all works great for most errors, but it is missing one thing. I have a controller action like this.
[AuthorizeToken]
[HttpPost]
public JsonResult MyAction()
{
return new JsonResult();
}
It works fine when you submit via post, but on a get I receive an unhandled 404 error.
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource
you are looking for (or one of its
dependencies) could have been removed,
had its name changed, or is
temporarily unavailable. Please
review the following URL and make sure
that it is spelled correctly.
Requested URL: /MyController/MyAction
Version Information: Microsoft .NET
Framework Version:4.0.30319; ASP.NET
Version:4.0.30319.1
This happens on a GET, which is to be expected as default behavior. However, how can I handle for this condition so that I could instead return json like this
{"ErrorCode":404,"Message":"Page Not Found"}
To handle errors personally I prefer the Application_Error event in Global.asax:
protected void Application_Error(object sender, EventArgs e)
{
var exception = Server.GetLastError();
Response.Clear();
Server.ClearError();
var httpException = exception as HttpException;
var routeData = new RouteData();
routeData.Values["controller"] = "Errors";
routeData.Values["action"] = "Index";
routeData.Values["error"] = exception;
IController errorController = new ErrorsController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
and then have an ErrorsController:
public class ErrorsController : Controller
{
public ActionResult Index(Exception exception)
{
var errorCode = 500;
var httpException = exception as HttpException;
if (httpException != null)
{
errorCode = httpException.ErrorCode;
}
return Json(new
{
ErrorCode = errorCode,
Message = exception.Message
}, JsonRequestBehavior.AllowGet);
}
}