Add BindXML AND ShouldBindXML #1484
This commit is contained in:
		
							
								
								
									
										61
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								README.md
									
									
									
									
									
								
							| @ -534,10 +534,10 @@ Note that you need to set the corresponding binding tag on all fields you want t | |||||||
|  |  | ||||||
| Also, Gin provides two sets of methods for binding: | Also, Gin provides two sets of methods for binding: | ||||||
| - **Type** - Must bind | - **Type** - Must bind | ||||||
|   - **Methods** - `Bind`, `BindJSON`, `BindQuery` |   - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery` | ||||||
|   - **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method. |   - **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method. | ||||||
| - **Type** - Should bind | - **Type** - Should bind | ||||||
|   - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindQuery` |   - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery` | ||||||
|   - **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately. |   - **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately. | ||||||
|  |  | ||||||
| When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`. | When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`. | ||||||
| @ -547,8 +547,8 @@ You can also specify that specific fields are required. If a field is decorated | |||||||
| ```go | ```go | ||||||
| // Binding from JSON | // Binding from JSON | ||||||
| type Login struct { | type Login struct { | ||||||
| 	User     string `form:"user" json:"user" binding:"required"` | 	User     string `form:"user" json:"user" xml:"user"  binding:"required"` | ||||||
| 	Password string `form:"password" json:"password" binding:"required"` | 	Password string `form:"password" json:"password" xml:"password" binding:"required"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| @ -557,30 +557,55 @@ func main() { | |||||||
| 	// Example for binding JSON ({"user": "manu", "password": "123"}) | 	// Example for binding JSON ({"user": "manu", "password": "123"}) | ||||||
| 	router.POST("/loginJSON", func(c *gin.Context) { | 	router.POST("/loginJSON", func(c *gin.Context) { | ||||||
| 		var json Login | 		var json Login | ||||||
| 		if err := c.ShouldBindJSON(&json); err == nil { | 		if err := c.ShouldBindXML(&json); err != nil { | ||||||
| 			if json.User == "manu" && json.Password == "123" { |  | ||||||
| 				c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) |  | ||||||
| 			} else { |  | ||||||
| 				c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | 			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||||
|  | 			return | ||||||
| 		} | 		} | ||||||
|  | 		 | ||||||
|  | 		if json.User != "manu" || json.Password != "123" { | ||||||
|  | 			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) | ||||||
|  | 			return | ||||||
|  | 		}  | ||||||
|  | 		 | ||||||
|  | 		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// Example for binding XML ( | ||||||
|  | 	//	<?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | 	//	<root> | ||||||
|  | 	//		<user>user</user> | ||||||
|  | 	//		<password>123</user> | ||||||
|  | 	//	</root>) | ||||||
|  | 	router.POST("/loginXML", func(c *gin.Context) { | ||||||
|  | 		var xml Login | ||||||
|  | 		if err := c.ShouldBindXML(&xml); err != nil { | ||||||
|  | 			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if xml.User != "manu" || xml.Password != "123" { | ||||||
|  | 			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) | ||||||
|  | 			return | ||||||
|  | 		}  | ||||||
|  | 		 | ||||||
|  | 		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	// Example for binding a HTML form (user=manu&password=123) | 	// Example for binding a HTML form (user=manu&password=123) | ||||||
| 	router.POST("/loginForm", func(c *gin.Context) { | 	router.POST("/loginForm", func(c *gin.Context) { | ||||||
| 		var form Login | 		var form Login | ||||||
| 		// This will infer what binder to use depending on the content-type header. | 		// This will infer what binder to use depending on the content-type header. | ||||||
| 		if err := c.ShouldBind(&form); err == nil { | 		if err := c.ShouldBind(&form); err != nil { | ||||||
| 			if form.User == "manu" && form.Password == "123" { |  | ||||||
| 				c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) |  | ||||||
| 			} else { |  | ||||||
| 				c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | 			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||||
|  | 			return | ||||||
| 		} | 		} | ||||||
|  | 		 | ||||||
|  | 		if form.User != "manu" || form.Password != "123" { | ||||||
|  | 			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) | ||||||
|  | 			return | ||||||
|  | 		}  | ||||||
|  | 		 | ||||||
|  | 		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	// Listen and serve on 0.0.0.0:8080 | 	// Listen and serve on 0.0.0.0:8080 | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								context.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								context.go
									
									
									
									
									
								
							| @ -511,6 +511,11 @@ func (c *Context) BindJSON(obj interface{}) error { | |||||||
| 	return c.MustBindWith(obj, binding.JSON) | 	return c.MustBindWith(obj, binding.JSON) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML). | ||||||
|  | func (c *Context) BindXML(obj interface{}) error { | ||||||
|  | 	return c.MustBindWith(obj, binding.XML) | ||||||
|  | } | ||||||
|  |  | ||||||
| // BindQuery is a shortcut for c.MustBindWith(obj, binding.Query). | // BindQuery is a shortcut for c.MustBindWith(obj, binding.Query). | ||||||
| func (c *Context) BindQuery(obj interface{}) error { | func (c *Context) BindQuery(obj interface{}) error { | ||||||
| 	return c.MustBindWith(obj, binding.Query) | 	return c.MustBindWith(obj, binding.Query) | ||||||
| @ -545,6 +550,11 @@ func (c *Context) ShouldBindJSON(obj interface{}) error { | |||||||
| 	return c.ShouldBindWith(obj, binding.JSON) | 	return c.ShouldBindWith(obj, binding.JSON) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML). | ||||||
|  | func (c *Context) ShouldBindXML(obj interface{}) error { | ||||||
|  | 	return c.ShouldBindWith(obj, binding.XML) | ||||||
|  | } | ||||||
|  |  | ||||||
| // ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query). | // ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query). | ||||||
| func (c *Context) ShouldBindQuery(obj interface{}) error { | func (c *Context) ShouldBindQuery(obj interface{}) error { | ||||||
| 	return c.ShouldBindWith(obj, binding.Query) | 	return c.ShouldBindWith(obj, binding.Query) | ||||||
|  | |||||||
| @ -1302,6 +1302,26 @@ func TestContextBindWithJSON(t *testing.T) { | |||||||
| 	assert.Equal(t, "bar", obj.Foo) | 	assert.Equal(t, "bar", obj.Foo) | ||||||
| 	assert.Equal(t, 0, w.Body.Len()) | 	assert.Equal(t, 0, w.Body.Len()) | ||||||
| } | } | ||||||
|  | func TestContextBindWithXML(t *testing.T) { | ||||||
|  | 	w := httptest.NewRecorder() | ||||||
|  | 	c, _ := CreateTestContext(w) | ||||||
|  |  | ||||||
|  | 	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | 		<root> | ||||||
|  | 			<foo>FOO</foo> | ||||||
|  | 		   	<bar>BAR</bar> | ||||||
|  | 		</root>`)) | ||||||
|  | 	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type | ||||||
|  |  | ||||||
|  | 	var obj struct { | ||||||
|  | 		Foo string `xml:"foo"` | ||||||
|  | 		Bar string `xml:"bar"` | ||||||
|  | 	} | ||||||
|  | 	assert.NoError(t, c.BindXML(&obj)) | ||||||
|  | 	assert.Equal(t, "FOO", obj.Foo) | ||||||
|  | 	assert.Equal(t, "BAR", obj.Bar) | ||||||
|  | 	assert.Equal(t, 0, w.Body.Len()) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestContextBindWithQuery(t *testing.T) { | func TestContextBindWithQuery(t *testing.T) { | ||||||
| 	w := httptest.NewRecorder() | 	w := httptest.NewRecorder() | ||||||
| @ -1372,6 +1392,27 @@ func TestContextShouldBindWithJSON(t *testing.T) { | |||||||
| 	assert.Equal(t, 0, w.Body.Len()) | 	assert.Equal(t, 0, w.Body.Len()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestContextShouldBindWithXML(t *testing.T) { | ||||||
|  | 	w := httptest.NewRecorder() | ||||||
|  | 	c, _ := CreateTestContext(w) | ||||||
|  |  | ||||||
|  | 	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | 		<root> | ||||||
|  | 			<foo>FOO</foo> | ||||||
|  | 		   	<bar>BAR</bar> | ||||||
|  | 		</root>`)) | ||||||
|  | 	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type | ||||||
|  |  | ||||||
|  | 	var obj struct { | ||||||
|  | 		Foo string `xml:"foo"` | ||||||
|  | 		Bar string `xml:"bar"` | ||||||
|  | 	} | ||||||
|  | 	assert.NoError(t, c.ShouldBindXML(&obj)) | ||||||
|  | 	assert.Equal(t, "FOO", obj.Foo) | ||||||
|  | 	assert.Equal(t, "BAR", obj.Bar) | ||||||
|  | 	assert.Equal(t, 0, w.Body.Len()) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestContextShouldBindWithQuery(t *testing.T) { | func TestContextShouldBindWithQuery(t *testing.T) { | ||||||
| 	w := httptest.NewRecorder() | 	w := httptest.NewRecorder() | ||||||
| 	c, _ := CreateTestContext(w) | 	c, _ := CreateTestContext(w) | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user