Can someone, once and for all, point out how to write REST APIs in Go?

agolangf · · 614 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>There seems to be an endless number of ways people create REST APIs in Go. By the time I knew that db handlers were supposed to be transferred via context, people were already considering it bad practice. Mat Ryer suggests a struct based approach using closures and such but it doesn’t scale well to even a handful of rotes. </p> <p>Basically the things I’m looking for: 1. Scalable APIs where adding and removing routes is easily managed and idiomatic with proper separations 2. Managing database connections 3. Writing testable code</p> <p>A sample repository would be great. </p> <p>Thanks!</p> <hr/>**评论:**<br/><br/>krak3n_: <pre><ol> <li>Gorilla mux is the best most feature complete router out there IMO. </li> <li>Don&#39;t send the db around in context. Depency inject. So you could have a function that takes a db as an argument that return a http.HandlerFunc, this way the handler has access to the db. Or go down the struct route that implements the http.Handler interface and has the db on it as a property. I would mix a match, simple handlers can just be funcs, complex handlers that need loads of dependencies use structs.</li> <li>Use httptest to spin up a real http server with your handler you can throw real requests at. Imo always connect to a real db loaded with fixtures for repeatable test cases. You can also interface your db to force certain error cases if you need.</li> </ol></pre>jerf: <pre><p>If you&#39;ve got a ton of handlers that have the same needs, you can use one struct:</p> <pre><code>type DBHandler struct { db *whateverDB } func (dbh *DBHandler) Handler1(rw http.RequestWriter, req *http.Request) { // has access to dbh.db } func (dbh *DBHandler) Handler2(rw http.RequestWriter, req *http.Request) { // has access to dbh.db } // register these via: dbHandlers := &amp;DBHandler{whateverDB} route.Add(&#34;/...1&#34;, http.HandlerFunc(dbHandlers.Handler1)) route.Add(&#34;/...2&#34;, http.HandlerFunc(dbHandlers.Handler2)) </code></pre> <p>Though the instant you grow a handler that needs another parameter, be sure to create a new object, rather than an HTTP handler <a href="https://en.wikipedia.org/wiki/God_object">God Object</a>.</p></pre>BobbyChompers: <pre><p>Situation: There are 15 competing standards.</p></pre>jerf: <pre><p>Yes, sure, but A: they all play with each other just fine and B: this is programming. Any language powerful enough to solve the problem is going to have multiple ways to do it.</p> <p>If Go isn&#39;t prescriptive enough for you, I&#39;ve got bad news... you&#39;re already pretty much at maximum prescriptiveness.</p></pre>pharrisee: <pre><p>We just need to define a standard over those standards, and then we&#39;d have 16 standards. ;)</p></pre>toorusan: <pre><p>I thought the server struct pattern was fine?</p> <pre><code>type Server struct { db *whateverDB search *elasticSearchHandler rt *realtimeHandler } </code></pre> <p>(most recently seen at <a href="https://medium.com/statuscode/how-i-write-go-http-services-after-seven-years-37c208122831">https://medium.com/statuscode/how-i-write-go-http-services-after-seven-years-37c208122831</a>)</p></pre>peterbourgon: <pre><p>Yes this is also fine</p></pre>ReadFoo: <pre><p>If golang would fix (wink) the variable declaration order more people might use it...I was pleasantly surprised to see &#34;struct&#34; there and what looks like pointers. Also not fond of that Pascal remnant :=.</p></pre>krak3n_: <pre><p>Oh also check out table testing and also I wrote an article about testing with golden files here: <a href="https://medium.com/soon-london/testing-with-golden-files-in-go-7fccc71c43d3" rel="nofollow">https://medium.com/soon-london/testing-with-golden-files-in-go-7fccc71c43d3</a></p></pre>albatr0s: <pre><p>Whatever works for you and your requirements is fine.</p></pre>sboyette2: <pre><p>This. A &#34;REST API&#34; isn&#39;t a framework, or something magical. It&#39;s just a defined set of URIs and HTTP verbs, and then one or more pieces of software which handle requests matching those definitions.</p> <p>But I know that isn&#39;t what OP was actually asking. I think what OP wants is for the framework space to quiet down and for people to settle on &#34;the&#34; way to do things, to which I can only reply: &#34;Look upon the Javascript space, and despair.&#34;</p> <p>Stay flexible, enjoy learning, and realize that there&#39;s never going to be any tool which will last forever.</p></pre>titpetric: <pre><p>Also this. People can&#39;t agree if to pass authorization tokens in the URL query, HTTP header or even a cookie. All three ways are valid, preferring one over the other is subjective, and won&#39;t make your app one bit different to the user. There&#39;s no way to quiet it down, as you say: despair.</p> <p>Add websockets and gRPC to the mix, and you&#39;ll realize that you have to structure your APIs way differently than you would just for HTTP handlers, because damn sure you&#39;re not going to re-implement everything for each protocol you add. Or at least, you don&#39;t want to get stuck writing glue for it either.</p> <p>tl;dr: learn and go your own path young padawan</p></pre>torie_anal_gerbiler: <pre><p>Except for vi and emacs, those last forever</p></pre>redbeard0x0a: <pre><p>I&#39;m curious, how does Mat Ryer&#39;s suggestions not scale well? I&#39;ve been meaning to try out his method, but what are the pitfalls?</p> <p>Mat Ryer&#39;s article: <a href="https://medium.com/statuscode/how-i-write-go-http-services-after-seven-years-37c208122831" rel="nofollow">How I write go http services after seven years</a></p></pre>central_marrow: <pre><p>Thanks for sharing this article, this is great. Simple, clear and practical examples. Definitely going to be applying some of these patterns to my projects.</p></pre>Mattho: <pre><p>I use go-kit and it&#39;s fine. Doesn&#39;t provide too much, is a bit opinionated, and needs too much boilerplate I guess. But once you get going it&#39;s very easy and separates the layers very well.</p> <p>There was a short talk by the author at some conference, should be easy to find.</p></pre>beowulf_71: <pre><p>I will tell you what would be ideal. RAML 1.0 to design the APIs, a Golang generator for generating the data models and server side resource handlers. Done. </p> <p>Longer story.. in the Java world we have RAML2JAXRS which JAXRS if not aware is the Java EE standard for handling http requests/responses (more notably, rest requests/responses). Within the RAML2JAXRS generator is the ability to configure a few things.. for example there are various libraries used for json to java and java to json objects. GSON, Jackson, JAXB, etc. You can configure it to use any of those libraries. </p> <p>Thus, what we need is a good RAML 1 to Golang (server side) generator that can be configured to use one of a few options. One would be straight http with what I see is the most common example of handling routes. One should be built around Gorilla/mux. Something like that would go a LONG way in abstracting the underlying concern of what is the right way to do APIs on the back end. Then you focus on your API itself, and let the generator build the code for you. </p> <p>Some will balk at this, especially those using Swagger/OpenAPI (as I understand there is some sort of half baked solution similar to what I am talking about for Swagger). The majority of java swagger users I have seen tend to build code first, then go back and add proprietary swagger annotations to generate a json swagger api doc. Then they use that to generate the awful swagger api doc and in some rare cases, see some people use the doc for generating tests and even client sdk code. It is, in my opinion the wrong approach though. API First design, single source of truth really allows you to focus on your API as you should, and generate all artifacts from that design doc.</p> <p>Anyway.. got off topic slightly.. but would love to see someone that is good enough with Go build a RAML 1 go parser that can use templates to then generate whatever, including server side api handling code in go.</p></pre>iluminae: <pre><p>As long as we are talking about generated code, we have had great success with gRPC APIs with a rest-grpc gateway on top to provide the json http interface. All our developers need to do is write a protobuf and the business logic in a function and the rest is generated for them. It&#39;s still up to the protobuf designer to fit to a REST verb structure if they want, but most people only really mean JSON+http when they say REST anyway...</p> <p>Massive secondary plus side is having a much faster gRPC interface, which has since become our primary interface with JSON+http as a compatibility interface basically.</p></pre>fractalpal: <pre><p>Just remember about separation of concerns and create interfaces to be able to mock them easily in unit tests.</p> <ol> <li><p>You want to have handlers as structs that implements http.Handle/HandleFunc and dependency inject your repository/store interface. Repository layer can be ommited if no advanced caching/multiple data sources fetching needed (just one data source like db). It should be used if you want to first check cache for data, if no data then fetch from store, save to cache etc. Store interface should have funcs for your DB operations like CRUD. Then your proper struct implements this (like postgres- dependency inject your *sql.db driver implementation and use it to implement store interface). For routing you can use gorilla mux that someone post here before.</p></li> <li><p>For unit testing I would use gomock (it can generate your mocks implementation based on interfaces, very handy and convinient to use). Sometime with advanced scenarios or as other tool you can use monkey patching. I prefer BDD testing so ginkgo and gomega are tools that I like to use. Use go-sqlmock for *sql.db driver mock if you use one.</p></li> <li><p>For integration tests you can use docker-compose to spin up needed DB, you service and scripts to invoke your tests.</p></li> <li><p>Use wrk to test REST API performance. If needed, implement your handlers/repositories/stores using Chanel&#39;s for operations that can be done asynchronically (you can avoid blocking request, return some created ID and heavy I/O operation to be done in goroutines. This advice only if you want to scale and optimize every request.</p></li> </ol> <p>// Edit - formating text</p> <p>// Edit 2 - added info about go-sqlmock for unit tests</p></pre>h00s: <pre><p>Have a look at my URL shortener microservice: <a href="https://github.com/h00s/url-shortener-backend" rel="nofollow">https://github.com/h00s/url-shortener-backend</a></p> <p>I&#39;ve tried to keep it clean and simple. Adding routes is simple, DB queries are separated in own file as const etc. Would also love to get comments from fellow gophers on my code as this is my first Go microservice. </p></pre>SimplySerenity: <pre><p>My only comment is that Gin isn&#39;t the best, it&#39;s really lacking documentation wise. I was using it before too, but I much prefer Echo.</p> <p>Edit: I&#39;d also advocate for sqlx, it can really clean up some dumb patterns that you have to make without it.</p></pre>fharding: <pre><p>There&#39;s not just one way to accomplish something... Of course there are many ways to do REST APIs, just like how there are a million ways to write fizzbuzz or something. Don&#39;t worry too much about what others think as long as it seems neat and easy to read. Do what works for <em>you</em>. Here&#39;s a project you can look at for an idea of what one might look like (that I wrote): <a href="https://github.com/fharding1/todo" rel="nofollow">https://github.com/fharding1/todo</a></p></pre>dvirsky: <pre><p>This is my own attempt from a couple of years ago. I rather liked it but the company I wrote it for had shut down. <a href="https://github.com/Everythingme/vertex" rel="nofollow">https://github.com/Everythingme/vertex</a></p></pre>monkey-go-code: <pre><p>I just copied the same model, controller, component/business layer model I use at work in c# for my home project in golang. It works great. Its small but it can scale to lots of routes. I have a api package where I define my route functions, and a component package where I define structs for my business logic. </p> <p><a href="https://i.imgur.com/nZtiUFQ.png" rel="nofollow">Here is a picture of the general layout. This will work fine for you.</a> </p></pre>mfwbanana: <pre><p>whats wrong with your eyes haha jeez</p></pre>petepete: <pre><p>I use <a href="https://godoc.org/github.com/husobee/vestigo" rel="nofollow">Vestigo</a> and <a href="https://github.com/urfave/negroni" rel="nofollow">Negroni</a>.</p></pre>Ribice: <pre><p>Take a look at <a href="https://github.com/ribice/gorsk" rel="nofollow">Gorsk</a>.</p></pre>buffer_overfl0w: <pre><p>I started using buffalo, mainly because I wanted to create a RESTful API. I know frameworks are frowned upon in the Go community but I really wanted something where I can create migrations and manage the database that way instead because it makes deployment a bit nicer.</p> <p>I have only been using Go for a few weeks so I&#39;m probably doing it wrong. </p></pre>nerneyj: <pre><p>This is right up my alley, as I have been doing just this on my current project at work. We are using the gin framework, it’s super quick and straightforward. Every request is a context, and you can access query params or url params, you can use json marshaling with struct tags to easy bind json from requests into your go structs. We use swagger code generation to generate our models, but not any other go code. We are using dynamo db, which I don’t recommend. The biggest thing in Go is getting the hang of using interfaces and packages and understanding the order in which packages are initialized. Encapsulation is at the package level, so you can have as many files in a package as you want and all that code is in one namespace. Oh and write your tests in (package-name)_test format, that way you can import them and you test them the way they are imported elsewhere. There’s so much I could say if I went on and on. But yeah, Go is fun. </p></pre>allochi: <pre><p>I have been using gin too for about 3 years, and it proved itself to be easy yet powerful tool. Very flexible to different application structures, and easy to manage. </p> <p>In couple of projects, I use it to create proxy endpoints to other rest services like PostgREST and OSNAME geolocation, while keep authentication and session management on the main backend service.</p></pre>thetall0ne1: <pre><p><a href="https://medium.com/statuscode/how-i-write-go-http-services-after-seven-years-37c208122831?source=linkShare-b9a723f6b2a0-1528336752" rel="nofollow">https://medium.com/statuscode/how-i-write-go-http-services-after-seven-years-37c208122831?source=linkShare-b9a723f6b2a0-1528336752</a></p></pre>drvd: <pre><p>No, sorry. Different constraints lead to different solutions for this task.</p></pre>tvmaly: <pre><p>I tend to stick with something stable, has a large number of contributors, and is well documented. For my food project I used Gin with sqlx. For micro services at work I tend to stick with Gorilla router. For testing, I stick with standard library httptest</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

614 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传