{"id":296413,"date":"2019-12-26T09:00:09","date_gmt":"2019-12-26T09:00:09","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=296413"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=296413","title":{"rendered":"\u041f\u0438\u0448\u0435\u043c \u0431\u043b\u043e\u0433 \u043d\u0430 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 \u2013 \u0447\u0430\u0441\u0442\u044c 3 \u00abUser\u00bb"},"content":{"rendered":"\n<div class=\"post__text post__text-html js-mediator-article\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/company\/X5RetailGroup\/blog\/482002\/\">\u0412\u043e \u0432\u0442\u043e\u0440\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0446\u0438\u043a\u043b\u0430 \u0441\u0442\u0430\u0442\u0435\u0439 \u00ab\u041f\u0438\u0448\u0435\u043c \u0431\u043b\u043e\u0433 \u043d\u0430 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445\u00bb \u043c\u044b \u043e\u043f\u0438\u0441\u0430\u043b\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/473516\/\">\u00abAPI Gateway\u00bb<\/a>.<\/p>\n<p>  \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043e\u043f\u0438\u0448\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 User. <br \/>  <a name=\"habracut\"><\/a><br \/>  \u041d\u0430\u0448 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0443\u043c\u0435\u0442\u044c: <\/p>\n<p>   \u2014 \u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0438 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u0435\u043c TraceId (\u0442\u043e\u0442 \u0441\u0430\u043c\u044b\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b\u043b \u0432\u044b\u0434\u0430\u043d api-gw, \u0441\u043c. <a href=\"https:\/\/habr.com\/ru\/post\/473516\/\">\u0427\u0430\u0441\u0442\u044c 2 \u00abAPI Gateway\u00bb<\/a>)<br \/>   \u2014 \u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0412\u0445\u043e\u0434(SignIN) \u0438 \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f(SignUp)<br \/>   \u2014 \u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 CRUD (\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435, \u0447\u0442\u0435\u043d\u0438\u0435, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0411\u0414). \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0411\u0414 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c MongoDB.<\/p>\n<p>  \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441 \u0432 \u043f\u0440\u043e\u0442\u043e\u0444\u0430\u0439\u043b\u0435 (.\/services\/user\/protobuf\/user.proto).<br \/>  \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u2014 proto3. \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0430\u043a\u0435\u0442\u0430 protobuf, \u0432 \u044d\u0442\u043e\u043c \u043f\u0430\u043a\u0435\u0442\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0430\u0432\u0442\u043e\u0441\u0433\u0435\u043d\u0435\u0440\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438. <\/p>\n<p>  \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 google\/api\/annotations.proto, \u043e\u043d\u0430 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 REST API.<\/p>\n<pre><code class=\"go\">syntax = \"proto3\"; package protobuf; import \"google\/api\/annotations.proto\"; <\/code><\/pre>\n<p>  \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 User, \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 (\u043c\u0435\u0442\u043e\u0434\u043e\u0432) \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043e\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u043b\u0430\u0434\u0430\u0442\u044c. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 SignUp(\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f): \u043e\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 SignUpRequest, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b Username, Password, FirstName \u0438 LastName \u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c SignUpResponse, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b Slug (UserID), Username, Role. \u0422\u0430\u043a\u0436\u0435 \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430, \u0432 \u0441\u0435\u043a\u0446\u0438\u0438 options \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432\u0443 post: &#171;\/api\/v1\/user\/signup. \u041d\u0430 \u0435\u0435 \u043e\u0441\u043d\u043e\u0432\u0435 \u043a\u043e\u0434\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u0441\u043e\u0437\u0434\u0430\u0441\u0442 REST \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c POST \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 http:{{api_gw_host}}\/api\/v1\/user\/signup. <\/p>\n<pre><code class=\"go\">\/\/-------------------------------------------------- \/\/ \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 User \/\/-------------------------------------------------- service UserService {      \/\/\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b   rpc SignUp (SignUpRequest) returns (SignUpResponse) {     option (google.api.http) = {       post: \"\/api\/v1\/user\/signup\"     };   }    \u2026 }   \/\/--------------------------------------------------  \/\/  SignUp \/\/-------------------------------------------------- message SignUpRequest {   string Username = 1;   string Password = 2;   string FirstName = 3;   string LastName = 4; } message SignUpResponse {   string Slug = 1;   string Username = 2;   string Role = 3; } <\/code><\/pre>\n<p>  \u0418 \u0431\u0443\u0434\u0435\u0442 \u043e\u0436\u0438\u0434\u0430\u0442\u044c \u0432 body \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:<\/p>\n<pre><code class=\"go\">{     Username: 'username_value',     Password: 'password_value',     FirstName: 'firstname_value',     LastName: 'lastname_value', } <\/code><\/pre>\n<p>  \u0418 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0443\u0441\u043f\u0435\u0445\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:<\/p>\n<pre><code class=\"go\">{     Slug: 'user_id_value',     Username: 'username_value',     Role: 'role_value', } <\/code><\/pre>\n<p>  \u041b\u0438\u0431\u043e \u043e\u0448\u0438\u0431\u043a\u0443. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043f\u0440\u043e \u043e\u0448\u0438\u0431\u043a\u0438 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u043c \u0447\u0443\u0442\u044c \u043d\u0438\u0436\u0435 \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0439, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0445 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0440\u043e\u0442\u043e\u0444\u0430\u0439\u043b\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b.<\/p>\n<p>  \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b (SignIn, Create, Update, Delete, Get, Find) \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e.<\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u0444\u0430\u0439\u043b. \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 sh .\/bin\/protogen.sh. \u042d\u0442\u043e\u0442 \u0441\u043a\u0440\u0438\u043f\u0442 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043e\u0434.<br \/>  \u0414\u0430\u043b\u0435\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0441 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 .\/services\/user \u0438 \u0432 \u0444\u0430\u0439\u043b\u0435 functions.go \u043f\u0438\u0448\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432.<\/p>\n<p>  \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c middleware. \u041f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u043c\u044b \u0432\u044b\u0442\u0430\u0441\u043a\u0438\u0432\u0430\u0435\u043c \u0438\u0437 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b TraceId, UserId, UserRole \u0438 \u043f\u0438\u0448\u0435\u043c \u0438\u0445 \u0432 \u043b\u043e\u0433 \u0444\u0430\u0439\u043b. \u0417\u0434\u0435\u0441\u044c \u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<pre><code class=\"go\">\/\/-------------------------------------------------- \/\/ Midelware \/\/-------------------------------------------------- func AccessLogInterceptor(ctx context.Context,req interface{},info *grpc.UnaryServerInfo,handler grpc.UnaryHandler,) (interface{}, error) {      start:=time.Now()     md,_:=metadata.FromIncomingContext(ctx)      \/\/ Calls the handler     reply, err := handler(ctx, req)      var traceId,userId,userRole string     if len(md[\"trace-id\"])&gt;0{         traceId=md[\"trace-id\"][0]     }     if len(md[\"user-id\"])&gt;0{         userId=md[\"user-id\"][0]     }     if len(md[\"user-role\"])&gt;0{         userRole=md[\"user-role\"][0]     }          msg:=fmt.Sprintf(\"Call:%v, traceId: %v, userId: %v, userRole: %v, time: %v\", info.FullMethod,traceId,userId,userRole,time.Since(start))     app.AccesLog(msg)      return reply, err } <\/code><\/pre>\n<p>  \u0412 \u043c\u0435\u0442\u043e\u0434\u0435 SignUp \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043e\u0442\u0432\u0435\u0442\u0430.<\/p>\n<pre><code class=\"go\">\/\/\u041e\u0442\u0432\u0435\u0442, \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e STATUS_FAIL out:=&amp;SignUpResponse{} <\/code><\/pre>\n<p>  \u0414\u0430\u043b\u0435\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<pre><code class=\"go\">\/\/\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043f\u0435\u0440\u0435\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c     \/\/\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 Username     err:=checkUserName(in.Username)     if err!=nil{         log.Printf(\"[ERR] %s.SignUp, %v\", app.SERVICE_NAME,err)         return out,err     }      \/\/\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 Username \u043d\u0430 \u0434\u0443\u0431\u043b\u044c     err=o.checkUserNameExist(in.Username)     if err!=nil{         log.Printf(\"[ERR] %s.SignUp, %v\", app.SERVICE_NAME,err)         return out,err     }          \/\/\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 Password     err=checkPassword(in.Password)     if err!=nil{         log.Printf(\"[ERR] %s.SignUp, %v\", app.SERVICE_NAME,err)         return out,err     } <\/code><\/pre>\n<p>  \u0418 \u0435\u0441\u043b\u0438 \u0432\u0441\u0435 \u041e\u043a, \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 User, \u043f\u0438\u0448\u0435\u043c \u0432 \u0411\u0414 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442.<\/p>\n<pre><code class=\"go\">user:=&amp;User{         Username:in.Username,         FirstName:in.FirstName,         LastName:in.LastName,         Password:getMD5(in.Password),     }      var slug string     collection:= o.DbClient.Database(\"blog\").Collection(\"users\")     insertResult, err := collection.InsertOne(context.TODO(), user)     if err != nil {         return out,err     }     if oid, ok := insertResult.InsertedID.(primitive.ObjectID); ok {         slug=fmt.Sprintf(\"%s\",oid.Hex())     }else {         err:=app.ErrInsert         return out,err     }      out.Slug=slug     out.Username=in.Username     out.Role=app.ROLE_USER     return out,nil <\/code><\/pre>\n<p>  \u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u043c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0442 \u043e\u0448\u0438\u0431\u043a\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"go\">err:=app.ErrInsert <\/code><\/pre>\n<p>  \u0422\u0430\u043a \u043a\u0430\u043a \u0432 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0438\u0442\u043e\u0433\u0435 \u044d\u0442\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u0432\u0435\u0440\u043d\u0435\u0442\u0441\u044f \u043d\u0430\u0448\u0435\u043c\u0443 api-wg (\u0432 \u0435\u0433\u043e REST \u0447\u0430\u0441\u0442\u044c) \u0438 \u0431\u044b\u043b\u043e \u0431\u044b \u043a\u0440\u0443\u0442\u043e, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0435 \u0432 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 HTTP \u043a\u043e\u0434-\u043e\u0442\u0432\u0435\u0442. \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0443\u0447\u0443 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 go error, \u0430 status.error \u0438\u0437 \u043f\u0430\u043a\u0435\u0442\u0430 google.golang.org\/grpc\/status.<\/p>\n<p>  \u0412\u0441\u0435 \u0442\u0438\u043f\u043e\u0432\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 User \u0438 \u043a\u0430\u043a \u043e\u043d\u0438 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 HTTP \u043a\u043e\u0434\u044b \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0432 \u0444\u0430\u0439\u043b\u0435.\/services\/user\/app\/errors.go.<\/p>\n<pre><code class=\"go\">package app  import (     \"google.golang.org\/grpc\/codes\"     \"google.golang.org\/grpc\/status\" )  \/\/\u041e\u0448\u0438\u0431\u043a\u0438 \u0443\u0440\u043e\u0432\u043d\u044f \u0431\u0438\u0437\u043d\u0435\u0441 \u043b\u043e\u0433\u0438\u043a\u0438 var (     \/\/\u041e\u0448\u0438\u0431\u043a\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438     ErrEmailIncorrect                   = status.Error(codes.InvalidArgument, \"\u041d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 E-mail\")     ErrPasswordIsEmpty                  = status.Error(codes.InvalidArgument, \"Password \u043d\u0435 \u0437\u0430\u0434\u0430\u043d\")     ErrUserNameIsEmpty                  = status.Error(codes.InvalidArgument, \"E-mail \u043d\u0435 \u0437\u0430\u0434\u0430\u043d\")     ErrUserNameIsExist                  = status.Error(codes.AlreadyExists, \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\")      ErrNotFound                         = status.Error(codes.NotFound, \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\")     ErrIncorrectLoginOrPassword         = status.Error(codes.Unauthenticated,\"\u041d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 \u043b\u043e\u0433\u0438\u043d \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c\")      \/\/\u041e\u0448\u0438\u0431\u043a\u0438 CRUD     ErrInsert                           = status.Error(codes.Internal, \"\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0438\")     ErrUpdate                           = status.Error(codes.Internal, \"\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0438\")      )  \/\/================================================== \/\/ All gRPC err codes \/\/================================================== \/\/ codes.OK - http.StatusOK \/\/ codes.Canceled - http.StatusRequestTimeout \/\/ codes.Unknown - http.StatusInternalServerError \/\/ codes.InvalidArgument - http.StatusBadRequest \/\/ codes.DeadlineExceeded - http.StatusGatewayTimeout \/\/ codes.NotFound - http.StatusNotFound \/\/ codes.AlreadyExists - http.StatusConflict \/\/ codes.PermissionDenied - http.StatusForbidden \/\/ codes.Unauthenticated - http.StatusUnauthorized \/\/ codes.ResourceExhausted - http.StatusTooManyRequests \/\/ codes.FailedPrecondition - http.StatusBadRequest \/\/ codes.Aborted - http.StatusConflict \/\/ codes.OutOfRange - http.StatusBadRequest \/\/ codes.Unimplemented - http.StatusNotImplemented \/\/ codes.Internal - http.StatusInternalServerError \/\/ codes.Unavailable - http.StatusServiceUnavailable \/\/ codes.DataLoss - http.StatusInternalServerError <\/code><\/pre>\n<p>  \u0418 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0447\u0442\u043e \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043e \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0435 User, \u044d\u0442\u043e \u0442\u043e \u043a\u0430\u043a \u043e\u043d \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u042d\u0442\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u0432 \u0444\u0430\u0439\u043b\u0435 .\/services\/user\/main.go.<\/p>\n<p>  \u0417\u0430\u043f\u0443\u0441\u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0430:<\/p>\n<pre><code class=\"go\">lis,err:= net.Listen(\"tcp\", fmt.Sprintf(\":%s\", Port))     if err != nil {         log.Fatalf(\"failed to listen: %v\", err)     }      grpcServer:= grpc.NewServer(         grpc.UnaryInterceptor(protobuf.AccessLogInterceptor),     )     s:=&amp;protobuf.Server{}  \u2026 \/\/ attach the user service to the server     protobuf.RegisterUserServiceServer(grpcServer, s) <\/code><\/pre>\n<p>  \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0411\u0414 (main.go):<\/p>\n<pre><code class=\"go\">\/\/\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0411\u0414 s.DbConnect() defer s.DbDisconnect() <\/code><\/pre>\n<p>  \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 DbConnect (.\/services\/user\/functions.go):<\/p>\n<pre><code class=\"go\">\/\/-------------------------------------------------- \/\/ \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\/\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0411\u0414 \/\/-------------------------------------------------- func (o *Server) DbConnect() error {     var client *mongo.Client          \/\/ Create client     strURI:=fmt.Sprintf(\"mongodb:\/\/%s:%s@%s:%s\",os.Getenv(\"MONGO_USER\"),os.Getenv(\"MONGO_PASS\"),os.Getenv(\"MONGO_HOST\"),os.Getenv(\"MONGO_PORT\"))     client, err:= mongo.NewClient(options.Client().ApplyURI(strURI))     if err != nil {         return err     }      \/\/ Create connect     err = client.Connect(context.TODO())     if err != nil {         return err     }     o.DbClient=client     return nil } <\/code><\/pre>\n<\/div>\n<p>               <script class=\"js-mediator-script\">!function(e){function t(t,n){if(!(n in e)){for(var r,a=e.document,i=a.scripts,o=i.length;o--;)if(-1!==i[o].src.indexOf(t)){r=i[o];break}if(!r){r=a.createElement(\"script\"),r.type=\"text\/javascript\",r.async=!0,r.defer=!0,r.src=t,r.charset=\"UTF-8\";var d=function(){var e=a.getElementsByTagName(\"script\")[0];e.parentNode.insertBefore(r,e)};\"[object Opera]\"==e.opera?a.addEventListener?a.addEventListener(\"DOMContentLoaded\",d,!1):e.attachEvent(\"onload\",d):d() } } }t(\"\/\/mediator.mail.ru\/script\/2820404\/\",\"_mediator\")}(window);<\/script>      <br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/X5RetailGroup\/blog\/482002\/\"> https:\/\/habr.com\/ru\/company\/X5RetailGroup\/blog\/482002\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html js-mediator-article\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/company\/X5RetailGroup\/blog\/482002\/\">\u0412\u043e \u0432\u0442\u043e\u0440\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0446\u0438\u043a\u043b\u0430 \u0441\u0442\u0430\u0442\u0435\u0439 \u00ab\u041f\u0438\u0448\u0435\u043c \u0431\u043b\u043e\u0433 \u043d\u0430 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445\u00bb \u043c\u044b \u043e\u043f\u0438\u0441\u0430\u043b\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/473516\/\">\u00abAPI Gateway\u00bb<\/a>.<\/p>\n<p>  \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043e\u043f\u0438\u0448\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 User.   <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-296413","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/296413","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=296413"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/296413\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=296413"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=296413"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=296413"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}