11package io .github .wimdeblauwe .htmx .spring .boot .mvc ;
22
3- import com .fasterxml .jackson .core .JsonProcessingException ;
4- import com .fasterxml .jackson .databind .ObjectMapper ;
53import jakarta .servlet .http .HttpServletRequest ;
64import jakarta .servlet .http .HttpServletResponse ;
75import org .springframework .http .HttpHeaders ;
86import org .springframework .web .method .HandlerMethod ;
97import org .springframework .web .servlet .HandlerInterceptor ;
8+ import org .springframework .web .servlet .ModelAndView ;
109
1110import java .lang .reflect .Method ;
12- import java .util .Collection ;
13- import java .util .HashMap ;
14- import java .util .stream .Collectors ;
1511
1612/**
1713 * HandlerInterceptor that adds htmx specific headers to the response.
1814 */
1915public class HtmxHandlerInterceptor implements HandlerInterceptor {
2016
21- private final ObjectMapper objectMapper ;
22- private final HtmxHandlerMethodAnnotationHandler handlerMethodAnnotationHandler ;
17+ private final HtmxHandlerMethodHandler htmxHandlerMethodHandler ;
2318
24- public HtmxHandlerInterceptor (ObjectMapper objectMapper , HtmxHandlerMethodAnnotationHandler handlerMethodAnnotationHandler ) {
25- this .objectMapper = objectMapper ;
26- this .handlerMethodAnnotationHandler = handlerMethodAnnotationHandler ;
19+ public HtmxHandlerInterceptor (HtmxHandlerMethodHandler htmxHandlerMethodHandler ) {
20+ this .htmxHandlerMethodHandler = htmxHandlerMethodHandler ;
2721 }
2822
2923 @ Override
30- public void afterCompletion (HttpServletRequest request , HttpServletResponse response , Object handler , Exception ex ) throws Exception {
31-
32- HtmxResponse htmxResponse = RequestContextUtils .getHtmxResponse (request );
33- if (htmxResponse != null ) {
34- addHxTriggerHeaders (response , HtmxResponseHeader .HX_TRIGGER , htmxResponse .getTriggersInternal ());
35- addHxTriggerHeaders (response , HtmxResponseHeader .HX_TRIGGER_AFTER_SETTLE , htmxResponse .getTriggersAfterSettleInternal ());
36- addHxTriggerHeaders (response , HtmxResponseHeader .HX_TRIGGER_AFTER_SWAP , htmxResponse .getTriggersAfterSwapInternal ());
37-
38- if (htmxResponse .getLocation () != null ) {
39- HtmxLocation location = htmxResponse .getLocation ();
40- if (location .hasContextData ()) {
41- location .setPath (RequestContextUtils .createUrl (request , location .getPath (), htmxResponse .isContextRelative ()));
42- setHeaderJsonValue (response , HtmxResponseHeader .HX_LOCATION , location );
43- } else {
44- response .setHeader (HtmxResponseHeader .HX_LOCATION .getValue (), RequestContextUtils .createUrl (request , location .getPath (), htmxResponse .isContextRelative ()));
45- }
46- }
47- if (htmxResponse .getReplaceUrl () != null ) {
48- response .setHeader (HtmxResponseHeader .HX_REPLACE_URL .getValue (), RequestContextUtils .createUrl (request , htmxResponse .getReplaceUrl (), htmxResponse .isContextRelative ()));
49- }
50- if (htmxResponse .getPushUrl () != null ) {
51- response .setHeader (HtmxResponseHeader .HX_PUSH_URL .getValue (), RequestContextUtils .createUrl (request , htmxResponse .getPushUrl (), htmxResponse .isContextRelative ()));
52- }
53- if (htmxResponse .getRedirect () != null ) {
54- response .setHeader (HtmxResponseHeader .HX_REDIRECT .getValue (), RequestContextUtils .createUrl (request , htmxResponse .getRedirect (), htmxResponse .isContextRelative ()));
55- }
56- if (htmxResponse .isRefresh ()) {
57- response .setHeader (HtmxResponseHeader .HX_REFRESH .getValue (), "true" );
58- }
59- if (htmxResponse .getRetarget () != null ) {
60- response .setHeader (HtmxResponseHeader .HX_RETARGET .getValue (), htmxResponse .getRetarget ());
61- }
62- if (htmxResponse .getReselect () != null ) {
63- response .setHeader (HtmxResponseHeader .HX_RESELECT .getValue (), htmxResponse .getReselect ());
64- }
65- if (htmxResponse .getReswap () != null ) {
66- response .setHeader (HtmxResponseHeader .HX_RESWAP .getValue (), htmxResponse .getReswap ().toHeaderValue ());
67- }
68- }
24+ public void postHandle (HttpServletRequest request , HttpServletResponse response , Object handler , ModelAndView modelAndView ) throws Exception {
25+ htmxHandlerMethodHandler .handleMethodArgument (request , response );
6926 }
7027
7128 @ Override
7229 public boolean preHandle (HttpServletRequest request ,
7330 HttpServletResponse response ,
7431 Object handler ) {
7532
76- if ( handler instanceof HandlerMethod ) {
77- Method method = (( HandlerMethod ) handler ). getMethod ();
78- setVary ( request , response );
79- handlerMethodAnnotationHandler . handleMethod ( method , request , response );
33+ setVary ( request , response );
34+
35+ if ( handler instanceof HandlerMethod handlerMethod ) {
36+ htmxHandlerMethodHandler . handleMethodAnnotations ( handlerMethod . getMethod () , request , response );
8037 }
8138
8239 return true ;
@@ -88,35 +45,4 @@ private void setVary(HttpServletRequest request, HttpServletResponse response) {
8845 }
8946 }
9047
91- private void setHeaderJsonValue (HttpServletResponse response , HtmxResponseHeader header , Object value ) {
92- try {
93- response .setHeader (header .getValue (), objectMapper .writeValueAsString (value ));
94- } catch (JsonProcessingException e ) {
95- throw new IllegalArgumentException ("Unable to set header " + header .getValue () + " to " + value , e );
96- }
97- }
98-
99- private void addHxTriggerHeaders (HttpServletResponse response , HtmxResponseHeader headerName , Collection <HtmxTrigger > triggers ) {
100- if (triggers .isEmpty ()) {
101- return ;
102- }
103-
104- // separate event names by commas if no additional details are available
105- if (triggers .stream ().allMatch (t -> t .getEventDetail () == null )) {
106- String value = triggers .stream ()
107- .map (HtmxTrigger ::getEventName )
108- .collect (Collectors .joining ("," ));
109-
110- response .setHeader (headerName .getValue (), value );
111- return ;
112- }
113-
114- // multiple events with or without details
115- var triggerMap = new HashMap <String , Object >();
116- for (HtmxTrigger trigger : triggers ) {
117- triggerMap .put (trigger .getEventName (), trigger .getEventDetail ());
118- }
119- setHeaderJsonValue (response , headerName , triggerMap );
120- }
121-
12248}
0 commit comments