Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring 使用弹簧和反应器的API成分(BFF)_Spring_Reactive Programming_Spring Webflux_Project Reactor - Fatal编程技术网

Spring 使用弹簧和反应器的API成分(BFF)

Spring 使用弹簧和反应器的API成分(BFF),spring,reactive-programming,spring-webflux,project-reactor,Spring,Reactive Programming,Spring Webflux,Project Reactor,假设我有两个微服务,我想在使用WebFlux的SpringREST控制器中实现BFF(前端后端)模式 来自2个远程服务的域对象是: public class Comment { private Long id; private String text; private Long authorId; private Long editorId; } public class Person { private Long id; private Str

假设我有两个微服务,我想在使用WebFlux的SpringREST控制器中实现BFF(前端后端)模式

来自2个远程服务的域对象是:

public class Comment {
    private Long id;
    private String text;
    private Long authorId;
    private Long editorId;
}

public class Person {
    private Long id;
    private String firstName;
    private String lastName;
}
API编写器必须返回以下类型的对象:

public class ComposedComment {
    private String text;
    private String authorFullName;
    private String editorFullName;
}
为了模拟性,我编写了一个控制器,在一个控制器中模拟所有服务

@RestController
@RequestMapping("/api")
public class Controller {

    private static final List<Comment> ALL_COMMENTS = Arrays.asList(//
            new Comment(1L, "Bla bla", 1L, null), //
            new Comment(2L, "lorem ipsum", 2L, 3L), //
            new Comment(3L, "a comment", 2L, 1L));
    private static final Map<Long, Person> PERSONS;

    static {
        PERSONS = new HashMap<>();
        PERSONS.put(1L, new Person(1L, "John", "Smith"));
        PERSONS.put(2L, new Person(2L, "Paul", "Black"));
        PERSONS.put(3L, new Person(3L, "Maggie", "Green"));
    }

    private WebClient clientCommentService = WebClient.create("http://localhost:8080/api");
    private WebClient clientPersonService = WebClient.create("http://localhost:8080/api");

    @GetMapping("/composed/comments")
    public Flux<ComposedComment> getComposedComments() {
     //This is the tricky part
    }

    private String extractFullName(Map<Long, Person> map, Long personId) {
        Person person = map.get(personId);
        return person == null ? null : person.getFirstName() + " " + person.getLastName();
    }

    @GetMapping("/comments")
    public ResponseEntity<List<Comment>> getAllComments() {
        return new ResponseEntity<List<Comment>>(ALL_COMMENTS, HttpStatus.OK);
    }

    @GetMapping("/persons/{personIds}")
    public ResponseEntity<List<Person>> getPersonsByIdIn(@PathVariable("personIds") Set<Long> personIds) {
        List<Person> persons = personIds.stream().map(id -> PERSONS.get(id)).filter(person -> person != null)
                .collect(Collectors.toList());
        return new ResponseEntity<List<Person>>(persons, HttpStatus.OK);
    }
}
@RestController
@请求映射(“/api”)
公共类控制器{
私有静态最终列表所有注释=Arrays.asList(//
新注释(1L,“Bla Bla”,1L,空)//
新评论(2L,“lorem ipsum”,2L,3L)//
新评论(3L,“评论”,2L,1L);
私人静态最终地图;
静止的{
PERSONS=新HashMap();
人物。放置(1L,新人物(1L,“约翰”、“史密斯”);
人物。放置(2L,新人物(2L,“保罗”,“黑色”);
人物。放置(3L,新人物(3L,“玛姬”,“绿色”);
}
private WebClient clientCommentService=WebClient.create(“http://localhost:8080/api");
private WebClient clientPersonService=WebClient.create(“http://localhost:8080/api");
@GetMapping(“/composited/comments”)
公共流量getComposedComments(){
//这是棘手的部分
}
私有字符串extractFullName(映射映射,长personId){
Person=map.get(personId);
return person==null?null:person.getFirstName()+“”+person.getLastName();
}
@GetMapping(“/comments”)
公共响应getAllComments(){
返回新的响应属性(所有注释,HttpStatus.OK);
}
@GetMapping(“/persons/{personIds}”)
公共响应getPersonsByIdIn(@PathVariable(“personIds”)Set personIds){
List persons=personIds.stream().map(id->persons.get(id)).filter(person->person!=null)
.collect(Collectors.toList());
返回新的响应状态(persons,HttpStatus.OK);
}
}
我的问题是我刚刚开始使用反应堆,我不确定我在做什么。。这是my composer方法的当前版本:

@GetMapping("/composed/comments")
public Flux<ComposedComment> getComposedComments() {
    Flux<Comment> commentFlux = clientCommentService.get().uri("/comments").retrieve().bodyToFlux(Comment.class);
    Set<Long> personIds = commentFlux.toStream().map(comment -> Arrays.asList(comment.getAuthorId(), comment.getEditorId())).flatMap(Collection::stream).filter(Objects::nonNull).collect(Collectors.toSet());
    Map<Long, Person> personsById = clientPersonService.get().uri("/persons/{ids}", personIds.stream().map(Object::toString).collect(Collectors.joining(","))).retrieve().bodyToFlux(Person.class).collectMap(Person::getId).block();
    return commentFlux.map(
            comment -> new ComposedComment(
                    comment.getText(),
                    extractFullName(personsById, comment.getAuthorId()),
                    extractFullName(personsById, comment.getEditorId()))
    );
}
@GetMapping(“/composited/comments”)
公共流量getComposedComments(){
Flux commentFlux=clientCommentService.get().uri(“/comments”).retrieve().bodyToFlux(Comment.class);
Set personIds=commentFlux.toStream().map(comment->Arrays.asList(comment.getAuthorId(),comment.getEditorId()).flatMap(Collection::stream).filter(Objects::nonNull).collect(Collectors.toSet());
Map personsById=clientPersonService.get().uri(“/persons/{ids}”),personIds.stream().Map(Object::toString).collect(collector.joining(“,”).retrieve().bodyToFlux(Person.class).collectMap(Person::getId).block();
返回commentflow.map(
注释->新建复合注释(
comment.getText(),
extractFullName(personsById,comment.getAuthorId()),
extractFullName(personsById,comment.getEditorId())
);
}

它可以工作,但是我知道我应该使用map、flatMap和zip进行一些转换,而不是调用block()和toStream()。。。你能帮我正确地重写这个方法吗?:)

您应该尝试使用
zip
操作符组合这两个发布者。如果你想退货,不要订阅流量

如果因为第二个发布者依赖于第一个发布者的结果而无法使用
zip
,则使用
flatMap

可以像这样使用平面图:

commentsFlux.flatMap(comment -> personService.getPersonsByIds(comment.getPersonId1() + "," + comment.getPersonId2())
                                      //at this moment you have scope on both
                                      .map(listOfTwoPersons -> new Composed(listOfTwoPersons, comment))

注意:我没有使用webflux客户端,我只是根据您的工作示例猜测,即使您返回一个实体或实体列表,它也知道如何包装为Flux/Mono。

您应该尝试使用
zip
操作符组合这两个发布者。如果你想退货,不要订阅流量

如果因为第二个发布者依赖于第一个发布者的结果而无法使用
zip
,则使用
flatMap

可以像这样使用平面图:

commentsFlux.flatMap(comment -> personService.getPersonsByIds(comment.getPersonId1() + "," + comment.getPersonId2())
                                      //at this moment you have scope on both
                                      .map(listOfTwoPersons -> new Composed(listOfTwoPersons, comment))

注意:我没有使用webflux客户端,我只是根据您的工作示例猜测,即使您返回一个实体或实体列表,它也知道要包装为Flux/Mono。

您的控制器中返回null。 取而代之的是返回反应流

return commentFlux.flatMap(comment -> ....)
....
您的控制器签名返回一个

Flux<ComposedComment>
流量
因此,请确保在最后一次返回时,必须使用flatMap或map将它们转换为ComposedComment。您可以将其视为一个承诺链,您可以在实现中执行许多flatMap、map转换为最终数据集

在这些情况下不要使用subscribe,subscribe适合于演示反应流的调用过程,或者在应用程序中的某个地方,结果调用该方法不需要直接作为该控制器


此时,您只需使用map、flatMap、collect、zip。。。。。。只需返回反应流(Mono,Flux),然后SpringWebflux将调用它们。

在控制器中返回null。 取而代之的是返回反应流

return commentFlux.flatMap(comment -> ....)
....
您的控制器签名返回一个

Flux<ComposedComment>
流量
因此,请确保在最后一次返回时,必须使用flatMap或map将它们转换为ComposedComment。您可以将其视为一个承诺链,您可以在实现中执行许多flatMap、map转换为最终数据集

在这些情况下不要使用subscribe,subscribe适合于演示反应流的调用过程,或者在应用程序中的某个地方,结果调用该方法不需要直接作为该控制器

此时,您只需使用map、flatMap、collect、zip。。。。。。只要返回反应流(Mono,Flux),SpringWebflux就会调用它们。

如果我这样做:Flux-commentsWithPersonIds=Flux.zip(commentFlux,commentFlux.map(comment->Arrays.asList(comment.getEdit