Все технические форумы на одном сайте Удобный поиск информации с популярных форумов в одном месте
Вопрос: Rest Api nested object - best practice, есть ли?

Всем, привет.
Народ, подскажите направьте. Допустим, есть микросервисное приложение. Есть форма, на которой, можно создать узера.
На форме много вкладок, там и узер дату можно отметить, можно и детей его отметить, и паспорт и так далее. И по сабмиту,
вся эта гигантская ДТО летит на сервер.
Есть контроллер, который отрабатывает по определенному адресу и выполняет бизнес логику.
В одной из книг, подглядел прием который позволяет справляться с overfetching, например

GET /orders/1345?expand=consumer

Получив подобный запрос, я подтащу данные по order, а также consumer.

ОК, прием мне понравился, и с запросом GET вроде как вся понятно.
Но непонятно, как быть с POST запросом, в котором nested объекты.

Допустим, есть объект

class User{
 var name:String
 var children : List<Children>
}


Есть контроллер :

@PostMapping("/")
fun createUser(user: UserDto) : ResponseBody<*>{
  
  userRepo.save(user) 
  
  if (user.hasChildren()){
   childrenRepo.save(user.getChildren)
  }

return ResponseBody(ok()).body(user.getId());
}

Мне вот что не нравится/непонятно, я проверяю DTO на наличие в ней children,
И глядя на аннотацию метода в контроллере @PostMapping("/"), я не могу четко сказать,
что он будет внутри делать, будет он сохранять только user, или user+children, или
user+children+other nested, а вот тут мне все понятно

GET /orders/1345?expand=consumer


Хочу узнать корректно, ли использование таких методов в контроллере, или может best практики подразумевают
передачу в боди еще каких-то данных, которые говорят что именно сохраняем, ну или в хедере чего-нибудь.
Например (случай когда в ДТО говорю что хочу сохранить):

@PostMapping("/")
fun createUser(user: UserDto) : ResponseBody<*>{
  
  userRepo.save(user) 
  
  for (PersistItem pItem: user.getPersistItems()){
    switch(pItem){
      "children"-> childrenRepo.save(user.getChildren)
      "passport"-> passportRepo.save(user.getPassport)
      "otherNestedData"->otherNestedDataRepo(user.saveOtherNestedData)
    }
  }
 
return ResponseBody(ok()).body(user.getId());
}


Или с хедерами

@PostMapping("/")
fun createUser(user: UserDto, @RequestHeader("persistChildren") persistChildren: Boolean ) : ResponseBody<*>{
 //body
}


Подскажите плз лучшие практики, или где почитать, конкретно ответ на такой вопрос, не нашел. Или может я усложняю.
Или может,технология какая есть....Спасибо.
Ответ:
justcoder
Может graphql прикручу

Зачёт.
Вопрос: Best Practice относительно использования dao и сервиса в сервлетах

Возник вопрос, есть ли существенная разница(или какая-то best practice) в двух случаях:
1. Service/dao объект как поле класса.
2. Service/dao объект создается в соотвествующих методах сервлета, то есть при каждом запросе создается объект. Как быстро gc убьет созданный объет? Ссылка пропадет сразу после тела метода?

Как правильнее делать?
Ответ: тогда первый варик с синглтонами
Вопрос: Почему Object.sleep() должен быть обязательно в synchronized?

Java
1
2
3
4
5
6
7
  synchronized (Thread.currentThread()){
            try {
                Thread.currentThread().wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
Почему вызов Object.wait() должен быть обязательно синхронизирован? Если убрать оператор synchronized, то получаю ошибку java.lang.IllegalMonitorStateException
Прощу прощения за ошибку в названии темы - должно быть wait() вместо sleep

Добавлено через 16 минут
synchronized/wait()/notify()/notifyAll() крепко увязаны вместе в концепции монитора (monitor). Это приводит к интересным побочным эффектам - нельзя вызывать у объекта wait()/notify()/notifyAll() вне блока синхронизации (или синхронизированного метода) по этому объекту (иначе будет IllegalMonitorStateException)
Вот что мне удалось найти - получается из-за того, что все завязано на концепции монитора?
Ответ: Gr1f0nn, спасибо! Я кажется понял - просто использование wait() уже подразумевает в себе то, что мы находимся в synchronized, иначе, как вы сказали, действительно, освобождать будет нечего. Поэтому wait() и заключается в блок synchronized.
Вопрос: Задетектим багу object-ов (Delphi7 < ??? < Delphi2010)

Берём код
type
  TDataStorage = object
    Data: array of Byte;
    Size: Integer;
  end;

  TMyDataStorage = object(TDataStorage)
  end;

procedure Test;
var
  S: TMyDataStorage;
begin
  System.Finalize(S);
end;

initialization
  Test;

end.

Delphi7 выдаёт хинт "Expression needs no Initialize/Finalize" потому как в Delphi7 не воспринимаются сложные типы "предков". В Delphi2010 этой баги уже нет, невидимый try/finally выставляется как надо, поэтому собственно и System.Finalize(S) хинтов не выдаёт. У кого Delphi 2005..2009 - посмотрите, где есть ошибка и где уже исправлена. Пожалуйста
Ответ:
GunSmoker
SOFT FOR YOU, object в Delphi есть только по одной причине: чтобы работал старый код из Turbo Pascal. Соответственно, новые возможности языка (в данном случае - динамические массивы) с ними просто не тестируют. Подобного вида "баги" в object не исправляют (тикеты на QC закрывают с пометкой "won't do"). Якобы "исправление" этого "бага" в D2010 является, вероятно, побочным продуктом других изменений. Возможно - расширением RTTI или улучшением записей.

Мне плевать для чего и почему нужен object. Я знаю, что альтернатив ему нет. Новые record-ы с методами не умеют ни protected, ни наследоваться. И ещё я знаю, что в Delphi7 ошибка есть, а в Delphi2010 уже нет. Поэтому я сформулировал вопрос так, чтобы каждому он был ясен.
GunSmoker
У кого Delphi 2005..2009 - посмотрите, где есть ошибка и где уже исправлена. Пожалуйста
Вопрос: Пятничный Best practices.

Добрый день, коллеги!

Вот такой вот пятничный говнокодец.

Очень часто нужно сортировать сущности в коллекциях по разным колонкам.
В настоящий момент это уродство выглядит вот так.

Entity.java
+

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;

public class FuckenEntity {

    private BigInteger account;
    private String clientName;
    private Date clientActivityDate;
    private String clientSubType;
    private BigDecimal clientBalance;

    public BigInteger getAccount() {
        return account;
    }

    public void setAccount(BigInteger account) {
        this.account = account;
    }

    public String getClientName() {
        return clientName;
    }

    public void setClientName(String clientName) {
        this.clientName = clientName;
    }

    public Date getClientActivityDate() {
        return clientActivityDate;
    }

    public void setClientActivityDate(Date clientActivityDate) {
        this.clientActivityDate = clientActivityDate;
    }

    public String getClientSubType() {
        return clientSubType;
    }

    public void setClientSubType(String clientSubType) {
        this.clientSubType = clientSubType;
    }

    public BigDecimal getClientBalance() {
        return clientBalance;
    }

    public void setClientBalance(BigDecimal clientBalance) {
        this.clientBalance = clientBalance;
    }
}


SortProcessor
+
import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanNameAware;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import static java.lang.String.format;
import static java.lang.String.format;

public class FuckenEntityProcessor implements BeanNameAware {

    private static Logger logger = Logger.getLogger(FuckenEntityProcessor.class);

    /**
     *
     * @param FuckenEntitys - fucken collection to sort (out)
     * @param nField - number of field (Just like SELECT .... FROM ... ORDER by nField )
     * @param direction - direction of sorting like { ASC | DESC }
     */
    public static void sort(List<FuckenEntity> FuckenEntitys, int nField, int direction) {
        if (direction == 0 || direction == 1) {
            if (FuckenEntitys != null && FuckenEntitys.size() > 0) {
                try {
                    if (nField == 0) {
                        Collections.sort(FuckenEntitys, new Comparator<FuckenEntity>() {
                            @Override
                            public int compare(FuckenEntity o1, FuckenEntity o2) {
                                if (o1.getAccount()==null && o2.getAccount()==null) return 0;
                                if (o1.getAccount()==null) return 1;
                                if (o2.getAccount()==null) return -1;
                                return o1.getAccount().compareTo(o2.getAccount());
                            }
                        });
                    } else if (nField == 1) {
                        Collections.sort(FuckenEntitys, new Comparator<FuckenEntity>() {
                            @Override
                            public int compare(FuckenEntity o1, FuckenEntity o2) {
                                if (o1.getClientName()==null && o2.getClientName()==null) return 0;
                                if (o1.getClientName()==null) return 1;
                                if (o2.getClientName()==null) return -1;
                                return o1.getClientName().compareTo(o2.getClientName());
                            }
                        });
                    } else if (nField == 2) {
                        Collections.sort(FuckenEntitys, new Comparator<FuckenEntity>() {
                            @Override
                            public int compare(FuckenEntity o1, FuckenEntity o2) {
                                if (o1.getClientActivityDate()==null && o2.getClientActivityDate()==null) return 0;
                                if (o1.getClientActivityDate()==null) return 1;
                                if (o2.getClientActivityDate()==null) return -1;
                                return o1.getClientActivityDate().compareTo(o2.getClientActivityDate());
                            }
                        });
                    } else if (nField == 3) {
                        Collections.sort(FuckenEntitys, new Comparator<FuckenEntity>() {
                            @Override
                            public int compare(FuckenEntity o1, FuckenEntity o2) {
                                if (o1.getClientSubType()==null && o2.getClientSubType()==null) return 0;
                                if (o1.getClientSubType()==null) return 1;
                                if (o2.getClientSubType()==null) return -1;
                                return o1.getClientSubType().compareTo(o2.getClientSubType());
                            }
                        });
                    } else if (nField == 4) {
                        Collections.sort(FuckenEntitys, new Comparator<FuckenEntity>() {
                            @Override
                            public int compare(FuckenEntity o1, FuckenEntity o2) {
                                if (o1.getClientBalance()==null && o2.getClientBalance()==null) return 0;
                                if (o1.getClientBalance()==null) return 1;
                                if (o2.getClientBalance()==null) return -1;
                                return o1.getClientBalance().compareTo(o2.getClientBalance());
                            }
                        });
                    }
                    if (direction==1) Collections.reverse(FuckenEntitys);
                } catch (Exception ex) {
                    logger.error(
                            format("Exception while sorting FuckenEntitys param = %d,dir = %d",
                                    nField, direction), ex
                    );
                }
            }
        }
    }

    ................
}


Обычно List<entity> связана 1:1 с GWT FlexTable и отображается на UI пользователя
как есть.

Сортировать на стороне источника данных не всегда возможно. Где БД - мы
как-то сортируем а вот из rest-сервисов приходится делать всё самим.

Встроенные DBMS (Derby, HyperSonic, H2) мы не используем.

Сами entities лежат в скоупе GWT компилляции и имеют поля только основных
примитивных типов (int, String, Date, BigDecimal). Сложные объекты
я пока не встречал.

Вопрос:

Как сортировать универсально?

Нужен какой-то компонент (SpringBean) которому на вход приходит
неизвестная коллекция List<Object> и номер колонки и direction (ASC|DESC)
по которой надо сортировать и на выходе мы имеет отсортированное.

Буду рад выслушать идеи, Best Practices и почитать исходники.

Спасибо!

P.S. Реверс тоже радует :)

P.P.S Фух. Написал таки. Пятница ... мать ее так.
Ответ:
Monochromatique
Alexey Tomin
пропущено...
Иди из нашего кошмара в свой- установки MS серверов и совместимости версия дотнета :)


Да как бы нет проблем. ))

Но как-то хотелось думать (не приведи Бог столкнуться), что в JAVA есть эквивалент - persons.OrderBy(p=>p.name)


Будет в JDK8

Monochromatique
На что я кстати и привел ссылку. Просто ты наверное не понял.


Я не понял, что ты этим хотел сказать, да :)
Вопрос: Best practice Comparator и null-значения

Добрый вечер.
В одном из учебных задач потребовалось использовать Comparator для сортировки массива.
Java(TM) 2 Platform Standard Edition 5.0
1
2
3
4
5
6
        Arrays.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getFaculty().compareToIgnoreCase(o2.getFaculty());
            }
        });
Собственно всё работает, ровно до момента появления null в массиве students или поле faculty - валится с NPE.
Пару минут погуглив нашёл такое решение - теперь null не страшен в обоих случаях:
Java(TM) 2 Platform Standard Edition 5.0
1
2
3
4
5
6
7
8
9
10
11
12
        Arrays.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return (o1 == null) ?
                    ((o2 == null) ? (0) : (Integer.MIN_VALUE)) :
                    ((o2 == null) ? (Integer.MAX_VALUE) : 
                        (o1.getFaculty() == null) ?
                        ((o2.getFaculty() == null) ? (0) : (Integer.MIN_VALUE)) :
                        ((o2.getFaculty() == null) ? (Integer.MAX_VALUE) :  
                            (o1.getFaculty().compareToIgnoreCase(o2.getFaculty()))));
            }
        });
Я так понимаю это решение для параноиков или для сильно отказоустойчивых систем.
Хотелось бы узнать у опытных джавистов - какой вариант используется на реальных проектах чаще?
Стоит ли заморачиваться с проверками на null и в какой степени.
Ответ: Если очень нужно, можно и null-ы оставить. В java 8 появились удобные статические методы для создания компараторов. Comparator.nullsFirst/nullsLast например
Java(TM) 2 Platform Standard Edition 5.0
1
2
public static final Comparator<Student> BY_FACULTY_IGNORE_CASE =
    Comparator.nullsFirst((left, right) -> left.getFaculty().compareToIgnoreCase(right.getFaculty()));
Вопрос: Best practice. Сокращение программного кода

Здравствуйте. Пишу приложение для шифрования всего и вся

Каждый метод шифрования включает в себя две опции в статике (шифрование/дешифрование)

Для такого однообразного кода, я вижу решение использовать switch..case statement, Можно вложенный заменить на if statement

Как сократить следующие наработки оптимально (если нужно)?

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
             switch(Yii::$app->request->post('CryptForm')['method']) // методы шифрования
             {
                  case 1 :
                        switch (Yii::$app->request->post('CryptForm')['type']) // тип (шфрование/дешифрование)
                        {
                            case 1:
                                // шифруем 1-м методом...
                                break;
                            case 2:
                                // дешифруем 1-м методом...
                        }
                        break;
                    case 2:
                        switch (Yii::$app->request->post('CryptForm')['type']) 
                        {
                            case 1:
                                //шифруем 2-м методом...
                                break;
                            case 2:
                                // дешифруем 2-м методом...
                        }
                    ...
               }
Ответ: Jodah, ну, а раз методы повторяются, то можно сделать так:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class EncodeMethodGaus implements ICryptography
{
    public function encode($var)
    {
        return $var;
    }
    
    public function decode($var)
    {
        return $var;
    }
}
 
interface ICryptography 
{
    public function encode($var) {}
    
    public function decode($var) {}
}
Вопрос: хочу REST, но как правильно?

Попробовал на REST сделать 2 таблицы сущностей Master-Details.
Один REST контроллер отвечает за работу с Мастер, второй с Details. Очень понравилось.

Но нужно двигаться дальше. Дальше, для работы нужен интерфейс в программе. Всякие контролы на интерфейсе для сортировки по критерям. Выборка по диапазону даты. Для построения этих контролов, тоже нужна информация с сервера.
Для них тоже заводить отдельный REST? ресурс откуда подгружать справочники?
А если их много всяких Drop-Down Menu (параметров фильтрации/сортировки)? 130 сущностей и для каждого надо штук по 10 контролов? Хорошо конечно что их будут много и они будут простые. Это даст возможность закешировать ответы.
Я просто не понимаю это нормальный подход или это ад?

Ну и по методам. Если мне просто GET и GetById мало? А надо всякие выборки по диапазону дат+фильтрация по определенным полям? Я то конечно могу методов понаписывать, но насколько это правильно?
Ответ: Ага, спасибо.
На OData я и поглядывал изначально. Но вот смотрю под ASP.NET Core как то все тускло и печально. (полуофициальные альфа версии библиотек, с зачаточным функционалом) и не видно никакого бурного развития. Разработчики OData говорят, будет но потом, пока не до этого.
А у меня уже есть сколоченный костяк/основа приложения на связке ASP.NET Core + Angular4. Возвращаться на ASP.NET не очень хочется.
Может пока пожить без OData? (пока не появиться полноценные версии с богатым функционалом)
Как тогда сделать какой нибудь универсальный доступ к базе данных из Ангуляра? (типа REST). Копать в сторону ISpecification? и Query Object?
Планирую проектировать в стиле DDD (правда пока не умею), но хочу сделать сразу на такой архитектуре.
Вопрос: REST Debugger

Халява.





REST Debugger
Explore. Understand. Integrate

Embarcadero's free solution for exploring, understanding, and integrating RESTful web services with Delphi and C++Builder apps:


  • Debug RESTful web services
  • Easily prototype Delphi or C++Builder REST apps
  • Useful testing across app development lifecycle
  • No code required


  • Explore and Understand RESTful Web Services

    The Embarcadero REST Debugger empowers developers to explore, test, and ultimately understand how a RESTful web service works. Dive right into REST data with filterable JSON blobs, streamlined OAuth 1.0/2.0 authentication, and configurable request/resource parameters.

  • No Code Required to Integrate REST Functionality

    Directly copy and paste REST components from the REST Debugger to the RAD Studio, Delphi or C++Builder IDE. This enables configuration and consumption of REST services in Delphi or C++Builder apps with just a few clicks!

  • Prototype and Connect Delphi or C++Builder Apps to RESTful Web Services

    Escape the mire of debugging REST calls in code and use the REST Debugger to fast track data-rich prototyping with apps built using Delphi or C++Builder.
  • Ответ: И чего, там есть возможность интерграции с IDE и генерации в клипбоард уже настроеных компонетов?
    Вопрос: REST-сервис с двумя очередями

    Коллеги, добрый день! прошу сильно не пинать. Только изучаю азы и стоит следующая задача, прошу помочь: есть rest-сервис, у которого есть get и post методы, который делает rest-запросы к другому внешнему сервису и ответ возвращает клиенту т.е. этот rest-сервис занимается транспортировкой. Можно ли каким-нибудь образом в этом rest-сервисе реализовать две очереди in и out? Как?

    Спасибо!
    Ответ:
    Unknown-2018
    Коллеги, добрый день! прошу сильно не пинать. Только изучаю азы и стоит следующая задача, прошу помочь: есть rest-сервис, у которого есть get и post методы, который делает rest-запросы к другому внешнему сервису и ответ возвращает клиенту т.е. этот rest-сервис занимается транспортировкой. Можно ли каким-нибудь образом в этом rest-сервисе реализовать две очереди in и out? Как?

    Спасибо!


    Скорее всего надо первый post сделать как постановку запроса в очередь на удаленном сервисе. А get чтение топика из удаленного сервиса где запрос был обработан и опубликован в топик с уникальным ID. Клиент посылает своиз запрос с уникальным ID в очередь запросов и потом ждет ответ в топике если в топике приходит сообщение, что запрос с ID обработан, то Вася-кот транзакция завершилась.

    Ну или что-то народе такого. Вообще опубликуйте, что Вас спросили, а то очень сложно угадать