Обработка ошибок дооснащения

Я завернул свой код модернизации в класс, как показано ниже. Если из кода, который я публикую, неясно, он взаимодействует с успокоительной службой с помощью OAuth.

Что было бы хорошим способом обработки ошибок? Сервер REST возвращает сообщение об ошибке в формате json. Я хотел бы действовать в соответствии с этим сообщением, выбрасывая некоторые исключения из моего класса. Я пытаюсь сделать что-то вроде ниже. Но хороший ли это дизайн? Является ли смешивание обратных вызовов и генерации исключений хорошей идеей? Есть ли способ лучше?

Используя описанный ниже подход, я мог получать сообщения i18l из своих пользовательских исключений и отправлять их пользователю.

public class RestClient implements IRestClient {
    private IRestAPI api;

    /**
     *
     * @param accessToken
     */
    public RestClient(final String accessToken)
    {
        RequestInterceptor requestInterceptor = new RequestInterceptor()
        {
            @Override
            public void intercept(RequestFacade request) {
                request.addHeader("Authorization", "Bearer " + accessToken);
            }
        };

        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(Config.ENDPOINT)
                .setRequestInterceptor(requestInterceptor)
                .build();
        api = restAdapter.create(IRestAPI.class);
    }

    @Override
    public void requestSomething(final Callback callback) {
        api.getSomething(new Callback<Something>() {
            @Override
            public void success(Something something, Response response) {
                callback.success(something, response);
            }

            @Override
            public void failure(RetrofitError error) {
                if(error.getMessage().getId().euqals(ACCESS_TOKEN_EXPIRED))
                {
                    throw new AccessTokenExpired();
                }
                else if(error.getMessage().getId().euqals(USER_NOT_FOUND))
                {
                    throw new UsernamePasswordNotFound();
                }
                else // something else happened...
                {
                    throw error;
                }
            }
        });
    }

    @Override
    public void deleteSomething(final Callback callback) {
        api.deleteSomething(new Callback<Something>() {
            @Override
            public void success(Something something, Response response) {
                callback.success(something, response);
            }

            @Override
            public void failure(RetrofitError error) {
                if(error.getMessage().getId().euqals(SOMETHING_NOT_FOUND))
                {
                    ...
                    ...
                    Different exceptions
                }
                ...
            }
        });
    }

}

Естественно, мне пришлось бы создать свой собственный интерфейс обратного вызова только с методом успеха.


person user672009    schedule 01.11.2014    source источник
comment
Вы всегда можете использовать ErrorHandler Retrofit в построителе RestAdapter. square.github.io/retrofit/javadoc/retrofit/ErrorHandler.html   -  person daentech    schedule 01.11.2014


Ответы (1)


Когда вы создаете RestAdapter, вы можете предоставить обработчик ошибок, который отображает к вашим пользовательским исключениям, он обходит вызов failure в Callback<T> на чем-либо 4xx/5xx. Как действительно надуманный пример:

public class Scratch {
    public static void main(String[] args) {
        Endpoints e = new RestAdapter.Builder()
                .setEndpoint("http://google.com")
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .setErrorHandler(new ErrorHandler() {
                    @Override
                    public Throwable handleError(RetrofitError cause) {
                        switch (cause.getResponse().getStatus()) {
                            case 400:
                                /* Handle the expected body format */
                                cause.getBody();
                                throw new RuntimeException("Bad Request");
                            default:
                                /* Things and stuff */
                                throw new RuntimeException("");
                        }
                    }
                })
                .build()
                .create(Endpoints.class);

        e.getGoogle(new Callback<Response>() {
            @Override
            public void success(Response response, Response response2) {
                System.out.println("Got it");
            }

            @Override
            public void failure(RetrofitError error) {
                System.err.println("This won't ever be seen due to the error handler.");
            }
        });
    }

    private static interface Endpoints {
        @GET("/foo/bar")
        void getGoogle(Callback<Response> callback);
    }
}

редактировать: тем не менее, делая это, вы потенциально жертвуете серьезной причиной, по которой вы хотели бы использовать интерфейс Callback для начала. Если это обычное использование, которое вам понадобится, может иметь смысл использовать вызовы синхронизации и возвращать тип вашего объекта. Я не совсем понимаю, как вы говорите, что это необходимо, но кажется, что это может быть более подходящим.

person nerdwaller    schedule 01.11.2014
comment
Да, но мне нужны разные исключения для разных методов... или, по крайней мере, я так думаю. Я не слишком уверен в этом подходе полностью. Скажем, у меня есть метод createSomething. Это может сгенерировать исключение ужеExistsException. Если у меня есть метод deleteSomething, он может вызвать исключение SomethingNotFoundException... имеет смысл? - person user672009; 01.11.2014
comment
Я правда так не думаю, по крайней мере на этом уровне. Любая HTTP-служба, которую вы используете, должна использовать стандартные коды ошибок (или иметь некоторый стандарт, сообщающий вам, почему запрос не удался). Таким образом, в примере switch в моем ответе и использовании вашего комментария в качестве шаблона вы должны сопоставить 409 с вашим ConflictException, 404 с вашим NotFoundException в качестве кода состояния: отображение исключения. Когда вы вызываете что-то, что дает 4xx/5xx, независимо от того, что это такое, вы получите обратно стандартное исключение. - person nerdwaller; 01.11.2014
comment
Конечно, отрицательная сторона этого заключается в том, что вы жертвуете частью приятного характера асинхронного вызова, добавляя другой код, если вы ожидаете, что это будет обычным делом - возможно, использование интерфейса Callback - неправильный подход для ваше использование. - person nerdwaller; 01.11.2014
comment
Ну, дело в том, что я могу получить 404, не имея возможности найти пользователя и статью. Но я хочу разные сообщения об ошибках. Кроме того, речь идет не только о кодах ошибок http... Попытка удалить пользователя или статью также приведет к разным кодам для разных случаев. - person user672009; 01.11.2014
comment
Похоже, что удаление на любом ресурсе будет либо 2xx, либо 4xx (какой-то успех или какой-то сбой). Что касается сообщений - вы получаете обратно тело, чтобы делать то, что вы хотите. Если ответ json на ошибку звучит как какой-то стандарт, вы можете десериализовать его и использовать любые части для очень легкого создания ваших исключений. - person nerdwaller; 01.11.2014
comment
Конечно, но ответ может не подходить для просмотра пользователю. Программистам тогда не писать слишком понятные сообщения. Таким образом, я хочу сделать свой собственный. Интернационализированный тоже... и это будет по-разному, будь то статья, пользователь или продукт питания, который я удаляю. Поэтому мне нужны пользовательские ответы для каждого метода в моем клиенте. - person user672009; 01.11.2014
comment
Чего мне не хватает, так это... куда мне поместить блок try/catch? - person MikeWallaceDev; 27.04.2015
comment
@MikeWallaceDev Это действительно зависит от того, что вы делаете. Если вы возвращаете Observable<T>, то эта логика выполняется в методе onError вашего Subscriber, если вы делаете обратные вызовы, я не уверен (на самом деле я их не использовал, вместо этого я использую Rx), если вы делаете стандартный Response или Concrete Classes, то вы бы обернули HTTP-вызов (если это уместно). например try { myImplSyncronous.getResource(); } catch (RetrofitError e) { /*handle*/} - person nerdwaller; 27.04.2015