I want to serve the following route for any GET request path so that I can use a frontend routing library instead (react-router).
(GET "/" [] (resource-response "index.html" {:root "public"}))
This it what I have tried:
(GET "/*" [] (resource-response "index.html" {:root "public"}))
(rfn [] (resource-response "index.html" {:root "public"}))
(GET "/:any{.*}" [] (resource-response "index.html" {:root "public"}))
(GET "/*" [] (ring.util.response/content-type
(ring.util.response/resource-response "index.html" {:root "public"}) "text/html"))
(GET "/*" [] (clojure.java.io/resource "public/index.html"))
They all give the same error:
Resource interpreted as Stylesheet but transferred with MIME type text/html: "http://localhost:5000/index.css".
bundle.js:1 Uncaught SyntaxError: Unexpected token <
And the Unexpected token is the first < in index.html
So my question is how can I adapt my Compojure/Ring/Immutant stack to play nice with react-router?
UPDATE:
I have also tried the following midleware, to change all request to the root but with no luck:
(defn wrap-dir-index [handler]
(fn [req]
(handler
(assoc req :uri "/"))))
You can use wrap-resource as described on Ring wiki page combined with a catch all route:
(defroutes handler
(GET "/" []
(-> (resource-response "index.html" {:root "public"})
(content-type "text/html))
(GET "/*" [] (redirect "/")))
(def app
(-> handler
(wrap-resource "public")
(wrap-content-type)
(wrap-not-modified))
I think it's better to redirect from all other URLs to "/" so your application works always using the same base address.
Related
Is there any way to register multiple handlers while running an http-kit server:
(defroutes rest-main-app
(GET "/" "Welcome"))
(defroutes rest-events-app
(GET "/events" "Event API"))
(defn -main []
(run-server rest-main-app {:port 5000}))
How can I pass both routes to the run-server e.g both rest-main-app and rest-events-app ?
You can use compojure's routes function. You can also pass several handlers to defroutes, an example is provided below:
(defroutes get-routes
(GET "/events" [] "Event API")
(GET "/" [] "Welcome"))
(defroutes post-routes
(POST "/events" [] "Post Event API"))
(def all-routes
(routes
get-routes
post-routes))
(defn -main []
(run-server all-routes {:port 5000}))
How do I add webjars resources to lib-noir's app-handler?
I used to do this only using Ring like this:
(def app
(-> handler
(wrap-resource "public")
(wrap-resource "/META-INF/resources")
;;resources from webjars
))
Now I'm trying to figure out how to do this with lib-noir.
I tried this:
(def app (noir-middleware/app-handler [home-routes app-routes]
:ring-defaults {:static
{:resources
"/META-INF/resources"}}))
and it works, but I get a problem when posting forms after configuring this. The params are empty in the ring request now.
This seems to do it:
(defroutes app-routes
(route/resources "/")
(route/resources "/" {:root "META-INF/resources/"})
(route/not-found "Not Found"))
I'm trying to use a custom :store option for wrap-multipart-params and instead I'm clearly getting the default store. My custom function isn't even being called.
(mp/wrap-multipart-params
(POST "/upload-x" request (upload/upload-file request))
{:store upload/logging-store})
My logging store function looks like this (it's just a dummy for now - eventually I want to handle the stream in a custom way) None of that IO happens.
(defn logging-store [{filename :filename
content-type :content-type
stream :stream
:as params}]
(println "in logging store")
(pprint filename)
(pprint params)
filename)
upload-file looks like this:
(defn upload-file [{params :params
session :session :as request}]
(let [user-id (:user-id session)
files (get params "files")]
(pprint request)
(pprint params)
(response/response
{:status :success})))
The printing for the request and the params clearly show the multipart params in there and that they are being handled by the temp file store:
:multipart-params
{"files"
{:size 1674,
:tempfile
#<File /var/folders/rx/9ntjyyvs35qbmcbp6rhfmj200000gn/T/ring-multipart-3853352501927893381.tmp>,
:content-type "application/octet-stream",
:filename "blog-test.clj"}},
EDIT: app definition (as requested)
(defroutes file-list-routes
(GET "/simple-upload" request (upload/simple-upload-file request))
(mp/wrap-multipart-params
(POST "/upload-x" request (upload/upload-file request))
{:store upload/logging-store})
)
(defroutes scratch-app
(context "/files" request file-list-routes)
(route/resources "/")
(route/not-found "Page not found"))
(def my-app
(handler/site
(ring.middleware.json/wrap-json-response
(ring.middleware.json/wrap-json-params
(ring.middleware.stacktrace/wrap-stacktrace
(ring.middleware.session/wrap-session
scratch-app
{:store (ring.middleware.session.memory/memory-store all-the-sessions)
:cookie-attrs {:max-age 3600 ;in s 3600s = 1h
}}))))))
(ring.util.servlet/defservice my-app)
The compojure.handler/site function contains the wrap-multipart-params middleware, so you're unknowingly applying the multipart middleware twice: first with the defaults, then with your custom options. Because the multipart middleware with the default options is applied first, that take priority.
The following compojure routes work.
(defroutes app-routes
(GET "/" [] (index))
(GET "/twauth" [] (tw/authorize))
(ANY "/twcallback" [] (do
(tw/callback)
(index)))
(route/resources "/")
(route/not-found "Not Found"))
(def app (handler/site app-routes))
However I get error with the following. It throws a java.nullpointer.exception. What am I doing wrong here ?
(defroutes app-routes
(GET "/" [] (index))
(GET "/twauth" [] (tw/authorize))
(ANY "/twcallback" [] (do
(tw/callback)
(index))))
(defroutes base-routes
(route/resources "/")
(route/not-found "Not Found"))
(def app
(-> app-routes
base-routes
handler/site))
Your base-routes matches all requests. I think the following illustrates it better:
(defroutes base-routes
(route/not-found "Not found"))
(def app
(-> app-routes
base-routes
handler/site))
No matter what you do in app-routes above, base-routes is checked after and will always return not-found. Note that each request is threaded through both routes, not first match wins.
So you need to either move the base-routes into your app-routes as fallbacks -- like you already did in your working example -- or compose them in app:
(def app
(-> (routes app-routes base-routes)
handler/site))
Here the composed routes ensures that the first match wins.
My Compojure web app ([compojure "1.0.1"]) always receives an empty parameter map, despite adding wrap-params etc. Code sample below:
(defroutes public-routes
(PUT "/something" {params :params}
(println (str "Params: " params))
(do-put-something params)))
(def myapp
(-> public-routes
ring-params/wrap-params))
(defn start-server []
(future (jetty/run-jetty (var myapp) {:port 8080})))
I've tried adding the wrap-params, wrap-keyword-params and wrap-multipart-params but when I PUT to the endpoint using httpie (or my client), I find that params is always empty. Can anyone help?
Thanks!
The only problem with your example code was that it lacks a ring response hash-map in the route body. The solution is evaluate to a ring response instead of using println. When you call println in your route it prints to standard out where the server process is running, which has nothing to do with the response to the API call.
(defroutes public-routes
(PUT "/something" {params :params}
{:status 200
:body (str "Params: " params)}))
This produces a 200 response with Params: {"foo" "bar"} as the response body.
I am using this to test your PUT route:
curl -X PUT -d "foo=bar" http://127.0.0.1:8080/something